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.XMPPConnection; 022import org.jivesoftware.smack.XMPPException.XMPPErrorException; 023import org.jivesoftware.smack.packet.IQ; 024import org.jivesoftware.smack.util.Objects; 025 026import org.jivesoftware.smackx.commands.packet.AdHocCommandData; 027import org.jivesoftware.smackx.xdata.form.FillableForm; 028import org.jivesoftware.smackx.xdata.form.SubmitForm; 029import org.jivesoftware.smackx.xdata.packet.DataForm; 030 031import org.jxmpp.jid.Jid; 032 033/** 034 * Represents a ad-hoc command invoked on a remote entity. Invoking one of the 035 * {@link #execute()}, {@link #next(SubmitForm)}, 036 * {@link #prev()}, {@link #cancel()} or 037 * {@link #complete(SubmitForm)} actions results in executing that 038 * action on the remote entity. In response to that action the internal state 039 * of the this command instance will change. For example, if the command is a 040 * single stage command, then invoking the execute action will execute this 041 * action in the remote location. After that the local instance will have a 042 * state of "completed" and a form or notes that applies. 043 * 044 * @author Gabriel Guardincerri 045 * @author Florian Schmaus 046 * 047 */ 048public class AdHocCommand extends AbstractAdHocCommand { 049 050 /** 051 * The connection that is used to execute this command 052 */ 053 private final XMPPConnection connection; 054 055 /** 056 * The full JID of the command host 057 */ 058 private final Jid jid; 059 060 /** 061 * Creates a new RemoteCommand that uses an specific connection to execute a 062 * command identified by <code>node</code> in the host identified by 063 * <code>jid</code> 064 * 065 * @param connection the connection to use for the execution. 066 * @param node the identifier of the command. 067 * @param jid the JID of the host. 068 */ 069 protected AdHocCommand(XMPPConnection connection, String node, Jid jid) { 070 super(node); 071 this.connection = Objects.requireNonNull(connection); 072 this.jid = Objects.requireNonNull(jid); 073 } 074 075 public Jid getOwnerJID() { 076 return jid; 077 } 078 079 @Override 080 public final void cancel() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 081 executeAction(AdHocCommandData.Action.cancel); 082 } 083 084 /** 085 * Executes the command. This is invoked only on the first stage of the 086 * command. It is invoked on every command. If there is a problem executing 087 * the command it throws an XMPPException. 088 * 089 * @return an ad-hoc command result. 090 * @throws NoResponseException if there was no response from the remote entity. 091 * @throws XMPPErrorException if there is an error executing the command. 092 * @throws NotConnectedException if the XMPP connection is not connected. 093 * @throws InterruptedException if the calling thread was interrupted. 094 */ 095 public final AdHocCommandResult execute() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 096 return executeAction(AdHocCommandData.Action.execute); 097 } 098 099 /** 100 * Executes the next action of the command with the information provided in 101 * the <code>response</code>. This form must be the answer form of the 102 * previous stage. This method will be only invoked for commands that have one 103 * or more stages. If there is a problem executing the command it throws an 104 * XMPPException. 105 * 106 * @param filledForm the form answer of the previous stage. 107 * @return an ad-hoc command result. 108 * @throws NoResponseException if there was no response from the remote entity. 109 * @throws XMPPErrorException if there is a problem executing the command. 110 * @throws NotConnectedException if the XMPP connection is not connected. 111 * @throws InterruptedException if the calling thread was interrupted. 112 */ 113 public final AdHocCommandResult next(SubmitForm filledForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 114 return executeAction(AdHocCommandData.Action.next, filledForm.getDataForm()); 115 } 116 117 /** 118 * Completes the command execution with the information provided in the 119 * <code>response</code>. This form must be the answer form of the 120 * previous stage. This method will be only invoked for commands that have one 121 * or more stages. If there is a problem executing the command it throws an 122 * XMPPException. 123 * 124 * @param filledForm the form answer of the previous stage. 125 * @return an ad-hoc command result. 126 * @throws NoResponseException if there was no response from the remote entity. 127 * @throws XMPPErrorException if there is a problem executing the command. 128 * @throws NotConnectedException if the XMPP connection is not connected. 129 * @throws InterruptedException if the calling thread was interrupted. 130 */ 131 public AdHocCommandResult complete(SubmitForm filledForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 132 return executeAction(AdHocCommandData.Action.complete, filledForm.getDataForm()); 133 } 134 135 /** 136 * Goes to the previous stage. The requester is asking to re-send the 137 * information of the previous stage. The command must change it state to 138 * the previous one. If there is a problem executing the command it throws 139 * an XMPPException. 140 * 141 * @return an ad-hoc command result. 142 * @throws NoResponseException if there was no response from the remote entity. 143 * @throws XMPPErrorException if there is a problem executing the command. 144 * @throws NotConnectedException if the XMPP connection is not connected. 145 * @throws InterruptedException if the calling thread was interrupted. 146 */ 147 public final AdHocCommandResult prev() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 148 return executeAction(AdHocCommandData.Action.prev); 149 } 150 151 /** 152 * Executes the default action of the command with the information provided 153 * in the Form. This form must be the answer form of the previous stage. If 154 * there is a problem executing the command it throws an XMPPException. 155 * 156 * @param form the form answer of the previous stage. 157 * @return an ad-hoc command result. 158 * @throws XMPPErrorException if an error occurs. 159 * @throws NoResponseException if there was no response from the server. 160 * @throws NotConnectedException if the XMPP connection is not connected. 161 * @throws InterruptedException if the calling thread was interrupted. 162 */ 163 public final AdHocCommandResult execute(FillableForm form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 164 return executeAction(AdHocCommandData.Action.execute, form.getDataFormToSubmit()); 165 } 166 167 private AdHocCommandResult executeAction(AdHocCommandData.Action action) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 168 return executeAction(action, null); 169 } 170 171 /** 172 * Executes the <code>action</code> with the <code>form</code>. 173 * The action could be any of the available actions. The form must 174 * be the answer of the previous stage. It can be <code>null</code> if it is the first stage. 175 * 176 * @param action the action to execute. 177 * @param form the form with the information. 178 * @throws XMPPErrorException if there is a problem executing the command. 179 * @throws NoResponseException if there was no response from the server. 180 * @throws NotConnectedException if the XMPP connection is not connected. 181 * @throws InterruptedException if the calling thread was interrupted. 182 */ 183 private synchronized AdHocCommandResult executeAction(AdHocCommandData.Action action, DataForm form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 184 AdHocCommandData request = AdHocCommandData.builder(getNode(), connection) 185 .ofType(IQ.Type.set) 186 .to(getOwnerJID()) 187 .setSessionId(getSessionId()) 188 .setAction(action) 189 .setForm(form) 190 .build(); 191 192 addRequest(request); 193 194 AdHocCommandData response = connection.sendIqRequestAndWaitForResponse(request); 195 196 // The Ad-Hoc service ("server") may have generated a session id for us. 197 String sessionId = response.getSessionId(); 198 if (sessionId != null) { 199 setSessionId(sessionId); 200 } 201 202 AdHocCommandResult result = AdHocCommandResult.from(response); 203 addResult(result); 204 return result; 205 } 206 207}