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