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
018package org.apache.commons.daemon;
019
020import java.security.Permission;
021import 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 */
088public 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