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