001/**
002 *
003 * Copyright 2005-2007 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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 */
017package org.jivesoftware.smackx.commands;
018
019import java.util.List;
020
021import org.jivesoftware.smack.SmackException.NoResponseException;
022import org.jivesoftware.smack.SmackException.NotConnectedException;
023import org.jivesoftware.smack.XMPPException.XMPPErrorException;
024import org.jivesoftware.smack.packet.XMPPError;
025
026import org.jivesoftware.smackx.commands.packet.AdHocCommandData;
027import org.jivesoftware.smackx.xdata.Form;
028
029import org.jxmpp.jid.Jid;
030
031/**
032 * An ad-hoc command is responsible for executing the provided service and
033 * storing the result of the execution. Each new request will create a new
034 * instance of the command, allowing information related to executions to be
035 * stored in it. For example suppose that a command that retrieves the list of
036 * users on a server is implemented. When the command is executed it gets that
037 * list and the result is stored as a form in the command instance, i.e. the
038 * <code>getForm</code> method retrieves a form with all the users.
039 * <p>
040 * Each command has a <tt>node</tt> that should be unique within a given JID.
041 * </p>
042 * <p>
043 * Commands may have zero or more stages. Each stage is usually used for
044 * gathering information required for the command execution. Users are able to
045 * move forward or backward across the different stages. Commands may not be
046 * cancelled while they are being executed. However, users may request the
047 * "cancel" action when submitting a stage response indicating that the command
048 * execution should be aborted. Thus, releasing any collected information.
049 * Commands that require user interaction (i.e. have more than one stage) will
050 * have to provide the data forms the user must complete in each stage and the
051 * allowed actions the user might perform during each stage (e.g. go to the
052 * previous stage or go to the next stage).
053 * </p>
054 * All the actions may throw an XMPPException if there is a problem executing
055 * them. The <code>XMPPError</code> of that exception may have some specific
056 * information about the problem. The possible extensions are:
057 * <ul>
058 * <li><i>malformed-action</i>. Extension of a <i>bad-request</i> error.</li>
059 * <li><i>bad-action</i>. Extension of a <i>bad-request</i> error.</li>
060 * <li><i>bad-locale</i>. Extension of a <i>bad-request</i> error.</li>
061 * <li><i>bad-payload</i>. Extension of a <i>bad-request</i> error.</li>
062 * <li><i>bad-sessionid</i>. Extension of a <i>bad-request</i> error.</li>
063 * <li><i>session-expired</i>. Extension of a <i>not-allowed</i> error.</li>
064 * </ul>
065 * <p>
066 * See the <code>SpecificErrorCondition</code> class for detailed description
067 * of each one.
068 * </p>
069 * Use the <code>getSpecificErrorConditionFrom</code> to obtain the specific
070 * information from an <code>XMPPError</code>.
071 * 
072 * @author Gabriel Guardincerri
073 * 
074 */
075public abstract class AdHocCommand {
076    // TODO: Analyze the redesign of command by having an ExecutionResponse as a
077    // TODO: result to the execution of every action. That result should have all the
078    // TODO: information related to the execution, e.g. the form to fill. Maybe this
079    // TODO: design is more intuitive and simpler than the current one that has all in
080    // TODO: one class.
081
082    private AdHocCommandData data;
083
084    public AdHocCommand() {
085        super();
086        data = new AdHocCommandData();
087    }
088
089    /**
090     * Returns the specific condition of the <code>error</code> or <tt>null</tt> if the
091     * error doesn't have any.
092     * 
093     * @param error the error the get the specific condition from.
094     * @return the specific condition of this error, or null if it doesn't have
095     *         any.
096     */
097    public static SpecificErrorCondition getSpecificErrorCondition(XMPPError error) {
098        // This method is implemented to provide an easy way of getting a packet
099        // extension of the XMPPError.
100        for (SpecificErrorCondition condition : SpecificErrorCondition.values()) {
101            if (error.getExtension(condition.toString(),
102                    AdHocCommandData.SpecificError.namespace) != null) {
103                return condition;
104            }
105        }
106        return null;
107    }
108
109    /**
110     * Set the the human readable name of the command, usually used for
111     * displaying in a UI.
112     * 
113     * @param name the name.
114     */
115    public void setName(String name) {
116        data.setName(name);
117    }
118
119    /**
120     * Returns the human readable name of the command.
121     * 
122     * @return the human readable name of the command
123     */
124    public String getName() {
125        return data.getName();
126    }
127
128    /**
129     * Sets the unique identifier of the command. This value must be unique for
130     * the <code>OwnerJID</code>.
131     * 
132     * @param node the unique identifier of the command.
133     */
134    public void setNode(String node) {
135        data.setNode(node);
136    }
137
138    /**
139     * Returns the unique identifier of the command. It is unique for the
140     * <code>OwnerJID</code>.
141     * 
142     * @return the unique identifier of the command.
143     */
144    public String getNode() {
145        return data.getNode();
146    }
147
148    /**
149     * Returns the full JID of the owner of this command. This JID is the "to" of a
150     * execution request.
151     * 
152     * @return the owner JID.
153     */
154    public abstract Jid getOwnerJID();
155
156    /**
157     * Returns the notes that the command has at the current stage.
158     * 
159     * @return a list of notes.
160     */
161    public List<AdHocCommandNote> getNotes() {
162        return data.getNotes();
163    }
164
165    /**
166     * Adds a note to the current stage. This should be used when setting a
167     * response to the execution of an action. All the notes added here are
168     * returned by the {@link #getNotes} method during the current stage.
169     * Once the stage changes all the notes are discarded.
170     * 
171     * @param note the note.
172     */
173    protected void addNote(AdHocCommandNote note) {
174        data.addNote(note);
175    }
176
177    public String getRaw() {
178        return data.getChildElementXML().toString();
179    }
180
181    /**
182     * Returns the form of the current stage. Usually it is the form that must
183     * be answered to execute the next action. If that is the case it should be
184     * used by the requester to fill all the information that the executor needs
185     * to continue to the next stage. It can also be the result of the
186     * execution.
187     * 
188     * @return the form of the current stage to fill out or the result of the
189     *         execution.
190     */
191    public Form getForm() {
192        if (data.getForm() == null) {
193            return null;
194        }
195        else {
196            return new Form(data.getForm());
197        }
198    }
199
200    /**
201     * Sets the form of the current stage. This should be used when setting a
202     * response. It could be a form to fill out the information needed to go to
203     * the next stage or the result of an execution.
204     * 
205     * @param form the form of the current stage to fill out or the result of the
206     *      execution.
207     */
208    protected void setForm(Form form) {
209        data.setForm(form.getDataFormToSend());
210    }
211
212    /**
213     * Executes the command. This is invoked only on the first stage of the
214     * command. It is invoked on every command. If there is a problem executing
215     * the command it throws an XMPPException.
216     *
217     * @throws NoResponseException
218     * @throws XMPPErrorException if there is an error executing the command.
219     * @throws NotConnectedException 
220     * @throws InterruptedException 
221     */
222    public abstract void execute() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
223
224    /**
225     * Executes the next action of the command with the information provided in
226     * the <code>response</code>. This form must be the answer form of the
227     * previous stage. This method will be only invoked for commands that have one
228     * or more stages. If there is a problem executing the command it throws an
229     * XMPPException.
230     * 
231     * @param response the form answer of the previous stage.
232     * @throws NoResponseException
233     * @throws XMPPErrorException if there is a problem executing the command.
234     * @throws NotConnectedException 
235     * @throws InterruptedException 
236     */
237    public abstract void next(Form response) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
238
239    /**
240     * Completes the command execution with the information provided in the
241     * <code>response</code>. This form must be the answer form of the
242     * previous stage. This method will be only invoked for commands that have one
243     * or more stages. If there is a problem executing the command it throws an
244     * XMPPException.
245     * 
246     * @param response the form answer of the previous stage.
247     *
248     * @throws NoResponseException
249     * @throws XMPPErrorException if there is a problem executing the command.
250     * @throws NotConnectedException 
251     * @throws InterruptedException 
252     */
253    public abstract void complete(Form response) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
254
255    /**
256     * Goes to the previous stage. The requester is asking to re-send the
257     * information of the previous stage. The command must change it state to
258     * the previous one. If there is a problem executing the command it throws
259     * an XMPPException.
260     *
261     * @throws NoResponseException
262     * @throws XMPPErrorException if there is a problem executing the command.
263     * @throws NotConnectedException 
264     * @throws InterruptedException 
265     */
266    public abstract void prev() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
267
268    /**
269     * Cancels the execution of the command. This can be invoked on any stage of
270     * the execution. If there is a problem executing the command it throws an
271     * XMPPException.
272     *
273     * @throws NoResponseException
274     * @throws XMPPErrorException if there is a problem executing the command.
275     * @throws NotConnectedException 
276     * @throws InterruptedException 
277     */
278    public abstract void cancel() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
279
280    /**
281     * Returns a collection with the allowed actions based on the current stage.
282     * Possible actions are: {@link Action#prev prev}, {@link Action#next next} and
283     * {@link Action#complete complete}. This method will be only invoked for commands that
284     * have one or more stages.
285     * 
286     * @return a collection with the allowed actions based on the current stage
287     *      as defined in the SessionData.
288     */
289    protected List<Action> getActions() {
290        return data.getActions();
291    }
292
293    /**
294     * Add an action to the current stage available actions. This should be used
295     * when creating a response.
296     * 
297     * @param action the action.
298     */
299    protected void addActionAvailable(Action action) {
300        data.addAction(action);
301    }
302
303    /**
304     * Returns the action available for the current stage which is
305     * considered the equivalent to "execute". When the requester sends his
306     * reply, if no action was defined in the command then the action will be
307     * assumed "execute" thus assuming the action returned by this method. This
308     * method will never be invoked for commands that have no stages.
309     * 
310     * @return the action available for the current stage which is considered
311     *      the equivalent to "execute".
312     */
313    protected Action getExecuteAction() {
314        return data.getExecuteAction();
315    }
316
317    /**
318     * Sets which of the actions available for the current stage is
319     * considered the equivalent to "execute". This should be used when setting
320     * a response. When the requester sends his reply, if no action was defined
321     * in the command then the action will be assumed "execute" thus assuming
322     * the action returned by this method.
323     * 
324     * @param action the action.
325     */
326    protected void setExecuteAction(Action action) {
327        data.setExecuteAction(action);
328    }
329
330    /**
331     * Returns the status of the current stage.
332     * 
333     * @return the current status.
334     */
335    public Status getStatus() {
336        return data.getStatus();
337    }
338
339    /**
340     * Check if this command has been completed successfully.
341     * 
342     * @return <code>true</code> if this command is completed.
343     * @since 4.2
344     */
345    public boolean isCompleted() {
346        return getStatus() == Status.completed;
347    }
348
349    /**
350     * Sets the data of the current stage. This should not used.
351     * 
352     * @param data the data.
353     */
354    void setData(AdHocCommandData data) {
355        this.data = data;
356    }
357
358    /**
359     * Gets the data of the current stage. This should not used.
360     *
361     * @return the data.
362     */
363    AdHocCommandData getData() {
364        return data;
365    }
366
367    /**
368     * Returns true if the <code>action</code> is available in the current stage.
369     * The {@link Action#cancel cancel} action is always allowed. To define the
370     * available actions use the <code>addActionAvailable</code> method.
371     * 
372     * @param action
373     *            The action to check if it is available.
374     * @return True if the action is available for the current stage.
375     */
376    protected boolean isValidAction(Action action) {
377        return getActions().contains(action) || Action.cancel.equals(action);
378    }
379
380    /**
381     * The status of the stage in the adhoc command.
382     */
383    public enum Status {
384
385        /**
386         * The command is being executed.
387         */
388        executing,
389
390        /**
391         * The command has completed. The command session has ended.
392         */
393        completed,
394
395        /**
396         * The command has been canceled. The command session has ended.
397         */
398        canceled
399    }
400
401    public enum Action {
402
403        /**
404         * The command should be executed or continue to be executed. This is
405         * the default value.
406         */
407        execute,
408
409        /**
410         * The command should be canceled.
411         */
412        cancel,
413
414        /**
415         * The command should be digress to the previous stage of execution.
416         */
417        prev,
418
419        /**
420         * The command should progress to the next stage of execution.
421         */
422        next,
423
424        /**
425         * The command should be completed (if possible).
426         */
427        complete,
428
429        /**
430         * The action is unknown. This is used when a received message has an
431         * unknown action. It must not be used to send an execution request.
432         */
433        unknown
434    }
435
436    public enum SpecificErrorCondition {
437
438        /**
439         * The responding JID cannot accept the specified action.
440         */
441        badAction("bad-action"),
442
443        /**
444         * The responding JID does not understand the specified action.
445         */
446        malformedAction("malformed-action"),
447
448        /**
449         * The responding JID cannot accept the specified language/locale.
450         */
451        badLocale("bad-locale"),
452
453        /**
454         * The responding JID cannot accept the specified payload (e.g. the data
455         * form did not provide one or more required fields).
456         */
457        badPayload("bad-payload"),
458
459        /**
460         * The responding JID cannot accept the specified sessionid.
461         */
462        badSessionid("bad-sessionid"),
463
464        /**
465         * The requesting JID specified a sessionid that is no longer active
466         * (either because it was completed, canceled, or timed out).
467         */
468        sessionExpired("session-expired");
469
470        private final String value;
471
472        SpecificErrorCondition(String value) {
473            this.value = value;
474        }
475
476        @Override
477        public String toString() {
478            return value;
479        }
480    }
481}