11package com .github .dockerjava .netty ;
22
3+ import java .io .IOException ;
4+ import java .io .UnsupportedEncodingException ;
5+ import java .net .URLEncoder ;
6+ import java .nio .charset .Charset ;
7+ import java .nio .charset .UnsupportedCharsetException ;
38import java .util .ArrayList ;
49import java .util .Arrays ;
510import java .util .List ;
611import java .util .Map ;
12+ import java .util .Set ;
713
14+ import com .fasterxml .jackson .databind .ObjectMapper ;
15+ import com .google .common .collect .ImmutableSet ;
16+ import io .netty .handler .codec .http .HttpConstants ;
817import org .apache .commons .lang .StringUtils ;
918
1019import com .google .common .collect .ImmutableList ;
1120import com .google .common .collect .ImmutableMap ;
1221
1322/**
14- * This class is basically a replacement of javax.ws.rs.client.WebTarget to allow simpler migration of JAX-RS code to a netty based
23+ * This class is basically a replacement of {@link javax.ws.rs.client.WebTarget} to allow simpler migration of JAX-RS code to a netty based
1524 * implementation.
1625 *
1726 * @author Marcus Linke
1827 */
1928public class WebTarget {
29+ private static final ObjectMapper MAPPER = new ObjectMapper ();
2030
2131 private final ChannelProvider channelProvider ;
2232
2333 private final ImmutableList <String > path ;
2434
2535 private final ImmutableMap <String , String > queryParams ;
2636
37+ /**
38+ * Multiple values for the same name param.
39+ */
40+ private final ImmutableMap <String , Set <String >> queryParamsSet ;
41+
2742 private static final String PATH_SEPARATOR = "/" ;
2843
2944 public WebTarget (ChannelProvider channelProvider ) {
30- this (channelProvider , ImmutableList .<String >of (), ImmutableMap .<String , String >of ());
45+ this (channelProvider , ImmutableList .<String >of (), ImmutableMap .<String , String >of (),
46+ ImmutableMap .<String , Set <String >>of ());
3147 }
3248
3349 private WebTarget (ChannelProvider channelProvider ,
3450 ImmutableList <String > path ,
35- ImmutableMap <String , String > queryParams ) {
51+ ImmutableMap <String , String > queryParams ,
52+ ImmutableMap <String , Set <String >> queryParamsSet ) {
3653 this .channelProvider = channelProvider ;
3754 this .path = path ;
3855 this .queryParams = queryParams ;
56+ this .queryParamsSet = queryParamsSet ;
3957 }
4058
4159 public WebTarget path (String ... components ) {
@@ -45,15 +63,21 @@ public WebTarget path(String... components) {
4563 newPath .addAll (Arrays .asList (StringUtils .split (component , PATH_SEPARATOR )));
4664 }
4765
48- return new WebTarget (channelProvider , newPath .build (), queryParams );
66+ return new WebTarget (channelProvider , newPath .build (), queryParams , queryParamsSet );
4967 }
5068
5169 public InvocationBuilder request () {
5270 String resource = PATH_SEPARATOR + StringUtils .join (path , PATH_SEPARATOR );
5371
5472 List <String > params = new ArrayList <>();
5573 for (Map .Entry <String , String > entry : queryParams .entrySet ()) {
56- params .add (entry .getKey () + "=" + entry .getValue ());
74+ params .add (entry .getKey () + "=" + encodeComponent (entry .getValue (), HttpConstants .DEFAULT_CHARSET ));
75+ }
76+
77+ for (Map .Entry <String , Set <String >> entry : queryParamsSet .entrySet ()) {
78+ for (String entryValueValue : entry .getValue ()) {
79+ params .add (entry .getKey () + "=" + encodeComponent (entryValueValue , HttpConstants .DEFAULT_CHARSET ));
80+ }
5781 }
5882
5983 if (!params .isEmpty ()) {
@@ -63,21 +87,58 @@ public InvocationBuilder request() {
6387 return new InvocationBuilder (channelProvider , resource );
6488 }
6589
90+ /**
91+ * @see io.netty.handler.codec.http.QueryStringEncoder
92+ */
93+ private static String encodeComponent (String s , Charset charset ) {
94+ // TODO: Optimize me.
95+ try {
96+ return URLEncoder .encode (s , charset .name ()).replace ("+" , "%20" );
97+ } catch (UnsupportedEncodingException ignored ) {
98+ throw new UnsupportedCharsetException (charset .name ());
99+ }
100+ }
101+
66102 public WebTarget resolveTemplate (String name , Object value ) {
67103 ImmutableList .Builder <String > newPath = ImmutableList .builder ();
68104 for (String component : path ) {
69105 component = component .replaceAll ("\\ {" + name + "\\ }" , value .toString ());
70106 newPath .add (component );
71107 }
72- return new WebTarget (channelProvider , newPath .build (), queryParams );
108+ return new WebTarget (channelProvider , newPath .build (), queryParams , queryParamsSet );
73109 }
74110
75111 public WebTarget queryParam (String name , Object value ) {
76112 ImmutableMap .Builder <String , String > builder = ImmutableMap .<String , String >builder ().putAll (queryParams );
77113 if (value != null ) {
78114 builder .put (name , value .toString ());
79115 }
80- return new WebTarget (channelProvider , path , builder .build ());
116+ return new WebTarget (channelProvider , path , builder .build (), queryParamsSet );
117+ }
118+
119+ public WebTarget queryParamsSet (String name , Set <?> values ) {
120+ ImmutableMap .Builder <String , Set <String >> builder = ImmutableMap .<String , Set <String >>builder ().putAll (queryParamsSet );
121+ if (values != null ) {
122+ ImmutableSet .Builder <String > valueBuilder = ImmutableSet .builder ();
123+ for (Object value : values ) {
124+ valueBuilder .add (value .toString ());
125+ }
126+ builder .put (name , valueBuilder .build ());
127+ }
128+ return new WebTarget (channelProvider , path , queryParams , builder .build ());
129+ }
130+
131+ public WebTarget queryParamsJsonMap (String name , Map <String , String > values ) {
132+ if (values != null && !values .isEmpty ()) {
133+ try {
134+ // when param value is JSON string
135+ return queryParam (name , MAPPER .writeValueAsString (values ));
136+ } catch (IOException e ) {
137+ throw new RuntimeException (e );
138+ }
139+ } else {
140+ return this ;
141+ }
81142 }
82143
83144 @ Override
@@ -97,14 +158,22 @@ public boolean equals(Object o) {
97158 if (path != null ? !path .equals (webTarget .path ) : webTarget .path != null ) {
98159 return false ;
99160 }
100- return queryParams != null ? queryParams .equals (webTarget .queryParams ) : webTarget .queryParams == null ;
161+ if (queryParams != null ? !queryParams .equals (webTarget .queryParams ) : webTarget .queryParams != null ) {
162+ return false ;
163+ }
164+ if (queryParamsSet != null ? !queryParamsSet .equals (webTarget .queryParamsSet ) : webTarget .queryParamsSet != null ) {
165+ return false ;
166+ }
167+
168+ return true ;
101169 }
102170
103171 @ Override
104172 public int hashCode () {
105173 int result = channelProvider != null ? channelProvider .hashCode () : 0 ;
106174 result = 31 * result + (path != null ? path .hashCode () : 0 );
107175 result = 31 * result + (queryParams != null ? queryParams .hashCode () : 0 );
176+ result = 31 * result + (queryParamsSet != null ? queryParamsSet .hashCode () : 0 );
108177 return result ;
109178 }
110179}
0 commit comments