1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.daemon; 19 20 import java.security.Permission; 21 import java.util.StringTokenizer; 22 23 /** 24 * Represents the permissions to control and query the status of 25 * a {@code Daemon}. A {@code DaemonPermission} consists of a 26 * target name and a list of actions associated with it. 27 * <p> 28 * In this specification version the only available target name for this 29 * permission is "control", but further releases may add more target 30 * names to fine-tune the access that needs to be granted to the caller. 31 * </p> 32 * <p> 33 * Actions are defined by a string of comma-separated values, as shown in the 34 * table below. The empty string implies no permission at all, while the 35 * special "*" value implies all permissions for the given 36 * name: 37 * </p> 38 * <table border="1"> 39 * <caption>Supported Actions</caption> 40 * <tr> 41 * <th>Target"Name</th> 42 * <th>Action</th> 43 * <th>Description</th> 44 * </tr> 45 * <tr> 46 * <td rowspan="5">"control"</td> 47 * <td>"start"</td> 48 * <td> 49 * The permission to call the {@code start()} method in an instance 50 * of a {@code DaemonController} interface. 51 * </td> 52 * </tr> 53 * <tr> 54 * <td>"stop"</td> 55 * <td> 56 * The permission to call the {@code stop()} method in an instance 57 * of a {@code DaemonController} interface. 58 * </td> 59 * </tr> 60 * <tr> 61 * <td>"shutdown"</td> 62 * <td> 63 * The permission to call the {@code shutdown()} method in an instance 64 * of a {@code DaemonController} interface. 65 * </td> 66 * </tr> 67 * <tr> 68 * <td>"reload"</td> 69 * <td> 70 * The permission to call the {@code reload()} method in an instance 71 * of a {@code DaemonController} interface. 72 * </td> 73 * </tr> 74 * <tr> 75 * <td>"*"</td> 76 * <td> 77 * The special wildcard action implies all above-mentioned action. This is 78 * equal to construct a permission with the "start, stop, shutdown, 79 * reload" list of actions. 80 * </td> 81 * </tr> 82 * </table> 83 */ 84 public final class DaemonPermission extends Permission 85 { 86 87 /* ==================================================================== 88 * Constants. 89 */ 90 91 private static final long serialVersionUID = -8682149075879731987L; 92 93 /** 94 * The target name when associated with control actions 95 * ("control"). 96 */ 97 protected static final String CONTROL = "control"; 98 99 /** 100 * The target type when associated with control actions. 101 */ 102 protected static final int TYPE_CONTROL = 1; 103 104 /** 105 * The action name associated with the permission to call the 106 * {@code DaemonController.start()} method. 107 */ 108 protected static final String CONTROL_START = "start"; 109 110 /** 111 * The action name associated with the permission to call the 112 * {@code DaemonController.stop()} method. 113 */ 114 protected static final String CONTROL_STOP = "stop"; 115 116 /** 117 * The action name associated with the permission to call the 118 * {@code DaemonController.shutdown()} method. 119 */ 120 protected static final String CONTROL_SHUTDOWN = "shutdown"; 121 122 /** 123 * The action name associated with the permission to call the 124 * {@code DaemonController.reload()} method. 125 */ 126 protected static final String CONTROL_RELOAD = "reload"; 127 128 /** 129 * The action mask associated with the permission to call the 130 * {@code DaemonController.start()} method. 131 */ 132 protected static final int MASK_CONTROL_START = 0x01; 133 134 /** 135 * The action mask associated with the permission to call the 136 * {@code DaemonController.stop()} method. 137 */ 138 protected static final int MASK_CONTROL_STOP = 0x02; 139 140 /** 141 * The action mask associated with the permission to call the 142 * {@code DaemonController.shutdown()} method. 143 */ 144 protected static final int MASK_CONTROL_SHUTDOWN = 0x04; 145 146 /** 147 * The action mask associated with the permission to call the 148 * {@code DaemonController.reload()} method. 149 */ 150 protected static final int MASK_CONTROL_RELOAD = 0x08; 151 152 /** 153 * The "wildcard" action implying all actions for the given 154 * target name. 155 */ 156 protected static final String WILDCARD = "*"; 157 158 /* ==================================================================== 159 * Instance variables 160 */ 161 162 /** The type of this permission object. */ 163 private transient int type; 164 /** The permission mask associated with this permission object. */ 165 private transient int mask; 166 /** The String representation of this permission object. */ 167 private transient String desc; 168 169 /* ==================================================================== 170 * Constructors 171 */ 172 173 /** 174 * Creates a new {@code DaemonPermission} instance with a specified 175 * permission name. 176 * <p> 177 * This constructor will create a new {@code DaemonPermission} 178 * instance that <b>will not</b> grant any permission to the caller. 179 * 180 * @param target The target name of this permission. 181 * @throws IllegalArgumentException If the specified target name is not 182 * supported. 183 */ 184 public DaemonPermission(final String target) 185 throws IllegalArgumentException 186 { 187 // Set up the target name of this permission object. 188 super(target); 189 190 // Check if the permission target name was specified 191 if (target == null) { 192 throw new IllegalArgumentException("Null permission name"); 193 } 194 195 // Check if this is a "control" permission and set up accordingly. 196 if (CONTROL.equalsIgnoreCase(target)) { 197 type = TYPE_CONTROL; 198 return; 199 } 200 201 // If we got here, we have an invalid permission name. 202 throw new IllegalArgumentException("Invalid permission name \"" + 203 target + "\" specified"); 204 } 205 206 /** 207 * Creates a new {@code DaemonPermission} instance with a specified 208 * permission name and a specified list of actions. 209 * 210 * @param target The target name of this permission. 211 * @param actions The list of actions permitted by this permission. 212 * @throws IllegalArgumentException If the specified target name is not 213 * supported, or the specified list of actions includes an 214 * invalid value. 215 */ 216 public DaemonPermission(final String target, final String actions) 217 throws IllegalArgumentException 218 { 219 // Setup this instance's target name. 220 this(target); 221 222 // Create the appropriate mask if this is a control permission. 223 if (this.type == TYPE_CONTROL) { 224 this.mask = this.createControlMask(actions); 225 } 226 } 227 228 /* ==================================================================== 229 * Public methods 230 */ 231 232 /** 233 * Returns the list of actions permitted by this instance of 234 * {@code DaemonPermission} in its canonical form. 235 * 236 * @return The canonicalized list of actions. 237 */ 238 @Override 239 public String getActions() 240 { 241 if (this.type == TYPE_CONTROL) { 242 return this.createControlActions(this.mask); 243 } 244 return ""; 245 } 246 247 /** 248 * Returns the hash code for this {@code DaemonPermission} instance. 249 * 250 * @return An hash code value. 251 */ 252 @Override 253 public int hashCode() 254 { 255 this.setupDescription(); 256 return this.desc.hashCode(); 257 } 258 259 /** 260 * Checks if a specified object equals {@code DaemonPermission}. 261 * 262 * @return <b>true</b> or <b>false</b> whether the specified object equals 263 * this {@code DaemonPermission} instance or not. 264 */ 265 @Override 266 public boolean equals(final Object object) 267 { 268 if (object == this) { 269 return true; 270 } 271 272 if (!(object instanceof DaemonPermission)) { 273 return false; 274 } 275 276 final DaemonPermission that = (DaemonPermission) object; 277 278 if (this.type != that.type) { 279 return false; 280 } 281 return this.mask == that.mask; 282 } 283 284 /** 285 * Checks if this {@code DaemonPermission} implies another 286 * {@code Permission}. 287 * 288 * @return <b>true</b> or <b>false</b> whether the specified permission 289 * is implied by this {@code DaemonPermission} instance or 290 * not. 291 */ 292 @Override 293 public boolean implies(final Permission permission) 294 { 295 if (permission == this) { 296 return true; 297 } 298 299 if (!(permission instanceof DaemonPermission)) { 300 return false; 301 } 302 303 final DaemonPermission that = (DaemonPermission) permission; 304 305 if (this.type != that.type) { 306 return false; 307 } 308 return (this.mask & that.mask) == that.mask; 309 } 310 311 /** 312 * Returns a {@code String} representation of this instance. 313 * 314 * @return A {@code String} representing this 315 * {@code DaemonPermission} instance. 316 */ 317 @Override 318 public String toString() 319 { 320 this.setupDescription(); 321 return this.desc; 322 } 323 324 /* ==================================================================== 325 * Private methods 326 */ 327 328 /** 329 * Creates a String description for this permission instance. 330 */ 331 private void setupDescription() 332 { 333 if (this.desc != null) { 334 return; 335 } 336 337 final StringBuilder buf = new StringBuilder(); 338 buf.append(this.getClass().getName()); 339 buf.append('['); 340 switch (this.type) { 341 case TYPE_CONTROL: 342 buf.append(CONTROL); 343 break; 344 default: 345 buf.append("UNKNOWN"); 346 break; 347 } 348 buf.append(':'); 349 buf.append(this.getActions()); 350 buf.append(']'); 351 352 this.desc = buf.toString(); 353 } 354 355 /** 356 * Creates a permission mask for a given control actions string. 357 */ 358 private int createControlMask(final String actions) 359 throws IllegalArgumentException 360 { 361 if (actions == null) { 362 return 0; 363 } 364 365 int mask = 0; 366 final StringTokenizer tok = new StringTokenizer(actions, ",", false); 367 368 while (tok.hasMoreTokens()) { 369 final String val = tok.nextToken().trim(); 370 371 if (WILDCARD.equals(val)) { 372 return MASK_CONTROL_START | MASK_CONTROL_STOP | 373 MASK_CONTROL_SHUTDOWN | MASK_CONTROL_RELOAD; 374 } 375 if (CONTROL_START.equalsIgnoreCase(val)) { 376 mask = mask | MASK_CONTROL_START; 377 } 378 else if (CONTROL_STOP.equalsIgnoreCase(val)) { 379 mask = mask | MASK_CONTROL_STOP; 380 } 381 else if (CONTROL_SHUTDOWN.equalsIgnoreCase(val)) { 382 mask = mask | MASK_CONTROL_SHUTDOWN; 383 } 384 else if (CONTROL_RELOAD.equalsIgnoreCase(val)) { 385 mask = mask | MASK_CONTROL_RELOAD; 386 } 387 else { 388 throw new IllegalArgumentException("Invalid action name \"" + 389 val + "\" specified"); 390 } 391 } 392 return mask; 393 } 394 395 /** Creates an actions list for a given control permission mask. */ 396 private String createControlActions(final int mask) 397 { 398 final StringBuilder buf = new StringBuilder(); 399 boolean sep = false; 400 401 if ((mask & MASK_CONTROL_START) == MASK_CONTROL_START) { 402 sep = true; 403 buf.append(CONTROL_START); 404 } 405 406 if ((mask & MASK_CONTROL_STOP) == MASK_CONTROL_STOP) { 407 if (sep) { 408 buf.append(","); 409 } 410 else { 411 sep = true; 412 } 413 buf.append(CONTROL_STOP); 414 } 415 416 if ((mask & MASK_CONTROL_SHUTDOWN) == MASK_CONTROL_SHUTDOWN) { 417 if (sep) { 418 buf.append(","); 419 } 420 else { 421 sep = true; 422 } 423 buf.append(CONTROL_SHUTDOWN); 424 } 425 426 if ((mask & MASK_CONTROL_RELOAD) == MASK_CONTROL_RELOAD) { 427 if (sep) { 428 buf.append(","); 429 } 430 else { 431 sep = true; 432 } 433 buf.append(CONTROL_RELOAD); 434 } 435 436 return buf.toString(); 437 } 438 } 439