Shuyi Chen | d7955ce | 2013-05-22 14:51:55 -0700 | [diff] [blame] | 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 | } |