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