f7fa30bfb9c986c41dd4ae52a91cbd80824b1530
[so/libs.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  * ============LICENSE_END=========================================================
15  */
16
17 /*
18  * ============LICENSE_START==========================================
19  * ===================================================================
20  * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
21  * ===================================================================
22  * Licensed under the Apache License, Version 2.0 (the "License");
23  * you may not use this file except in compliance with the License.
24  * You may obtain a copy of the License at
25  *
26  *        http://www.apache.org/licenses/LICENSE-2.0
27  *
28  * Unless required by applicable law or agreed to in writing, software
29  * distributed under the License is distributed on an "AS IS" BASIS,
30  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31  * See the License for the specific language governing permissions and
32  * limitations under the License.
33  * ============LICENSE_END============================================
34  *
35  * ECOMP and OpenECOMP are trademarks
36  * and service marks of AT&T Intellectual Property.
37  *
38  */
39
40 package com.woorea.openstack.connector;
41
42 import java.io.IOException;
43 import java.net.URI;
44 import java.net.URISyntaxException;
45 import java.net.UnknownHostException;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Map.Entry;
49
50 import org.apache.http.HttpEntity;
51 import org.apache.http.HttpStatus;
52 import org.apache.http.client.HttpResponseException;
53 import org.apache.http.client.methods.CloseableHttpResponse;
54 import org.apache.http.client.methods.HttpDelete;
55 import org.apache.http.client.methods.HttpGet;
56 import org.apache.http.client.methods.HttpPost;
57 import org.apache.http.client.methods.HttpPut;
58 import org.apache.http.client.methods.HttpUriRequest;
59 import org.apache.http.client.utils.URIBuilder;
60 import org.apache.http.entity.ContentType;
61 import org.apache.http.entity.StringEntity;
62 import org.apache.http.impl.client.CloseableHttpClient;
63 import org.apache.http.impl.client.HttpClients;
64 import org.apache.log4j.Logger;
65 import org.codehaus.jackson.JsonProcessingException;
66 import org.codehaus.jackson.map.DeserializationConfig;
67 import org.codehaus.jackson.map.ObjectMapper;
68 import org.codehaus.jackson.map.SerializationConfig;
69 import org.codehaus.jackson.map.annotate.JsonRootName;
70 import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
71
72 import com.woorea.openstack.base.client.OpenStackClientConnector;
73 import com.woorea.openstack.base.client.OpenStackConnectException;
74 import com.woorea.openstack.base.client.OpenStackRequest;
75 import com.woorea.openstack.base.client.OpenStackResponse;
76 import com.woorea.openstack.base.client.OpenStackResponseException;
77
78 public class HttpClientConnector implements OpenStackClientConnector {
79
80         public static ObjectMapper DEFAULT_MAPPER;
81         public static ObjectMapper WRAPPED_MAPPER;
82         
83         private static Logger LOGGER = Logger.getLogger(HttpClientConnector.class);
84
85         static {
86                 DEFAULT_MAPPER = new ObjectMapper();
87
88                 DEFAULT_MAPPER.setSerializationInclusion(Inclusion.NON_NULL);
89                 DEFAULT_MAPPER.disable(SerializationConfig.Feature.INDENT_OUTPUT);
90                 DEFAULT_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
91                 DEFAULT_MAPPER.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
92
93                 WRAPPED_MAPPER = new ObjectMapper();
94
95                 WRAPPED_MAPPER.setSerializationInclusion(Inclusion.NON_NULL);
96                 WRAPPED_MAPPER.disable(SerializationConfig.Feature.INDENT_OUTPUT);
97                 WRAPPED_MAPPER.enable(SerializationConfig.Feature.WRAP_ROOT_VALUE);
98                 WRAPPED_MAPPER.enable(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE);
99                 WRAPPED_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
100                 WRAPPED_MAPPER.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
101         }
102         
103         protected static <T> ObjectMapper getObjectMapper (Class<T> type) {
104                 return type.getAnnotation(JsonRootName.class) == null ? DEFAULT_MAPPER : WRAPPED_MAPPER;
105         }
106
107         @Override
108         public <T> OpenStackResponse request(OpenStackRequest<T> request) {
109                 
110                 CloseableHttpClient httpClient = null; //HttpClients.createDefault();
111                 httpClient = HttpClients.custom().setRedirectStrategy(new HttpClientRedirectStrategy()).build();
112
113                 URI uri = null;
114                 
115                 // Build the URI with query params
116                 try {
117                         URIBuilder uriBuilder = new URIBuilder(request.endpoint() + request.path());
118
119                         for(Map.Entry<String, List<Object> > entry : request.queryParams().entrySet()) {
120                                 for (Object o : entry.getValue()) {
121                                         uriBuilder.setParameter(entry.getKey(), String.valueOf(o));
122                                 }
123                         }
124                         
125                         uri = uriBuilder.build();
126                 } catch (URISyntaxException e) {
127                         throw new HttpClientException (e);
128                 }
129
130                 HttpEntity entity = null;
131                 if (request.entity() != null) {
132                         // Flatten the entity to a Json string
133                                         
134                         try {
135                         // Get appropriate mapper, based on existence of a root element in Entity class
136                                 ObjectMapper mapper = getObjectMapper (request.entity().getEntity().getClass());
137
138                                 String entityJson = mapper.writeValueAsString (request.entity().getEntity());
139                                 entity = new StringEntity(entityJson, ContentType.create(request.entity().getContentType()));
140
141                                 LOGGER.debug("Openstack query JSON:"+entityJson);
142                                 LOGGER.debug ("Request JSON Body: " + entityJson.replaceAll("\"password\":\"[^\"]*\"", "\"password\":\"***\""));
143
144                         } catch (JsonProcessingException e) {
145                                 throw new HttpClientException ("Json processing error on request entity", e);
146                         } catch (IOException e) {
147                                 throw new HttpClientException ("Json IO error on request entity", e);
148                         }
149                 }
150                 
151                 // Determine the HttpRequest class based on the method
152                 HttpUriRequest httpRequest;
153                 
154                 switch (request.method()) {
155                 case POST:
156                         HttpPost post = new HttpPost(uri);
157                         post.setEntity (entity);
158                         httpRequest = post;
159                         break;
160                         
161                 case GET:
162                         httpRequest = new HttpGet(uri);
163                         break;
164
165                 case PUT:
166                         HttpPut put = new HttpPut(uri);
167                         put.setEntity (entity);
168                         httpRequest = put;
169                         break;
170                         
171                 case DELETE:
172                         httpRequest = new HttpDelete(uri);
173                         break;
174                         
175                 default:
176                         throw new HttpClientException ("Unrecognized HTTP Method: " + request.method());
177                 }
178                 
179                 for (Entry<String, List<Object>> h : request.headers().entrySet()) {
180                         StringBuilder sb = new StringBuilder();
181                         for (Object v : h.getValue()) {
182                                 sb.append(String.valueOf(v));
183                         }
184                         httpRequest.addHeader(h.getKey(), sb.toString());
185                 }
186
187                 LOGGER.debug ("Sending HTTP request: " + httpRequest.toString());
188                 
189                 // Get the Response.  But don't get the body entity yet, as this response
190                 // will be wrapped in an HttpClientResponse.  The HttpClientResponse
191                 // buffers the body in constructor, so can close the response here.
192                 HttpClientResponse httpClientResponse = null;
193                 CloseableHttpResponse httpResponse = null;
194                 
195                 // Catch known HttpClient exceptions, and wrap them in OpenStack Client Exceptions
196                 // so calling functions can distinguish.  Only RuntimeExceptions are allowed.
197                 try {
198                         httpResponse = httpClient.execute(httpRequest);
199
200                         LOGGER.debug ("Response status: " + httpResponse.getStatusLine().getStatusCode());
201                         
202                         httpClientResponse = new HttpClientResponse (httpResponse);
203
204                         int status = httpResponse.getStatusLine().getStatusCode();
205                         if (status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED ||
206                                 status == HttpStatus.SC_NO_CONTENT || status == HttpStatus.SC_ACCEPTED)
207                         {
208                                 return httpClientResponse;
209                         }
210                 }
211                 catch (HttpResponseException e) {
212                         // What exactly does this mean?  It does not appear to get thrown for
213                         // non-2XX responses as documented.
214                         throw new OpenStackResponseException(e.getMessage(), e.getStatusCode());
215                 }
216                 catch (UnknownHostException e) {
217                         throw new OpenStackConnectException("Unknown Host: " + e.getMessage());
218                 }
219                 catch (IOException e) {
220                         // Catch all other IOExceptions and throw as OpenStackConnectException
221                         throw new OpenStackConnectException(e.getMessage());
222                 }
223                 catch (Exception e) {
224                         // Catchall for anything else, must throw as a RuntimeException
225                         e.printStackTrace();
226                         throw new RuntimeException("Unexpected client exception", e);
227                 }
228                 finally {
229                         // Have the body.  Close the stream
230                         if (httpResponse != null)
231                                 try {
232                                         httpResponse.close();
233                                 } catch (IOException e) {
234                                         LOGGER.warn("Unable to close HTTP Response: " + e);
235                                 }
236                 }
237                 
238                 // Get here on an error response (4XX-5XX)
239                 throw new OpenStackResponseException(httpResponse.getStatusLine().getReasonPhrase(),
240                                                                                         httpResponse.getStatusLine().getStatusCode(),
241                                                                                         httpClientResponse);
242         }
243
244 }