1616
1717package io .appium .java_client .remote ;
1818
19+ import static com .google .common .base .Preconditions .checkNotNull ;
20+ import static com .google .common .base .Throwables .getRootCause ;
21+ import static com .google .common .base .Throwables .throwIfUnchecked ;
22+ import static org .openqa .selenium .remote .DriverCommand .GET_ALL_SESSIONS ;
23+ import static org .openqa .selenium .remote .DriverCommand .NEW_SESSION ;
24+ import static org .openqa .selenium .remote .DriverCommand .QUIT ;
1925
20- import com .google .common .base .Throwables ;
21-
26+ import io .appium .java_client .AppiumCommandInfo ;
27+ import org .openqa .selenium .NoSuchSessionException ;
28+ import org .openqa .selenium .SessionNotCreatedException ;
29+ import org .openqa .selenium .UnsupportedCommandException ;
2230import org .openqa .selenium .WebDriverException ;
2331import org .openqa .selenium .remote .Command ;
24- import org .openqa .selenium .remote .CommandInfo ;
32+ import org .openqa .selenium .remote .CommandCodec ;
33+ import org .openqa .selenium .remote .CommandExecutor ;
34+ import org .openqa .selenium .remote .Dialect ;
2535import org .openqa .selenium .remote .DriverCommand ;
26- import org .openqa .selenium .remote .HttpCommandExecutor ;
36+ import org .openqa .selenium .remote .HttpSessionId ;
2737import org .openqa .selenium .remote .Response ;
38+ import org .openqa .selenium .remote .ResponseCodec ;
2839import org .openqa .selenium .remote .http .HttpClient ;
40+ import org .openqa .selenium .remote .http .HttpRequest ;
41+ import org .openqa .selenium .remote .http .HttpResponse ;
2942import org .openqa .selenium .remote .internal .ApacheHttpClient ;
3043import org .openqa .selenium .remote .service .DriverService ;
3144
3447import java .net .URL ;
3548import java .util .Map ;
3649
37- public class AppiumCommandExecutor extends HttpCommandExecutor {
50+ public class AppiumCommandExecutor implements CommandExecutor {
3851
39- private final DriverService service ;
52+ private final URL remoteServer ;
53+ private final HttpClient client ;
54+ private final Map <String , AppiumCommandInfo > additionalCommands ;
55+ private CommandCodec <HttpRequest > commandCodec ;
56+ private ResponseCodec <HttpResponse > responseCodec ;
57+ private DriverService service ;
4058
41- public AppiumCommandExecutor (Map <String , CommandInfo > additionalCommands ,
59+ /**
60+ * Cretes an instance that sends requests and receives responses.
61+ *
62+ * @param additionalCommands is the mapped command repository
63+ * @param addressOfRemoteServer is the url to connect to the Appium remote/local server
64+ * @param httpClientFactory is the http client factory
65+ */
66+ public AppiumCommandExecutor (Map <String , AppiumCommandInfo > additionalCommands ,
4267 URL addressOfRemoteServer , HttpClient .Factory httpClientFactory ) {
43- super (additionalCommands , addressOfRemoteServer , httpClientFactory );
44- service = null ;
68+ checkNotNull (addressOfRemoteServer );
69+ remoteServer = addressOfRemoteServer ;
70+ this .additionalCommands = additionalCommands ;
71+ this .client = httpClientFactory .createClient (remoteServer );
4572 }
4673
47- public AppiumCommandExecutor (Map <String , CommandInfo > additionalCommands , DriverService service ,
74+ public AppiumCommandExecutor (Map <String , AppiumCommandInfo > additionalCommands , DriverService service ,
4875 HttpClient .Factory httpClientFactory ) {
49- super (additionalCommands , service .getUrl (), httpClientFactory );
76+ this (additionalCommands , service .getUrl (), httpClientFactory );
5077 this .service = service ;
5178 }
5279
53- public AppiumCommandExecutor (Map <String , CommandInfo > additionalCommands ,
80+ public AppiumCommandExecutor (Map <String , AppiumCommandInfo > additionalCommands ,
5481 URL addressOfRemoteServer ) {
5582 this (additionalCommands , addressOfRemoteServer , new ApacheHttpClient .Factory ());
5683 }
5784
58- public AppiumCommandExecutor (Map <String , CommandInfo > additionalCommands ,
85+ public AppiumCommandExecutor (Map <String , AppiumCommandInfo > additionalCommands ,
5986 DriverService service ) {
6087 this (additionalCommands , service , new ApacheHttpClient .Factory ());
6188 }
6289
90+ public URL getAddressOfRemoteServer () {
91+ return remoteServer ;
92+ }
93+
94+ private Response doExecute (Command command ) throws IOException , WebDriverException {
95+ if (command .getSessionId () == null ) {
96+ if (QUIT .equals (command .getName ())) {
97+ return new Response ();
98+ }
99+ if (!GET_ALL_SESSIONS .equals (command .getName ())
100+ && !NEW_SESSION .equals (command .getName ())) {
101+ throw new NoSuchSessionException (
102+ "Session ID is null. Using WebDriver after calling quit()?" );
103+ }
104+ }
105+
106+ if (NEW_SESSION .equals (command .getName ())) {
107+ if (commandCodec != null ) {
108+ throw new SessionNotCreatedException ("Session already exists" );
109+ }
110+ AppiumProtocolHandShake handshake = new AppiumProtocolHandShake ();
111+ AppiumProtocolHandShake .Result result = handshake .createSession (client , command );
112+ Dialect dialect = result .getDialect ();
113+ commandCodec = dialect .getCommandCodec ();
114+
115+ additionalCommands .forEach ((key , value ) -> {
116+ checkNotNull (key );
117+ checkNotNull (value );
118+ commandCodec .defineCommand (key , value .getMethod (), value .getUrl ());
119+ } );
120+
121+ responseCodec = dialect .getResponseCodec ();
122+ return result .createResponse ();
123+ }
124+
125+ if (commandCodec == null || responseCodec == null ) {
126+ throw new WebDriverException (
127+ "No command or response codec has been defined. Unable to proceed" );
128+ }
129+
130+ HttpRequest httpRequest = commandCodec .encode (command );
131+ try {
132+ HttpResponse httpResponse = client .execute (httpRequest , true );
133+
134+ Response response = responseCodec .decode (httpResponse );
135+ if (response .getSessionId () == null ) {
136+ if (httpResponse .getTargetHost () != null ) {
137+ response .setSessionId (HttpSessionId .getSessionId (httpResponse .getTargetHost ()));
138+ } else {
139+ response .setSessionId (command .getSessionId ().toString ());
140+ }
141+ }
142+ if (QUIT .equals (command .getName ())) {
143+ client .close ();
144+ }
145+ return response ;
146+ } catch (UnsupportedCommandException e ) {
147+ if (e .getMessage () == null || "" .equals (e .getMessage ())) {
148+ throw new UnsupportedOperationException (
149+ "No information from server. Command name was: " + command .getName (),
150+ e .getCause ());
151+ }
152+ throw e ;
153+ }
154+ }
155+
63156 @ Override public Response execute (Command command ) throws IOException , WebDriverException {
64157 if (DriverCommand .NEW_SESSION .equals (command .getName ()) && service != null ) {
65158 service .start ();
66159 }
67160
68161 try {
69- return super . execute (command );
162+ return doExecute (command );
70163 } catch (Throwable t ) {
71- Throwable rootCause = Throwables . getRootCause (t );
164+ Throwable rootCause = getRootCause (t );
72165 if (rootCause instanceof ConnectException
73166 && rootCause .getMessage ().contains ("Connection refused" )
74167 && service != null ) {
@@ -80,7 +173,7 @@ public AppiumCommandExecutor(Map<String, CommandInfo> additionalCommands,
80173 throw new WebDriverException ("The appium server has accidentally died!" , t );
81174 }
82175 }
83- Throwables . propagateIfPossible (t );
176+ throwIfUnchecked (t );
84177 throw new WebDriverException (t );
85178 } finally {
86179 if (DriverCommand .QUIT .equals (command .getName ()) && service != null ) {
0 commit comments