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