Skip to content

Commit 4ee6b0a

Browse files
#875 FIX
1 parent d9155a4 commit 4ee6b0a

File tree

3 files changed

+152
-34
lines changed

3 files changed

+152
-34
lines changed

src/main/java/io/appium/java_client/remote/AppiumCommandExecutor.java

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,44 @@
1818

1919
import static com.google.common.base.Preconditions.checkNotNull;
2020
import static com.google.common.base.Throwables.throwIfUnchecked;
21+
import static java.lang.String.format;
22+
import static java.nio.charset.StandardCharsets.UTF_8;
2123
import static java.util.Optional.ofNullable;
24+
import static org.openqa.selenium.remote.DriverCommand.NEW_SESSION;
2225

2326
import com.google.common.base.Supplier;
2427
import com.google.common.base.Throwables;
2528

29+
import com.google.common.io.CountingOutputStream;
30+
import com.google.common.io.FileBackedOutputStream;
31+
32+
import org.openqa.selenium.Capabilities;
33+
import org.openqa.selenium.ImmutableCapabilities;
34+
import org.openqa.selenium.SessionNotCreatedException;
2635
import org.openqa.selenium.WebDriverException;
2736
import org.openqa.selenium.remote.Command;
2837
import org.openqa.selenium.remote.CommandCodec;
2938
import org.openqa.selenium.remote.CommandInfo;
39+
import org.openqa.selenium.remote.Dialect;
3040
import org.openqa.selenium.remote.DriverCommand;
3141
import org.openqa.selenium.remote.HttpCommandExecutor;
42+
import org.openqa.selenium.remote.ProtocolHandshake;
3243
import org.openqa.selenium.remote.Response;
44+
import org.openqa.selenium.remote.ResponseCodec;
3345
import org.openqa.selenium.remote.http.HttpClient;
3446
import org.openqa.selenium.remote.http.HttpRequest;
47+
import org.openqa.selenium.remote.http.HttpResponse;
3548
import org.openqa.selenium.remote.http.W3CHttpCommandCodec;
3649
import org.openqa.selenium.remote.service.DriverService;
3750

51+
import java.io.BufferedInputStream;
3852
import java.io.IOException;
53+
import java.io.InputStream;
54+
import java.io.OutputStreamWriter;
55+
import java.io.Writer;
3956
import java.lang.reflect.Field;
57+
import java.lang.reflect.InvocationTargetException;
58+
import java.lang.reflect.Method;
4059
import java.net.ConnectException;
4160
import java.net.URL;
4261
import java.util.Map;
@@ -111,8 +130,83 @@ private void setCommandCodec(CommandCodec<HttpRequest> newCodec) {
111130
setPrivateFieldValue("commandCodec", newCodec);
112131
}
113132

133+
private void setResponseCodec(ResponseCodec<HttpResponse> codec) {
134+
setPrivateFieldValue("responseCodec", codec);
135+
}
136+
137+
private HttpClient getClient() {
138+
//noinspection unchecked
139+
return getPrivateFieldValue("client", HttpClient.class);
140+
}
141+
142+
private Response createSession(Command command) throws IOException {
143+
if (getCommandCodec() != null) {
144+
throw new SessionNotCreatedException("Session already exists");
145+
}
146+
ProtocolHandshake handshake = new ProtocolHandshake() {
147+
@SuppressWarnings("unchecked")
148+
public Result createSession(HttpClient client, Command command)
149+
throws IOException {
150+
Capabilities desired = (Capabilities) command.getParameters().get("desiredCapabilities");
151+
desired = desired == null ? new ImmutableCapabilities() : desired;
152+
153+
int threshold = (int) Math.min(Runtime.getRuntime().freeMemory() / 10, Integer.MAX_VALUE);
154+
FileBackedOutputStream os = new FileBackedOutputStream(threshold);
155+
try (
156+
CountingOutputStream counter = new CountingOutputStream(os);
157+
Writer writer = new OutputStreamWriter(counter, UTF_8);
158+
NewAppiumSessionPayload payload = NewAppiumSessionPayload.create(desired)) {
159+
160+
payload.writeTo(writer);
161+
162+
try (InputStream rawIn = os.asByteSource().openBufferedStream();
163+
BufferedInputStream contentStream = new BufferedInputStream(rawIn)) {
164+
165+
Method createSessionMethod = this.getClass().getSuperclass()
166+
.getDeclaredMethod("createSession", HttpClient.class, InputStream.class, long.class);
167+
createSessionMethod.setAccessible(true);
168+
169+
Optional<Result> result = (Optional<Result>) createSessionMethod
170+
.invoke(this, client, contentStream, counter.getCount());
171+
172+
if (result.isPresent()) {
173+
Result toReturn = result.get();
174+
System.out.print(format("Detected dialect: %s", toReturn.getDialect()));
175+
return toReturn;
176+
}
177+
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
178+
throw new WebDriverException(format("It is impossible to create a new session "
179+
+ "because 'createSession' which takes %s, %s and %s was not found "
180+
+ "or it is not accessible",
181+
HttpClient.class.getSimpleName(),
182+
InputStream.class.getSimpleName(),
183+
long.class.getSimpleName()), e);
184+
}
185+
} finally {
186+
os.reset();
187+
}
188+
189+
throw new SessionNotCreatedException(
190+
format(
191+
"Unable to create new remote session. "
192+
+ "desired capabilities = %s",
193+
desired));
194+
}
195+
};
196+
197+
ProtocolHandshake.Result result = handshake
198+
.createSession(getClient(), command);
199+
Dialect dialect = result.getDialect();
200+
setCommandCodec(dialect.getCommandCodec());
201+
for (Map.Entry<String, CommandInfo> entry : getAdditionalCommands().entrySet()) {
202+
defineCommand(entry.getKey(), entry.getValue());
203+
}
204+
setResponseCodec(dialect.getResponseCodec());
205+
return result.createResponse();
206+
}
207+
114208
@Override
115-
public Response execute(Command command) throws WebDriverException {
209+
public Response execute(Command command) throws WebDriverException, IOException {
116210
if (DriverCommand.NEW_SESSION.equals(command.getName())) {
117211
serviceOptional.ifPresent(driverService -> {
118212
try {
@@ -125,7 +219,12 @@ public Response execute(Command command) throws WebDriverException {
125219

126220
Response response;
127221
try {
128-
response = super.execute(command);
222+
if (!NEW_SESSION.equals(command.getName())) {
223+
response = super.execute(command);
224+
}
225+
else {
226+
response = createSession(command);
227+
}
129228
} catch (Throwable t) {
130229
Throwable rootCause = Throwables.getRootCause(t);
131230
if (rootCause instanceof ConnectException

src/main/java/org/openqa/selenium/remote/NewSessionPayload.java renamed to src/main/java/io/appium/java_client/remote/NewAppiumSessionPayload.java

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// specific language governing permissions and limitations
1616
// under the License.writeTo
1717

18-
package org.openqa.selenium.remote;
18+
package io.appium.java_client.remote;
1919

2020
import static com.google.common.collect.ImmutableMap.of;
2121
import static com.google.common.collect.ImmutableMap.toImmutableMap;
@@ -38,15 +38,12 @@
3838
import com.google.common.io.CharStreams;
3939
import com.google.common.io.FileBackedOutputStream;
4040

41-
import io.appium.java_client.remote.AndroidMobileCapabilityType;
42-
import io.appium.java_client.remote.IOSMobileCapabilityType;
43-
import io.appium.java_client.remote.MobileCapabilityType;
44-
import io.appium.java_client.remote.YouiEngineCapabilityType;
4541
import org.openqa.selenium.Capabilities;
4642
import org.openqa.selenium.ImmutableCapabilities;
4743
import org.openqa.selenium.json.Json;
4844
import org.openqa.selenium.json.JsonInput;
4945
import org.openqa.selenium.json.JsonOutput;
46+
import org.openqa.selenium.remote.Dialect;
5047
import org.openqa.selenium.remote.session.CapabilitiesFilter;
5148
import org.openqa.selenium.remote.session.CapabilityTransform;
5249
import org.openqa.selenium.remote.session.ChromeFilter;
@@ -59,22 +56,33 @@
5956
import org.openqa.selenium.remote.session.StripAnyPlatform;
6057
import org.openqa.selenium.remote.session.W3CPlatformNameNormaliser;
6158

62-
import javax.annotation.Nullable;
6359
import java.io.Closeable;
6460
import java.io.IOException;
6561
import java.io.OutputStreamWriter;
6662
import java.io.Reader;
6763
import java.io.StringReader;
6864
import java.io.Writer;
69-
import java.util.*;
65+
import java.util.Arrays;
66+
import java.util.Collection;
67+
import java.util.HashMap;
68+
import java.util.HashSet;
69+
import java.util.LinkedList;
70+
import java.util.List;
71+
import java.util.Map;
72+
import java.util.Objects;
73+
import java.util.Queue;
74+
import java.util.ServiceLoader;
75+
import java.util.Set;
76+
import java.util.TreeMap;
7077
import java.util.function.Function;
7178
import java.util.function.Predicate;
7279
import java.util.regex.Pattern;
7380
import java.util.stream.Collectors;
7481
import java.util.stream.Stream;
82+
import javax.annotation.Nullable;
7583

7684

77-
public class NewSessionPayload implements Closeable {
85+
class NewAppiumSessionPayload implements Closeable {
7886

7987
private static final List<String> APPIUM_CAPABILITIES = ImmutableList.<String>builder()
8088
.addAll(getAppiumCapabilities(MobileCapabilityType.class))
@@ -88,11 +96,7 @@ public class NewSessionPayload implements Closeable {
8896
private static final String FIRST_MATCH = "firstMatch";
8997
private static final String ALWAYS_MATCH = "alwaysMatch";
9098

91-
private final Set<CapabilitiesFilter> adapters;
92-
private final Set<CapabilityTransform> transforms;
93-
private final boolean forceMobileJSONWP;
94-
95-
private final static Predicate<String> ACCEPTED_W3C_PATTERNS = Stream.of(
99+
private static final Predicate<String> ACCEPTED_W3C_PATTERNS = Stream.of(
96100
"^[\\w-]+:.*$",
97101
"^acceptInsecureCerts$",
98102
"^browserName$",
@@ -107,12 +111,16 @@ public class NewSessionPayload implements Closeable {
107111
.map(Pattern::asPredicate)
108112
.reduce(identity -> false, Predicate::or);
109113

114+
private final Set<CapabilitiesFilter> adapters;
115+
private final Set<CapabilityTransform> transforms;
116+
private final boolean forceMobileJSONWP;
117+
110118
private final Json json = new Json();
111119
private final FileBackedOutputStream backingStore;
112120

113121
private static List<String> getAppiumCapabilities(Class<?> capabilityList) {
114122
return Arrays.stream(capabilityList.getDeclaredFields()).map(field -> {
115-
field.setAccessible(true);
123+
field.setAccessible(true);
116124
try {
117125
return field.get(capabilityList).toString();
118126
} catch (IllegalAccessException e) {
@@ -121,7 +129,7 @@ private static List<String> getAppiumCapabilities(Class<?> capabilityList) {
121129
}).filter(s -> !FORCE_MJSONWP.equals(s)).collect(toList());
122130
}
123131

124-
public static NewSessionPayload create(Capabilities caps) throws IOException {
132+
public static NewAppiumSessionPayload create(Capabilities caps) throws IOException {
125133
boolean forceMobileJSONWP =
126134
ofNullable(caps.getCapability(FORCE_MJSONWP))
127135
.map(o -> Boolean.class.isAssignableFrom(o.getClass()) && Boolean.class.cast(o))
@@ -131,10 +139,10 @@ public static NewSessionPayload create(Capabilities caps) throws IOException {
131139
capabilityMap.remove(FORCE_MJSONWP);
132140
Map<String, ?> source = of(DESIRED_CAPABILITIES, capabilityMap);
133141
String json = new Json().toJson(source);
134-
return new NewSessionPayload(new StringReader(json), forceMobileJSONWP);
142+
return new NewAppiumSessionPayload(new StringReader(json), forceMobileJSONWP);
135143
}
136144

137-
private NewSessionPayload(Reader source, boolean forceMobileJSONWP) throws IOException {
145+
private NewAppiumSessionPayload(Reader source, boolean forceMobileJSONWP) throws IOException {
138146
this.forceMobileJSONWP = forceMobileJSONWP;
139147
// Dedicate up to 10% of all RAM or 20% of available RAM (whichever is smaller) to storing this
140148
// payload.
@@ -244,22 +252,22 @@ public void writeTo(Appendable appendable) throws IOException {
244252

245253
// Write the first capability we get as the desired capability.
246254
json.name(DESIRED_CAPABILITIES);
247-
json.write(first, MAP_TYPE);
255+
json.write(first);
248256

249257
// And write the first capability for gecko13
250258
json.name(CAPABILITIES);
251259
json.beginObject();
252260

253261
json.name(DESIRED_CAPABILITIES);
254-
json.write(first, MAP_TYPE);
262+
json.write(first);
255263

256264
// Then write everything into the w3c payload. Because of the way we do this, it's easiest
257265
// to just populate the "firstMatch" section. The spec says it's fine to omit the
258266
// "alwaysMatch" field, so we do this.
259267
json.name(FIRST_MATCH);
260268
json.beginArray();
261269
//noinspection unchecked
262-
getW3C().forEach(map -> json.write(map, MAP_TYPE));
270+
getW3C().forEach(json::write);
263271
json.endArray();
264272

265273
json.endObject(); // Close "capabilities" object
@@ -286,7 +294,7 @@ private void writeMetaData(JsonOutput out) throws IOException {
286294

287295
default:
288296
out.name(name);
289-
out.write(input.<Object>read(Object.class), Object.class);
297+
out.write(input.<Object>read(Object.class));
290298
break;
291299
}
292300
}
@@ -297,11 +305,9 @@ private void writeMetaData(JsonOutput out) throws IOException {
297305
* Stream the {@link Capabilities} encoded in the payload used to create this instance. The
298306
* {@link Stream} will start with a {@link Capabilities} object matching the OSS capabilities, and
299307
* will then expand each of the "{@code firstMatch}" and "{@code alwaysMatch}" contents as defined
300-
* in the W3C WebDriver spec.
301-
* <p>
302-
* The OSS {@link Capabilities} are listed first because converting the OSS capabilities to the
303-
* equivalent W3C capabilities isn't particularly easy, so it's hoped that this approach gives us
304-
* the most compatible implementation.
308+
* in the W3C WebDriver spec. The OSS {@link Capabilities} are listed first because converting the
309+
* OSS capabilities to the equivalent W3C capabilities isn't particularly easy, so it's hoped that
310+
* this approach gives us the most compatible implementation.
305311
*/
306312
public Stream<Capabilities> stream() throws IOException {
307313
// OSS first
@@ -399,9 +405,10 @@ public Object getValue() {
399405
@Override
400406
public Object setValue(Object value) {
401407
return stringObjectEntry.setValue(value);
402-
}})
408+
}
409+
})
403410
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)))
404-
.map(map -> (Map<String, Object>) map);
411+
.map(map -> map);
405412
} else {
406413
fromOss = Stream.of();
407414
}

src/test/java/io/appium/java_client/pagefactory_tests/DesktopBrowserCompatibilityTest.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818

1919
import static io.appium.java_client.pagefactory.LocatorGroupStrategy.ALL_POSSIBLE;
2020
import static io.github.bonigarcia.wdm.WebDriverManager.chromedriver;
21+
import static io.github.bonigarcia.wdm.WebDriverManager.firefoxdriver;
2122
import static java.time.Duration.ofSeconds;
2223
import static org.junit.Assert.assertEquals;
2324
import static org.junit.Assert.assertNotEquals;
25+
import static org.openqa.selenium.firefox.FirefoxDriver.PROFILE;
2426

2527
import io.appium.java_client.android.AndroidDriver;
2628
import io.appium.java_client.pagefactory.AndroidFindBy;
@@ -32,6 +34,10 @@
3234
import org.openqa.selenium.WebDriver;
3335
import org.openqa.selenium.WebElement;
3436
import org.openqa.selenium.chrome.ChromeDriver;
37+
import org.openqa.selenium.firefox.FirefoxDriver;
38+
import org.openqa.selenium.firefox.FirefoxDriverLogLevel;
39+
import org.openqa.selenium.firefox.FirefoxOptions;
40+
import org.openqa.selenium.firefox.FirefoxProfile;
3541
import org.openqa.selenium.support.FindBy;
3642
import org.openqa.selenium.support.FindBys;
3743
import org.openqa.selenium.support.PageFactory;
@@ -54,11 +60,17 @@ public class DesktopBrowserCompatibilityTest {
5460
* The starting.
5561
*/
5662
@BeforeClass public static void beforeClass() {
57-
chromedriver().setup();
63+
firefoxdriver().setup();
5864
}
5965

60-
@Test public void chromeTest() {
61-
WebDriver driver = new ChromeDriver();
66+
@Test public void fireFoxTest() {
67+
FirefoxOptions firefoxOptions = new FirefoxOptions();
68+
firefoxOptions.setLogLevel(FirefoxDriverLogLevel.TRACE);
69+
70+
FirefoxProfile firefoxProfile = new FirefoxProfile();
71+
firefoxOptions.setCapability(PROFILE, firefoxProfile);
72+
73+
WebDriver driver = new FirefoxDriver(firefoxOptions);
6274
try {
6375
PageFactory
6476
.initElements(new AppiumFieldDecorator(driver, ofSeconds(15)),
@@ -73,4 +85,4 @@ public class DesktopBrowserCompatibilityTest {
7385
driver.quit();
7486
}
7587
}
76-
}
88+
}

0 commit comments

Comments
 (0)