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 }