Home | History | Annotate | Download | only in commands
      1 /**
      2  * $RCSfile$
      3  * $Revision$
      4  * $Date$
      5  *
      6  * Copyright 2005-2007 Jive Software.
      7  *
      8  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
      9  * you may not use this file except in compliance with the License.
     10  * You may obtain a copy of the License at
     11  *
     12  *     http://www.apache.org/licenses/LICENSE-2.0
     13  *
     14  * Unless required by applicable law or agreed to in writing, software
     15  * distributed under the License is distributed on an "AS IS" BASIS,
     16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     17  * See the License for the specific language governing permissions and
     18  * limitations under the License.
     19  */
     20 package org.jivesoftware.smackx.commands;
     21 
     22 import org.jivesoftware.smack.XMPPException;
     23 import org.jivesoftware.smack.packet.XMPPError;
     24 import org.jivesoftware.smackx.Form;
     25 import org.jivesoftware.smackx.packet.AdHocCommandData;
     26 
     27 import java.util.List;
     28 
     29 /**
     30  * An ad-hoc command is responsible for executing the provided service and
     31  * storing the result of the execution. Each new request will create a new
     32  * instance of the command, allowing information related to executions to be
     33  * stored in it. For example suppose that a command that retrieves the list of
     34  * users on a server is implemented. When the command is executed it gets that
     35  * list and the result is stored as a form in the command instance, i.e. the
     36  * <code>getForm</code> method retrieves a form with all the users.
     37  * <p>
     38  * Each command has a <tt>node</tt> that should be unique within a given JID.
     39  * <p>
     40  * Commands may have zero or more stages. Each stage is usually used for
     41  * gathering information required for the command execution. Users are able to
     42  * move forward or backward across the different stages. Commands may not be
     43  * cancelled while they are being executed. However, users may request the
     44  * "cancel" action when submitting a stage response indicating that the command
     45  * execution should be aborted. Thus, releasing any collected information.
     46  * Commands that require user interaction (i.e. have more than one stage) will
     47  * have to provide the data forms the user must complete in each stage and the
     48  * allowed actions the user might perform during each stage (e.g. go to the
     49  * previous stage or go to the next stage).
     50  * <p>
     51  * All the actions may throw an XMPPException if there is a problem executing
     52  * them. The <code>XMPPError</code> of that exception may have some specific
     53  * information about the problem. The possible extensions are:
     54  *
     55  * <li><i>malformed-action</i>. Extension of a <i>bad-request</i> error.</li>
     56  * <li><i>bad-action</i>. Extension of a <i>bad-request</i> error.</li>
     57  * <li><i>bad-locale</i>. Extension of a <i>bad-request</i> error.</li>
     58  * <li><i>bad-payload</i>. Extension of a <i>bad-request</i> error.</li>
     59  * <li><i>bad-sessionid</i>. Extension of a <i>bad-request</i> error.</li>
     60  * <li><i>session-expired</i>. Extension of a <i>not-allowed</i> error.</li>
     61  * <p>
     62  * See the <code>SpecificErrorCondition</code> class for detailed description
     63  * of each one.
     64  * <p>
     65  * Use the <code>getSpecificErrorConditionFrom</code> to obtain the specific
     66  * information from an <code>XMPPError</code>.
     67  *
     68  * @author Gabriel Guardincerri
     69  *
     70  */
     71 public abstract class AdHocCommand {
     72     // TODO: Analyze the redesign of command by having an ExecutionResponse as a
     73     // TODO: result to the execution of every action. That result should have all the
     74     // TODO: information related to the execution, e.g. the form to fill. Maybe this
     75     // TODO: design is more intuitive and simpler than the current one that has all in
     76     // TODO: one class.
     77 
     78     private AdHocCommandData data;
     79 
     80     public AdHocCommand() {
     81         super();
     82         data = new AdHocCommandData();
     83     }
     84 
     85     /**
     86      * Returns the specific condition of the <code>error</code> or <tt>null</tt> if the
     87      * error doesn't have any.
     88      *
     89      * @param error the error the get the specific condition from.
     90      * @return the specific condition of this error, or null if it doesn't have
     91      *         any.
     92      */
     93     public static SpecificErrorCondition getSpecificErrorCondition(XMPPError error) {
     94         // This method is implemented to provide an easy way of getting a packet
     95         // extension of the XMPPError.
     96         for (SpecificErrorCondition condition : SpecificErrorCondition.values()) {
     97             if (error.getExtension(condition.toString(),
     98                     AdHocCommandData.SpecificError.namespace) != null) {
     99                 return condition;
    100             }
    101         }
    102         return null;
    103     }
    104 
    105     /**
    106      * Set the the human readable name of the command, usually used for
    107      * displaying in a UI.
    108      *
    109      * @param name the name.
    110      */
    111     public void setName(String name) {
    112         data.setName(name);
    113     }
    114 
    115     /**
    116      * Returns the human readable name of the command.
    117      *
    118      * @return the human readable name of the command
    119      */
    120     public String getName() {
    121         return data.getName();
    122     }
    123 
    124     /**
    125      * Sets the unique identifier of the command. This value must be unique for
    126      * the <code>OwnerJID</code>.
    127      *
    128      * @param node the unique identifier of the command.
    129      */
    130     public void setNode(String node) {
    131         data.setNode(node);
    132     }
    133 
    134     /**
    135      * Returns the unique identifier of the command. It is unique for the
    136      * <code>OwnerJID</code>.
    137      *
    138      * @return the unique identifier of the command.
    139      */
    140     public String getNode() {
    141         return data.getNode();
    142     }
    143 
    144     /**
    145      * Returns the full JID of the owner of this command. This JID is the "to" of a
    146      * execution request.
    147      *
    148      * @return the owner JID.
    149      */
    150     public abstract String getOwnerJID();
    151 
    152     /**
    153      * Returns the notes that the command has at the current stage.
    154      *
    155      * @return a list of notes.
    156      */
    157     public List<AdHocCommandNote> getNotes() {
    158         return data.getNotes();
    159     }
    160 
    161     /**
    162      * Adds a note to the current stage. This should be used when setting a
    163      * response to the execution of an action. All the notes added here are
    164      * returned by the {@link #getNotes} method during the current stage.
    165      * Once the stage changes all the notes are discarded.
    166      *
    167      * @param note the note.
    168      */
    169     protected void addNote(AdHocCommandNote note) {
    170         data.addNote(note);
    171     }
    172 
    173     public String getRaw() {
    174         return data.getChildElementXML();
    175     }
    176 
    177     /**
    178      * Returns the form of the current stage. Usually it is the form that must
    179      * be answered to execute the next action. If that is the case it should be
    180      * used by the requester to fill all the information that the executor needs
    181      * to continue to the next stage. It can also be the result of the
    182      * execution.
    183      *
    184      * @return the form of the current stage to fill out or the result of the
    185      *         execution.
    186      */
    187     public Form getForm() {
    188         if (data.getForm() == null) {
    189             return null;
    190         }
    191         else {
    192             return new Form(data.getForm());
    193         }
    194     }
    195 
    196     /**
    197      * Sets the form of the current stage. This should be used when setting a
    198      * response. It could be a form to fill out the information needed to go to
    199      * the next stage or the result of an execution.
    200      *
    201      * @param form the form of the current stage to fill out or the result of the
    202      *      execution.
    203      */
    204     protected void setForm(Form form) {
    205         data.setForm(form.getDataFormToSend());
    206     }
    207 
    208     /**
    209      * Executes the command. This is invoked only on the first stage of the
    210      * command. It is invoked on every command. If there is a problem executing
    211      * the command it throws an XMPPException.
    212      *
    213      * @throws XMPPException if there is an error executing the command.
    214      */
    215     public abstract void execute() throws XMPPException;
    216 
    217     /**
    218      * Executes the next action of the command with the information provided in
    219      * the <code>response</code>. This form must be the answer form of the
    220      * previous stage. This method will be only invoked for commands that have one
    221      * or more stages. If there is a problem executing the command it throws an
    222      * XMPPException.
    223      *
    224      * @param response the form answer of the previous stage.
    225      * @throws XMPPException if there is a problem executing the command.
    226      */
    227     public abstract void next(Form response) throws XMPPException;
    228 
    229     /**
    230      * Completes the command execution with the information provided in the
    231      * <code>response</code>. This form must be the answer form of the
    232      * previous stage. This method will be only invoked for commands that have one
    233      * or more stages. If there is a problem executing the command it throws an
    234      * XMPPException.
    235      *
    236      * @param response the form answer of the previous stage.
    237      * @throws XMPPException if there is a problem executing the command.
    238      */
    239     public abstract void complete(Form response) throws XMPPException;
    240 
    241     /**
    242      * Goes to the previous stage. The requester is asking to re-send the
    243      * information of the previous stage. The command must change it state to
    244      * the previous one. If there is a problem executing the command it throws
    245      * an XMPPException.
    246      *
    247      * @throws XMPPException if there is a problem executing the command.
    248      */
    249     public abstract void prev() throws XMPPException;
    250 
    251     /**
    252      * Cancels the execution of the command. This can be invoked on any stage of
    253      * the execution. If there is a problem executing the command it throws an
    254      * XMPPException.
    255      *
    256      * @throws XMPPException if there is a problem executing the command.
    257      */
    258     public abstract void cancel() throws XMPPException;
    259 
    260     /**
    261      * Returns a collection with the allowed actions based on the current stage.
    262      * Possible actions are: {@link Action#prev prev}, {@link Action#next next} and
    263      * {@link Action#complete complete}. This method will be only invoked for commands that
    264      * have one or more stages.
    265      *
    266      * @return a collection with the allowed actions based on the current stage
    267      *      as defined in the SessionData.
    268      */
    269     protected List<Action> getActions() {
    270         return data.getActions();
    271     }
    272 
    273     /**
    274      * Add an action to the current stage available actions. This should be used
    275      * when creating a response.
    276      *
    277      * @param action the action.
    278      */
    279     protected void addActionAvailable(Action action) {
    280         data.addAction(action);
    281     }
    282 
    283     /**
    284      * Returns the action available for the current stage which is
    285      * considered the equivalent to "execute". When the requester sends his
    286      * reply, if no action was defined in the command then the action will be
    287      * assumed "execute" thus assuming the action returned by this method. This
    288      * method will never be invoked for commands that have no stages.
    289      *
    290      * @return the action available for the current stage which is considered
    291      *      the equivalent to "execute".
    292      */
    293     protected Action getExecuteAction() {
    294         return data.getExecuteAction();
    295     }
    296 
    297     /**
    298      * Sets which of the actions available for the current stage is
    299      * considered the equivalent to "execute". This should be used when setting
    300      * a response. When the requester sends his reply, if no action was defined
    301      * in the command then the action will be assumed "execute" thus assuming
    302      * the action returned by this method.
    303      *
    304      * @param action the action.
    305      */
    306     protected void setExecuteAction(Action action) {
    307         data.setExecuteAction(action);
    308     }
    309 
    310     /**
    311      * Returns the status of the current stage.
    312      *
    313      * @return the current status.
    314      */
    315     public Status getStatus() {
    316         return data.getStatus();
    317     }
    318 
    319     /**
    320      * Sets the data of the current stage. This should not used.
    321      *
    322      * @param data the data.
    323      */
    324     void setData(AdHocCommandData data) {
    325         this.data = data;
    326     }
    327 
    328     /**
    329      * Gets the data of the current stage. This should not used.
    330      *
    331      * @return the data.
    332      */
    333     AdHocCommandData getData() {
    334         return data;
    335     }
    336 
    337     /**
    338      * Returns true if the <code>action</code> is available in the current stage.
    339      * The {@link Action#cancel cancel} action is always allowed. To define the
    340      * available actions use the <code>addActionAvailable</code> method.
    341      *
    342      * @param action
    343      *            The action to check if it is available.
    344      * @return True if the action is available for the current stage.
    345      */
    346     protected boolean isValidAction(Action action) {
    347         return getActions().contains(action) || Action.cancel.equals(action);
    348     }
    349 
    350     /**
    351      * The status of the stage in the adhoc command.
    352      */
    353     public enum Status {
    354 
    355         /**
    356          * The command is being executed.
    357          */
    358         executing,
    359 
    360         /**
    361          * The command has completed. The command session has ended.
    362          */
    363         completed,
    364 
    365         /**
    366          * The command has been canceled. The command session has ended.
    367          */
    368         canceled
    369     }
    370 
    371     public enum Action {
    372 
    373         /**
    374          * The command should be executed or continue to be executed. This is
    375          * the default value.
    376          */
    377         execute,
    378 
    379         /**
    380          * The command should be canceled.
    381          */
    382         cancel,
    383 
    384         /**
    385          * The command should be digress to the previous stage of execution.
    386          */
    387         prev,
    388 
    389         /**
    390          * The command should progress to the next stage of execution.
    391          */
    392         next,
    393 
    394         /**
    395          * The command should be completed (if possible).
    396          */
    397         complete,
    398 
    399         /**
    400          * The action is unknow. This is used when a recieved message has an
    401          * unknown action. It must not be used to send an execution request.
    402          */
    403         unknown
    404     }
    405 
    406     public enum SpecificErrorCondition {
    407 
    408         /**
    409          * The responding JID cannot accept the specified action.
    410          */
    411         badAction("bad-action"),
    412 
    413         /**
    414          * The responding JID does not understand the specified action.
    415          */
    416         malformedAction("malformed-action"),
    417 
    418         /**
    419          * The responding JID cannot accept the specified language/locale.
    420          */
    421         badLocale("bad-locale"),
    422 
    423         /**
    424          * The responding JID cannot accept the specified payload (e.g. the data
    425          * form did not provide one or more required fields).
    426          */
    427         badPayload("bad-payload"),
    428 
    429         /**
    430          * The responding JID cannot accept the specified sessionid.
    431          */
    432         badSessionid("bad-sessionid"),
    433 
    434         /**
    435          * The requesting JID specified a sessionid that is no longer active
    436          * (either because it was completed, canceled, or timed out).
    437          */
    438         sessionExpired("session-expired");
    439 
    440         private String value;
    441 
    442         SpecificErrorCondition(String value) {
    443             this.value = value;
    444         }
    445 
    446         public String toString() {
    447             return value;
    448         }
    449     }
    450 }