dc03f9e99be356b83e7c071b6d92fa38396ac3ca
[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         public <T> OpenStackResponse request(OpenStackRequest<T> request) {
108                 
109                 CloseableHttpClient httpClient = null; //HttpClients.createDefault();
110                 httpClient = HttpClients.custom().setRedirectStrategy(new HttpClientRedirectStrategy()).build();
111
112                 URI uri = null;
113                 
114                 // Build the URI with query params
115                 try {
116                         URIBuilder uriBuilder = new URIBuilder(request.endpoint() + request.path());
117
118                         for(Map.Entry<String, List<Object> > entry : request.queryParams().entrySet()) {
119                                 for (Object o : entry.getValue()) {
120                                         uriBuilder.setParameter(entry.getKey(), String.valueOf(o));
121                                 }
122                         }
123                         
124                         uri = uriBuilder.build();
125                 } catch (URISyntaxException e) {
126                         throw new HttpClientException (e);
127                 }
128
129                 HttpEntity entity = null;
130                 if (request.entity() != null) {
131                         // Flatten the entity to a Json string
132                                         
133                         try {
134                         // Get appropriate mapper, based on existence of a root element in Entity class
135                                 ObjectMapper mapper = getObjectMapper (request.entity().getEntity().getClass());
136
137                                 String entityJson = mapper.writeValueAsString (request.entity().getEntity());
138                                 entity = new StringEntity(entityJson, ContentType.create(request.entity().getContentType()));
139                                 
140                                 System.out.println("Openstack query JSON:"+entityJson);
141                                 LOGGER.debug ("Request JSON Body: " + entityJson.replaceAll("\"password\":\"[^\"]*\"", "\"password\":\"***\""));
142
143                         } catch (JsonProcessingException e) {
144                                 throw new HttpClientException ("Json processing error on request entity", e);
145                         } catch (IOException e) {
146                                 throw new HttpClientException ("Json IO error on request entity", e);
147                         }
148                 }
149                 
150                 // Determine the HttpRequest class based on the method
151                 HttpUriRequest httpRequest;
152                 
153                 switch (request.method()) {
154                 case POST:
155                         HttpPost post = new HttpPost(uri);
156                         post.setEntity (entity);
157                         httpRequest = post;
158                         break;
159                         
160                 case GET:
161                         httpRequest = new HttpGet(uri);
162                         break;
163
164                 case PUT:
165                         HttpPut put = new HttpPut(uri);
166                         put.setEntity (entity);
167                         httpRequest = put;
168                         break;
169                         
170                 case DELETE:
171                         httpRequest = new HttpDelete(uri);
172                         break;
173                         
174                 default:
175                         throw new HttpClientException ("Unrecognized HTTP Method: " + request.method());
176                 }
177                 
178                 for (Entry<String, List<Object>> h : request.headers().entrySet()) {
179                         StringBuilder sb = new StringBuilder();
180                         for (Object v : h.getValue()) {
181                                 sb.append(String.valueOf(v));
182                         }
183                         httpRequest.addHeader(h.getKey(), sb.toString());
184                 }
185
186                 LOGGER.debug ("Sending HTTP request: " + httpRequest.toString());
187                 
188                 // Get the Response.  But don't get the body entity yet, as this response
189                 // will be wrapped in an HttpClientResponse.  The HttpClientResponse
190                 // buffers the body in constructor, so can close the response here.
191                 HttpClientResponse httpClientResponse = null;
192                 CloseableHttpResponse httpResponse = null;
193                 
194                 // Catch known HttpClient exceptions, and wrap them in OpenStack Client Exceptions
195                 // so calling functions can distinguish.  Only RuntimeExceptions are allowed.
196                 try {
197                         httpResponse = httpClient.execute(httpRequest);
198
199                         LOGGER.debug ("Response status: " + httpResponse.getStatusLine().getStatusCode());
200                         
201                         httpClientResponse = new HttpClientResponse (httpResponse);
202
203                         int status = httpResponse.getStatusLine().getStatusCode();
204                         if (status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED ||
205                                 status == HttpStatus.SC_NO_CONTENT || status == HttpStatus.SC_ACCEPTED)
206                         {
207                                 return httpClientResponse;
208                         }
209                 }
210                 catch (HttpResponseException e) {
211                         // What exactly does this mean?  It does not appear to get thrown for
212                         // non-2XX responses as documented.
213                         throw new OpenStackResponseException(e.getMessage(), e.getStatusCode());
214                 }
215                 catch (UnknownHostException e) {
216                         throw new OpenStackConnectException("Unknown Host: " + e.getMessage());
217                 }
218                 catch (IOException e) {
219                         // Catch all other IOExceptions and throw as OpenStackConnectException
220                         throw new OpenStackConnectException(e.getMessage());
221                 }
222                 catch (Exception e) {
223                         // Catchall for anything else, must throw as a RuntimeException
224                         e.printStackTrace();
225                         throw new RuntimeException("Unexpected client exception", e);
226                 }
227                 finally {
228                         // Have the body.  Close the stream
229                         if (httpResponse != null)
230                                 try {
231                                         httpResponse.close();
232                                 } catch (IOException e) {
233                                         LOGGER.warn("Unable to close HTTP Response: " + e);
234                                 }
235                 }
236                 
237                 // Get here on an error response (4XX-5XX)
238                 throw new OpenStackResponseException(httpResponse.getStatusLine().getReasonPhrase(),
239                                                                                         httpResponse.getStatusLine().getStatusCode(),
240                                                                                         httpClientResponse);
241         }
242
243 }