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