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