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 * https://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 <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