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     *      http://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    
018    package org.apache.commons.daemon;
019    
020    import java.security.Permission;
021    import java.util.StringTokenizer;
022    
023    /**
024     * Represents the permissions to control and query the status of
025     * a <code>Daemon</code>. A <code>DaemonPermission</code> 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 &quot;control&quot;, 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 &quot;*&quot; value implies all permissions for the given
036     * name:
037     * </p>
038     * <p>
039     * <table width="100%" border="1">
040     *  <tr>
041     *   <th>Target&quot;Name</th>
042     *   <th>Action</th>
043     *   <th>Description</th>
044     *  </tr>
045     *  <tr>
046     *   <td rowspan="5">&quot;control&quot;</td>
047     *   <td>&quot;start&quot;</td>
048     *   <td>
049     *    The permission to call the <code>start()</code> method in an instance
050     *    of a <code>DaemonController</code> interface.
051     *   </td>
052     *  </tr>
053     *  <tr>
054     *   <td>&quot;stop&quot;</td>
055     *   <td>
056     *    The permission to call the <code>stop()</code> method in an instance
057     *    of a <code>DaemonController</code> interface.
058     *   </td>
059     *  </tr>
060     *  <tr>
061     *   <td>&quot;shutdown&quot;</td>
062     *   <td>
063     *    The permission to call the <code>shutdown()</code> method in an instance
064     *    of a <code>DaemonController</code> interface.
065     *   </td>
066     *  </tr>
067     *  <tr>
068     *   <td>&quot;reload&quot;</td>
069     *   <td>
070     *    The permission to call the <code>reload()</code> method in an instance
071     *    of a <code>DaemonController</code> interface.
072     *   </td>
073     *  </tr>
074     *  <tr>
075     *   <td>&quot;*&quot;</td>
076     *   <td>
077     *    The special wildcard action implies all above-mentioned action. This is
078     *    equal to construct a permission with the &quot;start, stop, shutdown,
079     *    reload&quot; list of actions.
080     *   </td>
081     *  </tr>
082     * </table>
083     * </p>
084     *
085     * @author Pier Fumagalli
086     * @version $Id: DaemonPermission.java 1204010 2011-11-19 16:15:23Z ggregory $
087     */
088    public final class DaemonPermission extends Permission
089    {
090    
091        /* ====================================================================
092         * Constants.
093         */
094    
095        /**
096         * The target name when associated with control actions
097         * (&quot;control&quot;).
098         */
099        protected static final String CONTROL = "control";
100    
101        /**
102         * The target type when associated with control actions.
103         */
104        protected static final int TYPE_CONTROL = 1;
105    
106        /**
107         * The action name associated with the permission to call the
108         * <code>DaemonController.start()</code> method.
109         */
110        protected static final String CONTROL_START = "start";
111    
112        /**
113         * The action name associated with the permission to call the
114         * <code>DaemonController.stop()</code> method.
115         */
116        protected static final String CONTROL_STOP = "stop";
117    
118        /**
119         * The action name associated with the permission to call the
120         * <code>DaemonController.shutdown()</code> method.
121         */
122        protected static final String CONTROL_SHUTDOWN = "shutdown";
123    
124        /**
125         * The action name associated with the permission to call the
126         * <code>DaemonController.reload()</code> method.
127         */
128        protected static final String CONTROL_RELOAD = "reload";
129    
130        /**
131         * The action mask associated with the permission to call the
132         * <code>DaemonController.start()</code> method.
133         */
134        protected static final int MASK_CONTROL_START = 0x01;
135    
136        /**
137         * The action mask associated with the permission to call the
138         * <code>DaemonController.stop()</code> method.
139         */
140        protected static final int MASK_CONTROL_STOP = 0x02;
141    
142        /**
143         * The action mask associated with the permission to call the
144         * <code>DaemonController.shutdown()</code> method.
145         */
146        protected static final int MASK_CONTROL_SHUTDOWN = 0x04;
147    
148        /**
149         * The action mask associated with the permission to call the
150         * <code>DaemonController.reload()</code> method.
151         */
152        protected static final int MASK_CONTROL_RELOAD = 0x08;
153    
154        /**
155         * The &quot;wildcard&quot; action implying all actions for the given
156         * target name.
157         */
158        protected static final String WILDCARD = "*";
159    
160        /* ====================================================================
161         * Instance variables
162         */
163    
164        /** The type of this permission object. */
165        private transient int type = 0;
166        /** The permission mask associated with this permission object. */
167        private transient int mask = 0;
168        /** The String representation of this permission object. */
169        private transient String desc = null;
170    
171        /* ====================================================================
172         * Constructors
173         */
174    
175        /**
176         * Creates a new <code>DaemonPermission</code> instance with a specified
177         * permission name.
178         * <p>
179         * This constructor will create a new <code>DaemonPermission</code>
180         * instance that <b>will not</b> grant any permission to the caller.
181         *
182         * @param target The target name of this permission.
183         * @exception IllegalArgumentException If the specified target name is not
184         *                supported.
185         */
186        public DaemonPermission(String target)
187            throws IllegalArgumentException
188        {
189            // Setup the target name of this permission object.
190            super(target);
191    
192            // Check if the permission target name was specified
193            if (target == null)
194                throw new IllegalArgumentException("Null permission name");
195    
196            // Check if this is a "control" permission and set up accordingly.
197            if (CONTROL.equalsIgnoreCase(target)) {
198                type = TYPE_CONTROL;
199                return;
200            }
201    
202            // If we got here, we have an invalid permission name.
203            throw new IllegalArgumentException("Invalid permission name \"" +
204                                               target + "\" specified");
205        }
206    
207        /**
208         * Creates a new <code>DaemonPermission</code> instance with a specified
209         * permission name and a specified list of actions.
210         * <p>
211         * </p>
212         *
213         * @param target The target name of this permission.
214         * @param actions The list of actions permitted by this permission.
215         * @exception IllegalArgumentException If the specified target name is not
216         *                supported, or the specified list of actions includes an
217         *                invalid value.
218         */
219        public DaemonPermission(String target, String actions)
220            throws IllegalArgumentException
221        {
222            // Setup this instance's target name.
223            this(target);
224    
225            // Create the appropriate mask if this is a control permission.
226            if (this.type == TYPE_CONTROL) {
227                this.mask = this.createControlMask(actions);
228                return;
229            }
230        }
231    
232        /* ====================================================================
233         * Public methods
234         */
235    
236        /**
237         * Returns the list of actions permitted by this instance of
238         * <code>DaemonPermission</code> in its canonical form.
239         *
240         * @return The canonicalized list of actions.
241         */
242        public String getActions()
243        {
244            if (this.type == TYPE_CONTROL) {
245                return this.createControlActions(this.mask);
246            }
247            return "";
248        }
249    
250        /**
251         * Returns the hash code for this <code>DaemonPermission</code> instance.
252         *
253         * @return An hash code value.
254         */
255        public int hashCode()
256        {
257            this.setupDescription();
258            return this.desc.hashCode();
259        }
260    
261        /**
262         * Checks if a specified object equals <code>DaemonPermission</code>.
263         *
264         * @return <b>true</b> or <b>false</b> wether the specified object equals
265         *         this <code>DaemonPermission</code> instance or not.
266         */
267        public boolean equals(Object object)
268        {
269            if (object == this)
270                return true;
271    
272            if (!(object instanceof DaemonPermission))
273                return false;
274    
275            DaemonPermission that = (DaemonPermission) object;
276    
277            if (this.type != that.type)
278                return false;
279            return this.mask == that.mask;
280        }
281    
282        /**
283         * Checks if this <code>DaemonPermission</code> implies another
284         * <code>Permission</code>.
285         *
286         * @return <b>true</b> or <b>false</b> wether the specified permission
287         *         is implied by this <code>DaemonPermission</code> instance or
288         *         not.
289         */
290        public boolean implies(Permission permission)
291        {
292            if (permission == this)
293                return true;
294    
295            if (!(permission instanceof DaemonPermission))
296                return false;
297    
298            DaemonPermission that = (DaemonPermission) permission;
299    
300            if (this.type != that.type)
301                return false;
302            return (this.mask & that.mask) == that.mask;
303        }
304    
305        /**
306         * Returns a <code>String</code> representation of this instance.
307         *
308         * @return A <code>String</code> representing this
309         *         <code>DaemonPermission</code> instance.
310         */
311        public String toString()
312        {
313            this.setupDescription();
314            return this.desc;
315        }
316    
317        /* ====================================================================
318         * Private methods
319         */
320    
321        /** 
322         * Creates a String description for this permission instance.
323         */
324        private void setupDescription()
325        {
326            if (this.desc != null)
327                return;
328    
329            StringBuffer buf = new StringBuffer();
330            buf.append(this.getClass().getName());
331            buf.append('[');
332            switch (this.type) {
333                case TYPE_CONTROL:
334                    buf.append(CONTROL);
335                break;
336                default:
337                    buf.append("UNKNOWN");
338                break;
339            }
340            buf.append(':');
341            buf.append(this.getActions());
342            buf.append(']');
343    
344            this.desc = buf.toString();
345        }
346    
347        /** 
348         * Creates a permission mask for a given control actions string.
349         */
350        private int createControlMask(String actions)
351            throws IllegalArgumentException
352        {
353            if (actions == null)
354                return 0;
355    
356            int mask = 0;
357            StringTokenizer tok = new StringTokenizer(actions, ",", false);
358    
359            while (tok.hasMoreTokens()) {
360                String val = tok.nextToken().trim();
361    
362                if (WILDCARD.equals(val)) {
363                    return MASK_CONTROL_START | MASK_CONTROL_STOP |
364                           MASK_CONTROL_SHUTDOWN | MASK_CONTROL_RELOAD;
365                }
366                else if (CONTROL_START.equalsIgnoreCase(val)) {
367                    mask = mask | MASK_CONTROL_START;
368                }
369                else if (CONTROL_STOP.equalsIgnoreCase(val)) {
370                    mask = mask | MASK_CONTROL_STOP;
371                }
372                else if (CONTROL_SHUTDOWN.equalsIgnoreCase(val)) {
373                    mask = mask | MASK_CONTROL_SHUTDOWN;
374                }
375                else if (CONTROL_RELOAD.equalsIgnoreCase(val)) {
376                    mask = mask | MASK_CONTROL_RELOAD;
377                }
378                else {
379                    throw new IllegalArgumentException("Invalid action name \"" +
380                                                       val + "\" specified");
381                }
382            }
383            return mask;
384        }
385    
386        /** Creates a actions list for a given control permission mask. */
387        private String createControlActions(int mask)
388        {
389            StringBuffer buf = new StringBuffer();
390            boolean sep = false;
391    
392            if ((mask & MASK_CONTROL_START) == MASK_CONTROL_START) {
393                sep = true;
394                buf.append(CONTROL_START);
395            }
396    
397            if ((mask & MASK_CONTROL_STOP) == MASK_CONTROL_STOP) {
398                if (sep)
399                    buf.append(",");
400                else
401                    sep = true;
402                buf.append(CONTROL_STOP);
403            }
404    
405            if ((mask & MASK_CONTROL_SHUTDOWN) == MASK_CONTROL_SHUTDOWN) {
406                if (sep)
407                    buf.append(",");
408                else
409                    sep = true;
410                buf.append(CONTROL_SHUTDOWN);
411            }
412    
413            if ((mask & MASK_CONTROL_RELOAD) == MASK_CONTROL_RELOAD) {
414                if (sep)
415                    buf.append(",");
416                else
417                    sep = true;
418                buf.append(CONTROL_RELOAD);
419            }
420    
421            return buf.toString();
422        }
423    }
424