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