View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2014 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.caravan.io.http.request;
21  
22  import static com.google.common.base.Preconditions.checkNotNull;
23  
24  import java.nio.charset.Charset;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.commons.lang3.StringUtils;
32  import org.apache.http.client.methods.HttpGet;
33  import org.osgi.annotation.versioning.ProviderType;
34  
35  import com.damnhandy.uri.template.UriTemplate;
36  import com.google.common.base.Charsets;
37  import com.google.common.base.Strings;
38  import com.google.common.collect.ArrayListMultimap;
39  import com.google.common.collect.ImmutableList;
40  import com.google.common.collect.Lists;
41  import com.google.common.collect.Maps;
42  import com.google.common.collect.Multimap;
43  import com.google.common.collect.Sets;
44  
45  import io.wcm.caravan.io.http.impl.CaravanHttpHelper;
46  
47  /**
48   * UriTemplate using HTTP request builder.
49   */
50  @ProviderType
51  public final class CaravanHttpRequestBuilder {
52  
53    private final String serviceId;
54    private String method = HttpGet.METHOD_NAME;
55    private StringBuilder path = new StringBuilder();
56    private Set<String> queryNames = Sets.newHashSet();
57    private Map<String, Object> values = Maps.newHashMap();
58    private final Multimap<String, String> headers = ArrayListMultimap.create();
59    private Charset charset;
60    private byte[] body;
61    private String bodyTemplate;
62  
63    /**
64     * Default constructor.
65     */
66    public CaravanHttpRequestBuilder() {
67      serviceId = null;
68    }
69  
70    /**
71     * @param serviceId Logical service ID. Can be null.
72     */
73    public CaravanHttpRequestBuilder(String serviceId) {
74      this.serviceId = serviceId;
75    }
76  
77    /**
78     * @param correlationId Correlation Id. Can be null.
79     * @return Builder
80     */
81    public CaravanHttpRequestBuilder correlationId(String correlationId) {
82      if (correlationId != null) {
83        header(CaravanHttpRequest.CORRELATION_ID_HEADER_NAME, ImmutableList.of(correlationId));
84      }
85      return this;
86    }
87  
88    /**
89     * @see CaravanHttpRequest#getMethod()
90     * @param newMethod HTTP method
91     * @return Builder
92     */
93    public CaravanHttpRequestBuilder method(String newMethod) {
94      this.method = checkNotNull(newMethod, "method");
95      return this;
96    }
97  
98    /**
99     * Adds a header to the HTTP request
100    * @see CaravanHttpRequest#getHeaders()
101    * @param name Header name
102    * @param value Header value
103    * @return Builder
104    */
105   public CaravanHttpRequestBuilder header(String name, String value) {
106     headers.put(name, value);
107     return this;
108   }
109 
110   /**
111    * Sets and replaces a header for the HTTP request
112    * @see CaravanHttpRequest#getHeaders()
113    * @param name Header name
114    * @param headerValues Header values
115    * @return Builder
116    */
117   public CaravanHttpRequestBuilder header(String name, Collection<String> headerValues) {
118     headers.putAll(name, headerValues);
119     return this;
120   }
121 
122   /**
123    * Appends a URL fragment.
124    * @param urlFragment URL fragment
125    * @return Builder
126    */
127   public CaravanHttpRequestBuilder append(String urlFragment) {
128     this.path.append(urlFragment);
129     return this;
130   }
131 
132   /**
133    * Adds a query template expression.
134    * @param name Query parameter name
135    * @return Builder
136    */
137   public CaravanHttpRequestBuilder query(String name) {
138     queryNames.add(name);
139     return this;
140   }
141 
142   /**
143    * Adds a parameter with value to the request query
144    * @see CaravanHttpRequest#getUrl()
145    * @param name Parameter name
146    * @param value Parameter value
147    * @return Builder
148    */
149   public CaravanHttpRequestBuilder query(String name, Object value) {
150     return query(name).value(name, value);
151   }
152 
153   /**
154    * Adds a value for any UriTemplate expression in URL, header or body.
155    * @param name Parameter name
156    * @param value Parameter value
157    * @return Builder
158    */
159   public CaravanHttpRequestBuilder value(String name, Object value) {
160     values.put(name, value);
161     return this;
162   }
163 
164   /**
165    * Sets a template for the HTTP body.
166    * @see CaravanHttpRequest#getBody()
167    * @param template Body template
168    * @return Builder
169    */
170   public CaravanHttpRequestBuilder body(String template) {
171     body = null;
172     charset = Charsets.UTF_8;
173     bodyTemplate = template;
174     return this;
175   }
176 
177   /**
178    * Sets the HTTP body and charset.
179    * @see CaravanHttpRequest#getBody()
180    * @param newBody HTTP body
181    * @param newCharset HTTP charset
182    * @return Builder
183    */
184   public CaravanHttpRequestBuilder body(byte[] newBody, Charset newCharset) {
185     body = newBody;
186     charset = newCharset;
187     bodyTemplate = null;
188     return this;
189   }
190 
191   /**
192    * Creates the request object with no values for the templates.
193    * @return Request
194    */
195   public CaravanHttpRequest build() {
196     return build(Collections.emptyMap());
197   }
198 
199   /**
200    * Creates the request object with given values for the templates.
201    * @param parameters Template values
202    * @return Request
203    */
204   public CaravanHttpRequest build(Map<String, Object> parameters) {
205     String expandedUrl = getExpandedUrl(parameters);
206     Multimap<String, String> expandedHeaders = getExpandedHeaders(parameters);
207     byte[] expandedBody = getExpandedBody(parameters);
208     return new CaravanHttpRequest(serviceId, method, expandedUrl, expandedHeaders, expandedBody, charset);
209   }
210 
211   private String getExpandedUrl(Map<String, Object> parameters) {
212 
213     Map<String, Object> mergedParams = Maps.newHashMap(parameters);
214     mergedParams.putAll(values);
215     List<String> sortedQueryNames = Lists.newArrayList(queryNames);
216     Collections.sort(sortedQueryNames);
217     String operator = path.indexOf("?") == -1 ? "?" : "&";
218     String query = queryNames.isEmpty() ? "" : ('{' + operator + StringUtils.join(queryNames, ',') + '}');
219     return UriTemplate.fromTemplate(path + query).expand(mergedParams);
220 
221   }
222 
223   private Multimap<String, String> getExpandedHeaders(Map<String, Object> parameters) {
224     Multimap<String, String> expandedHeaders = ArrayListMultimap.create();
225     headers.entries().forEach(entry -> {
226       String template = entry.getValue();
227       String expanded = UriTemplate.expand(template, parameters);
228       if (!Strings.isNullOrEmpty(expanded)) {
229         expandedHeaders.put(entry.getKey(), expanded);
230       }
231     });
232     return expandedHeaders;
233   }
234 
235   private byte[] getExpandedBody(Map<String, Object> parameters) {
236     if (bodyTemplate == null) {
237       return body;
238     }
239     return CaravanHttpHelper.urlDecode(UriTemplate.expand(bodyTemplate, parameters)).getBytes(Charsets.UTF_8);
240   }
241 
242 }