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 org.jivesoftware.smack.SmackException.NoResponseException;
020import org.jivesoftware.smack.SmackException.NotConnectedException;
021import org.jivesoftware.smack.XMPPException.XMPPErrorException;
022import org.jivesoftware.smack.packet.StanzaError;
023import org.jivesoftware.smackx.commands.packet.AdHocCommandData;
024import org.jivesoftware.smackx.commands.packet.AdHocCommandDataBuilder;
025import org.jivesoftware.smackx.xdata.form.SubmitForm;
026
027import org.jxmpp.jid.Jid;
028
029/**
030 * Represents a command that can be executed locally from a remote location. This
031 * class must be extended to implement an specific ad-hoc command. This class
032 * provides some useful tools:<ul>
033 *      <li>Node</li>
034 *      <li>Name</li>
035 *      <li>Session ID</li>
036 *      <li>Current Stage</li>
037 *      <li>Available actions</li>
038 *      <li>Default action</li>
039 * </ul>
040 * To implement a new command extend this class and implement all the abstract
041 * methods. When implementing the actions remember that they could be invoked
042 * several times, and that you must use the current stage number to know what to
043 * do.
044 *
045 * @author Gabriel Guardincerri
046 * @author Florian Schmaus
047 */
048public abstract class AdHocCommandHandler extends AbstractAdHocCommand {
049
050    /**
051     * The time stamp of first invocation of the command. Used to implement the session timeout.
052     */
053    private final long creationDate;
054
055    /**
056     * The number of the current stage.
057     */
058    private int currentStage;
059
060    public AdHocCommandHandler(String node, String name, String sessionId) {
061        super(node, name);
062        setSessionId(sessionId);
063        this.creationDate = System.currentTimeMillis();
064    }
065
066    protected abstract AdHocCommandData execute(AdHocCommandDataBuilder response) throws NoResponseException,
067                    XMPPErrorException, NotConnectedException, InterruptedException, IllegalStateException;
068
069    protected abstract AdHocCommandData next(AdHocCommandDataBuilder response, SubmitForm submittedForm)
070                    throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException,
071                    IllegalStateException;
072
073    protected abstract AdHocCommandData complete(AdHocCommandDataBuilder response, SubmitForm submittedForm)
074                    throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException,
075                    IllegalStateException;
076
077    protected abstract AdHocCommandData prev(AdHocCommandDataBuilder response) throws NoResponseException,
078                    XMPPErrorException, NotConnectedException, InterruptedException, IllegalStateException;
079
080    /**
081     * Returns the date the command was created.
082     *
083     * @return the date the command was created.
084     */
085    public long getCreationDate() {
086        return creationDate;
087    }
088
089    /**
090     * Returns true if the specified requester has permission to execute all the
091     * stages of this action. This is checked when the first request is received,
092     * if the permission is grant then the requester will be able to execute
093     * all the stages of the command. It is not checked again during the
094     * execution.
095     *
096     * @param jid the JID to check permissions on.
097     * @return true if the user has permission to execute this action.
098     */
099    public boolean hasPermission(Jid jid) {
100        return true;
101    };
102
103    /**
104     * Returns the currently executing stage number. The first stage number is
105     * 1. During the execution of the first action this method will answer 1.
106     *
107     * @return the current stage number.
108     */
109    public final int getCurrentStage() {
110        return currentStage;
111    }
112
113    /**
114     * Increase the current stage number.
115     */
116    final void incrementStage() {
117        currentStage++;
118    }
119
120    /**
121     * Decrease the current stage number.
122     */
123    final void decrementStage() {
124        currentStage--;
125    }
126
127    protected static XMPPErrorException newXmppErrorException(StanzaError.Condition condition) {
128        return newXmppErrorException(condition, null);
129    }
130
131    protected static XMPPErrorException newXmppErrorException(StanzaError.Condition condition, String descriptiveText) {
132        StanzaError stanzaError = StanzaError.from(condition, descriptiveText).build();
133        return new XMPPErrorException(null, stanzaError);
134    }
135
136    protected static XMPPErrorException newBadRequestException(String descriptiveTest) {
137        return newXmppErrorException(StanzaError.Condition.bad_request, descriptiveTest);
138    }
139
140    public abstract static class SingleStage extends AdHocCommandHandler {
141
142        public SingleStage(String node, String name, String sessionId) {
143            super(node, name, sessionId);
144        }
145
146        protected abstract AdHocCommandData executeSingleStage(AdHocCommandDataBuilder response)
147                        throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
148
149        @Override
150        protected final AdHocCommandData execute(AdHocCommandDataBuilder response)
151                        throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
152            response.setStatusCompleted();
153            return executeSingleStage(response);
154        }
155
156        @Override
157        public final AdHocCommandData next(AdHocCommandDataBuilder response, SubmitForm submittedForm)
158                        throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
159            throw newXmppErrorException(StanzaError.Condition.bad_request);
160        }
161
162        @Override
163        public final AdHocCommandData complete(AdHocCommandDataBuilder response, SubmitForm submittedForm)
164                        throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
165            throw newXmppErrorException(StanzaError.Condition.bad_request);
166        }
167
168        @Override
169        public final AdHocCommandData prev(AdHocCommandDataBuilder response)
170                        throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
171            throw newXmppErrorException(StanzaError.Condition.bad_request);
172        }
173
174        @Override
175        public final void cancel()
176                        throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
177            throw newXmppErrorException(StanzaError.Condition.bad_request);
178        }
179
180    }
181}