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 private static final long serialVersionUID = -8682149075879731987L;
88
89 /**
90 * The target name when associated with control actions
91 * ("control").
92 */
93 protected static final String CONTROL = "control";
94
95 /**
96 * The target type when associated with control actions.
97 */
98 protected static final int TYPE_CONTROL = 1;
99
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