View Javadoc
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 &quot;control&quot;, 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 &quot;*&quot; 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&quot;Name</th>
42   *   <th>Action</th>
43   *   <th>Description</th>
44   *  </tr>
45   *  <tr>
46   *   <td rowspan="5">&quot;control&quot;</td>
47   *   <td>&quot;start&quot;</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>&quot;stop&quot;</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>&quot;shutdown&quot;</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>&quot;reload&quot;</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>&quot;*&quot;</td>
76   *   <td>
77   *    The special wildcard action implies all above-mentioned action. This is
78   *    equal to construct a permission with the &quot;start, stop, shutdown,
79   *    reload&quot; 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       * (&quot;control&quot;).
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 &quot;wildcard&quot; 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