001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.daemon; 019 020import java.security.Permission; 021import java.util.StringTokenizer; 022 023/** 024 * Represents the permissions to control and query the status of 025 * a {@code Daemon}. A {@code DaemonPermission} consists of a 026 * target name and a list of actions associated with it. 027 * <p> 028 * In this specification version the only available target name for this 029 * permission is "control", but further releases may add more target 030 * names to fine-tune the access that needs to be granted to the caller. 031 * </p> 032 * <p> 033 * Actions are defined by a string of comma-separated values, as shown in the 034 * table below. The empty string implies no permission at all, while the 035 * special "*" value implies all permissions for the given 036 * name: 037 * </p> 038 * <table border="1"> 039 * <caption>Supported Actions</caption> 040 * <tr> 041 * <th>Target"Name</th> 042 * <th>Action</th> 043 * <th>Description</th> 044 * </tr> 045 * <tr> 046 * <td rowspan="5">"control"</td> 047 * <td>"start"</td> 048 * <td> 049 * The permission to call the {@code start()} method in an instance 050 * of a {@code DaemonController} interface. 051 * </td> 052 * </tr> 053 * <tr> 054 * <td>"stop"</td> 055 * <td> 056 * The permission to call the {@code stop()} method in an instance 057 * of a {@code DaemonController} interface. 058 * </td> 059 * </tr> 060 * <tr> 061 * <td>"shutdown"</td> 062 * <td> 063 * The permission to call the {@code shutdown()} method in an instance 064 * of a {@code DaemonController} interface. 065 * </td> 066 * </tr> 067 * <tr> 068 * <td>"reload"</td> 069 * <td> 070 * The permission to call the {@code reload()} method in an instance 071 * of a {@code DaemonController} interface. 072 * </td> 073 * </tr> 074 * <tr> 075 * <td>"*"</td> 076 * <td> 077 * The special wildcard action implies all above-mentioned action. This is 078 * equal to construct a permission with the "start, stop, shutdown, 079 * reload" list of actions. 080 * </td> 081 * </tr> 082 * </table> 083 */ 084public final class DaemonPermission extends Permission 085{ 086 087 /* ==================================================================== 088 * Constants. 089 */ 090 091 private static final long serialVersionUID = -8682149075879731987L; 092 093 /** 094 * The target name when associated with control actions 095 * ("control"). 096 */ 097 protected static final String CONTROL = "control"; 098 099 /** 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 <strong>will not</strong> 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 = 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 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 setupDescription(); 256 return this.desc.hashCode(); 257 } 258 259 /** 260 * Checks if a specified object equals {@code DaemonPermission}. 261 * 262 * @return <strong>true</strong> or <strong>false</strong> 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 <strong>true</strong> or <strong>false</strong> 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 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(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_CONTROL_START; 377 } 378 else if (CONTROL_STOP.equalsIgnoreCase(val)) { 379 mask |= MASK_CONTROL_STOP; 380 } 381 else if (CONTROL_SHUTDOWN.equalsIgnoreCase(val)) { 382 mask |= MASK_CONTROL_SHUTDOWN; 383 } 384 else if (CONTROL_RELOAD.equalsIgnoreCase(val)) { 385 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