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 * https://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 private static final long serialVersionUID = -8682149075879731987L; 088 089 /** 090 * The target name when associated with control actions 091 * ("control"). 092 */ 093 protected static final String CONTROL = "control"; 094 095 /** 096 * The target type when associated with control actions. 097 */ 098 protected static final int TYPE_CONTROL = 1; 099 100 /** 101 * The action name associated with the permission to call the 102 * {@code DaemonController.start()} method. 103 */ 104 protected static final String CONTROL_START = "start"; 105 106 /** 107 * The action name associated with the permission to call the 108 * {@code DaemonController.stop()} method. 109 */ 110 protected static final String CONTROL_STOP = "stop"; 111 112 /** 113 * The action name associated with the permission to call the 114 * {@code DaemonController.shutdown()} method. 115 */ 116 protected static final String CONTROL_SHUTDOWN = "shutdown"; 117 118 /** 119 * The action name associated with the permission to call the 120 * {@code DaemonController.reload()} method. 121 */ 122 protected static final String CONTROL_RELOAD = "reload"; 123 124 /** 125 * The action mask associated with the permission to call the 126 * {@code DaemonController.start()} method. 127 */ 128 protected static final int MASK_CONTROL_START = 0x01; 129 130 /** 131 * The action mask associated with the permission to call the 132 * {@code DaemonController.stop()} method. 133 */ 134 protected static final int MASK_CONTROL_STOP = 0x02; 135 136 /** 137 * The action mask associated with the permission to call the 138 * {@code DaemonController.shutdown()} method. 139 */ 140 protected static final int MASK_CONTROL_SHUTDOWN = 0x04; 141 142 /** 143 * The action mask associated with the permission to call the 144 * {@code DaemonController.reload()} method. 145 */ 146 protected static final int MASK_CONTROL_RELOAD = 0x08; 147 148 /** 149 * The "wildcard" action implying all actions for the given 150 * target name. 151 */ 152 protected static final String WILDCARD = "*"; 153 154 /** The type of this permission object. */ 155 private transient int type; 156 157 /** The permission mask associated with this permission object. */ 158 private transient int mask; 159 160 /** The String representation of this permission object. */ 161 private transient String desc; 162 163 /** 164 * Creates a new {@code DaemonPermission} instance with a specified 165 * permission name. 166 * <p> 167 * This constructor will create a new {@code DaemonPermission} 168 * instance that <strong>will not</strong> grant any permission to the caller. 169 * 170 * @param target The target name of this permission. 171 * @throws IllegalArgumentException If the specified target name is not 172 * supported. 173 */ 174 public DaemonPermission(final String target) 175 throws IllegalArgumentException 176 { 177 // Set up the target name of this permission object. 178 super(target); 179 180 // Check if the permission target name was specified 181 if (target == null) { 182 throw new IllegalArgumentException("Null permission name"); 183 } 184 185 // Check if this is a "control" permission and set up accordingly. 186 if (CONTROL.equalsIgnoreCase(target)) { 187 type = TYPE_CONTROL; 188 return; 189 } 190 191 // If we got here, we have an invalid permission name. 192 throw new IllegalArgumentException("Invalid permission name \"" + 193 target + "\" specified"); 194 } 195 196 /** 197 * Creates a new {@code DaemonPermission} instance with a specified 198 * permission name and a specified list of actions. 199 * 200 * @param target The target name of this permission. 201 * @param actions The list of actions permitted by this permission. 202 * @throws IllegalArgumentException If the specified target name is not 203 * supported, or the specified list of actions includes an 204 * invalid value. 205 */ 206 public DaemonPermission(final String target, final String actions) 207 throws IllegalArgumentException 208 { 209 // Setup this instance's target name. 210 this(target); 211 212 // Create the appropriate mask if this is a control permission. 213 if (this.type == TYPE_CONTROL) { 214 this.mask = createControlMask(actions); 215 } 216 } 217 218 /** 219 * Returns the list of actions permitted by this instance of 220 * {@code DaemonPermission} in its canonical form. 221 * 222 * @return The canonicalized list of actions. 223 */ 224 @Override 225 public String getActions() 226 { 227 if (this.type == TYPE_CONTROL) { 228 return createControlActions(this.mask); 229 } 230 return ""; 231 } 232 233 /** 234 * Returns the hash code for this {@code DaemonPermission} instance. 235 * 236 * @return An hash code value. 237 */ 238 @Override 239 public int hashCode() 240 { 241 setupDescription(); 242 return this.desc.hashCode(); 243 } 244 245 /** 246 * Checks if a specified object equals {@code DaemonPermission}. 247 * 248 * @return <strong>true</strong> or <strong>false</strong> whether the specified object equals 249 * this {@code DaemonPermission} instance or not. 250 */ 251 @Override 252 public boolean equals(final Object object) 253 { 254 if (object == this) { 255 return true; 256 } 257 258 if (!(object instanceof DaemonPermission)) { 259 return false; 260 } 261 262 final DaemonPermission that = (DaemonPermission) object; 263 264 if (this.type != that.type) { 265 return false; 266 } 267 return this.mask == that.mask; 268 } 269 270 /** 271 * Checks if this {@code DaemonPermission} implies another 272 * {@code Permission}. 273 * 274 * @return <strong>true</strong> or <strong>false</strong> whether the specified permission 275 * is implied by this {@code DaemonPermission} instance or 276 * not. 277 */ 278 @Override 279 public boolean implies(final Permission permission) 280 { 281 if (permission == this) { 282 return true; 283 } 284 285 if (!(permission instanceof DaemonPermission)) { 286 return false; 287 } 288 289 final DaemonPermission that = (DaemonPermission) permission; 290 291 if (this.type != that.type) { 292 return false; 293 } 294 return (this.mask & that.mask) == that.mask; 295 } 296 297 /** 298 * Returns a {@code String} representation of this instance. 299 * 300 * @return A {@code String} representing this 301 * {@code DaemonPermission} instance. 302 */ 303 @Override 304 public String toString() 305 { 306 setupDescription(); 307 return this.desc; 308 } 309 310 /** 311 * Creates a String description for this permission instance. 312 */ 313 private void setupDescription() 314 { 315 if (this.desc != null) { 316 return; 317 } 318 319 final StringBuilder buf = new StringBuilder(); 320 buf.append(this.getClass().getName()); 321 buf.append('['); 322 switch (this.type) { 323 case TYPE_CONTROL: 324 buf.append(CONTROL); 325 break; 326 default: 327 buf.append("UNKNOWN"); 328 break; 329 } 330 buf.append(':'); 331 buf.append(getActions()); 332 buf.append(']'); 333 334 this.desc = buf.toString(); 335 } 336 337 /** 338 * Creates a permission mask for a given control actions string. 339 */ 340 private int createControlMask(final String actions) 341 throws IllegalArgumentException 342 { 343 if (actions == null) { 344 return 0; 345 } 346 347 int mask = 0; 348 final StringTokenizer tok = new StringTokenizer(actions, ",", false); 349 350 while (tok.hasMoreTokens()) { 351 final String val = tok.nextToken().trim(); 352 353 if (WILDCARD.equals(val)) { 354 return MASK_CONTROL_START | MASK_CONTROL_STOP | 355 MASK_CONTROL_SHUTDOWN | MASK_CONTROL_RELOAD; 356 } 357 if (CONTROL_START.equalsIgnoreCase(val)) { 358 mask |= MASK_CONTROL_START; 359 } 360 else if (CONTROL_STOP.equalsIgnoreCase(val)) { 361 mask |= MASK_CONTROL_STOP; 362 } 363 else if (CONTROL_SHUTDOWN.equalsIgnoreCase(val)) { 364 mask |= MASK_CONTROL_SHUTDOWN; 365 } 366 else if (CONTROL_RELOAD.equalsIgnoreCase(val)) { 367 mask |= MASK_CONTROL_RELOAD; 368 } 369 else { 370 throw new IllegalArgumentException("Invalid action name \"" + 371 val + "\" specified"); 372 } 373 } 374 return mask; 375 } 376 377 /** Creates an actions list for a given control permission mask. */ 378 private String createControlActions(final int mask) 379 { 380 final StringBuilder buf = new StringBuilder(); 381 boolean sep = false; 382 383 if ((mask & MASK_CONTROL_START) == MASK_CONTROL_START) { 384 sep = true; 385 buf.append(CONTROL_START); 386 } 387 388 if ((mask & MASK_CONTROL_STOP) == MASK_CONTROL_STOP) { 389 if (sep) { 390 buf.append(","); 391 } 392 else { 393 sep = true; 394 } 395 buf.append(CONTROL_STOP); 396 } 397 398 if ((mask & MASK_CONTROL_SHUTDOWN) == MASK_CONTROL_SHUTDOWN) { 399 if (sep) { 400 buf.append(","); 401 } 402 else { 403 sep = true; 404 } 405 buf.append(CONTROL_SHUTDOWN); 406 } 407 408 if ((mask & MASK_CONTROL_RELOAD) == MASK_CONTROL_RELOAD) { 409 if (sep) { 410 buf.append(","); 411 } 412 else { 413 sep = true; 414 } 415 buf.append(CONTROL_RELOAD); 416 } 417 418 return buf.toString(); 419 } 420} 421