4
4
import static java .nio .file .StandardOpenOption .CREATE ;
5
5
import static java .nio .file .StandardOpenOption .TRUNCATE_EXISTING ;
6
6
7
- import com .google .common .base .Strings ;
8
7
import com .google .common .collect .ImmutableList ;
9
8
import com .google .common .collect .ImmutableMap ;
10
9
import com .google .common .collect .ImmutableSet ;
14
13
import com .google .common .io .CharStreams ;
15
14
import com .google .gson .Gson ;
16
15
import com .google .gson .GsonBuilder ;
16
+ import com .google .gson .JsonParseException ;
17
+ import com .google .gson .TypeAdapter ;
18
+ import com .google .gson .TypeAdapterFactory ;
17
19
import com .google .gson .reflect .TypeToken ;
18
20
import com .google .gson .stream .JsonReader ;
21
+ import com .google .gson .stream .JsonToken ;
22
+ import com .google .gson .stream .JsonWriter ;
19
23
20
24
import org .openqa .selenium .Capabilities ;
21
25
import org .openqa .selenium .ImmutableCapabilities ;
22
26
import org .openqa .selenium .io .FileHandler ;
23
27
import org .openqa .selenium .remote .BeanToJsonConverter ;
24
28
import org .openqa .selenium .remote .Dialect ;
29
+ import org .openqa .selenium .remote .JsonToBeanConverter ;
25
30
26
31
import java .io .ByteArrayInputStream ;
27
32
import java .io .Closeable ;
@@ -73,7 +78,12 @@ public class NewSessionPayload implements Closeable {
73
78
// Dedicate up to 10% of max ram to holding the payload
74
79
private static final long THRESHOLD = Runtime .getRuntime ().maxMemory () / 10 ;
75
80
76
- private static final Gson GSON = new GsonBuilder ().setLenient ().serializeNulls ().create ();
81
+ private static final Gson GSON = new GsonBuilder ()
82
+ .registerTypeAdapterFactory (ListAdapter .FACTORY )
83
+ .registerTypeAdapterFactory (MapAdapter .FACTORY )
84
+ .setLenient ()
85
+ .serializeNulls ()
86
+ .create ();
77
87
private static final Type MAP_TYPE = new TypeToken <Map <String , Object >>(){}.getType ();
78
88
79
89
private final Path root ;
@@ -128,7 +138,9 @@ public NewSessionPayload(long size, Reader source) throws IOException {
128
138
sources = diskBackedSource (source );
129
139
} else {
130
140
this .root = null ;
131
- sources = memoryBackedSource (GSON .fromJson (source , MAP_TYPE ));
141
+ sources =
142
+ memoryBackedSource (
143
+ new JsonToBeanConverter ().convert (Map .class , CharStreams .toString (source )));
132
144
}
133
145
134
146
validate (sources );
@@ -200,7 +212,7 @@ private Sources rewrite(Sources sources) {
200
212
return sources ;
201
213
}
202
214
203
- LOG .info ( "Mismatched capabilities. Creating a synthetic w3c capability." );
215
+ LOG .info ("Mismatched capabilities. Creating a synthetic w3c capability." );
204
216
205
217
ImmutableList .Builder <Supplier <Map <String , Object >>> newFirstMatches = ImmutableList .builder ();
206
218
newFirstMatches .add (sources .getOss ());
@@ -221,7 +233,7 @@ private Sources rewrite(Sources sources) {
221
233
sources .getDialects ());
222
234
}
223
235
224
- private Sources memoryBackedSource (Map <String , ?> source ) {
236
+ private Sources memoryBackedSource (Map <? , ?> source ) {
225
237
LOG .fine ("Memory-based payload for: " + source );
226
238
227
239
Set <Dialect > dialects = new TreeSet <>();
@@ -387,8 +399,8 @@ public Stream<Capabilities> stream() throws IOException {
387
399
if (getDownstreamDialects ().contains (Dialect .W3C )) {
388
400
Map <String , Object > always = sources .getAlwaysMatch ().get ();
389
401
mapStream = sources .getFirstMatch ().stream ()
390
- .map (Supplier ::get )
391
- .map (m -> ImmutableMap .<String , Object >builder ().putAll (always ).putAll (m ).build ());
402
+ .map (Supplier ::get )
403
+ .map (m -> ImmutableMap .<String , Object >builder ().putAll (always ).putAll (m ).build ());
392
404
} else if (getDownstreamDialects ().contains (Dialect .OSS )) {
393
405
mapStream = Stream .of (sources .getOss ().get ());
394
406
} else {
@@ -401,7 +413,7 @@ public Stream<Capabilities> stream() throws IOException {
401
413
public ImmutableSet <Dialect > getDownstreamDialects () {
402
414
return sources .getDialects ().isEmpty () ?
403
415
ImmutableSet .of (DEFAULT_DIALECT ) :
404
- sources .getDialects ();
416
+ sources .getDialects ();
405
417
}
406
418
407
419
public Supplier <InputStream > getPayload () {
@@ -421,6 +433,7 @@ public void close() {
421
433
422
434
423
435
private static class Sources {
436
+
424
437
private final Supplier <InputStream > originalPayload ;
425
438
private final long payloadSizeInBytes ;
426
439
private final Supplier <Map <String , Object >> oss ;
@@ -467,4 +480,130 @@ public long getPayloadSize() {
467
480
return payloadSizeInBytes ;
468
481
}
469
482
}
483
+
484
+ private static Object readValue (JsonReader in , Gson gson ) throws IOException {
485
+ switch (in .peek ()) {
486
+ case BEGIN_ARRAY :
487
+ case BEGIN_OBJECT :
488
+ case BOOLEAN :
489
+ case NULL :
490
+ case STRING :
491
+ return gson .fromJson (in , Object .class );
492
+
493
+ case NUMBER :
494
+ String number = in .nextString ();
495
+ if (number .indexOf ('.' ) != -1 ) {
496
+ return Double .parseDouble (number );
497
+ }
498
+ return Long .parseLong (number );
499
+
500
+ default :
501
+ throw new JsonParseException ("Unexpected type: " + in .peek ());
502
+ }
503
+ }
504
+
505
+ private static class MapAdapter extends TypeAdapter <Map <?, ?>> {
506
+
507
+ private final static TypeAdapterFactory FACTORY = new TypeAdapterFactory () {
508
+ @ SuppressWarnings ("unchecked" )
509
+ @ Override
510
+ public <T > TypeAdapter <T > create (Gson gson , TypeToken <T > type ) {
511
+ if (type .getRawType () == Map .class ) {
512
+ return (TypeAdapter <T >) new MapAdapter (gson );
513
+ }
514
+ return null ;
515
+ }
516
+ };
517
+
518
+ private final Gson gson ;
519
+
520
+ private MapAdapter (Gson gson ) {
521
+ this .gson = Objects .requireNonNull (gson );
522
+ }
523
+
524
+ @ Override
525
+ public Map <?, ?> read (JsonReader in ) throws IOException {
526
+ if (in .peek () == JsonToken .NULL ) {
527
+ in .nextNull ();
528
+ return null ;
529
+ }
530
+
531
+ Map <String , Object > map = new TreeMap <>();
532
+ in .beginObject ();
533
+
534
+ while (in .hasNext ()) {
535
+ String key = in .nextName ();
536
+ Object value = readValue (in , gson );
537
+
538
+ map .put (key , value );
539
+ }
540
+
541
+ in .endObject ();
542
+ return map ;
543
+ }
544
+
545
+ @ Override
546
+ public void write (JsonWriter out , Map <?, ?> value ) throws IOException {
547
+ // It's fine to use GSON's own default writer for this.
548
+ out .beginObject ();
549
+ for (Map .Entry <?, ?> entry : value .entrySet ()) {
550
+ out .name (String .valueOf (entry .getKey ()));
551
+ @ SuppressWarnings ("unchecked" )
552
+ TypeAdapter <Object >
553
+ adapter =
554
+ (TypeAdapter <Object >) gson .getAdapter (entry .getValue ().getClass ());
555
+ adapter .write (out , entry .getValue ());
556
+ }
557
+ out .endObject ();
558
+ }
559
+ }
560
+
561
+ private static class ListAdapter extends TypeAdapter <List <?>> {
562
+
563
+ private final static TypeAdapterFactory FACTORY = new TypeAdapterFactory () {
564
+ @ SuppressWarnings ("unchecked" )
565
+ @ Override
566
+ public <T > TypeAdapter <T > create (Gson gson , TypeToken <T > type ) {
567
+ if (type .getRawType () == List .class ) {
568
+ return (TypeAdapter <T >) new ListAdapter (gson );
569
+ }
570
+ return null ;
571
+ }
572
+ };
573
+
574
+ private final Gson gson ;
575
+
576
+ private ListAdapter (Gson gson ) {
577
+ this .gson = Objects .requireNonNull (gson );
578
+ }
579
+
580
+ @ Override
581
+ public List <?> read (JsonReader in ) throws IOException {
582
+ if (in .peek () == JsonToken .NULL ) {
583
+ in .nextNull ();
584
+ return null ;
585
+ }
586
+
587
+ List <Object > list = new LinkedList <>();
588
+ in .beginArray ();
589
+
590
+ while (in .hasNext ()) {
591
+ list .add (readValue (in , gson ));
592
+ }
593
+
594
+ in .endArray ();
595
+ return list ;
596
+ }
597
+
598
+ @ Override
599
+ public void write (JsonWriter out , List <?> value ) throws IOException {
600
+ out .beginArray ();
601
+ for (Object entry : value ) {
602
+ @ SuppressWarnings ("unchecked" )
603
+ TypeAdapter <Object > adapter = (TypeAdapter <Object >) gson .getAdapter (entry .getClass ());
604
+ adapter .write (out , entry );
605
+ }
606
+ out .endArray ();
607
+ }
608
+ }
470
609
}
0 commit comments