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    *      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 &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      private static final long serialVersionUID = -8682149075879731987L;
88  
89      /**
90       * The target name when associated with control actions
91       * (&quot;control&quot;).
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 &quot;wildcard&quot; 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