2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017 AT&T Intellectual Property.
6 * Copyright © 2017 Amdocs
8 * ================================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 * ============LICENSE_END=========================================================
22 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
24 package org.onap.aai.modelloader.restclient;
26 import com.sun.jersey.api.client.Client;
27 import com.sun.jersey.api.client.ClientResponse;
28 import com.sun.jersey.api.client.config.ClientConfig;
29 import com.sun.jersey.api.client.config.DefaultClientConfig;
30 import com.sun.jersey.api.client.filter.LoggingFilter;
31 import com.sun.jersey.client.urlconnection.HTTPSProperties;
33 import org.onap.aai.modelloader.config.ModelLoaderConfig;
34 import org.onap.aai.modelloader.restclient.AaiRestClient;
35 import org.onap.aai.modelloader.service.ModelLoaderMsgs;
36 import org.openecomp.cl.api.LogFields;
37 import org.openecomp.cl.api.LogLine;
38 import org.openecomp.cl.api.Logger;
39 import org.openecomp.cl.eelf.LoggerFactory;
40 import org.openecomp.cl.mdc.MdcContext;
41 import org.openecomp.cl.mdc.MdcOverride;
42 import org.w3c.dom.Document;
43 import org.w3c.dom.Node;
44 import org.w3c.dom.NodeList;
45 import org.xml.sax.InputSource;
46 import org.xml.sax.SAXException;
48 import java.io.ByteArrayOutputStream;
49 import java.io.FileInputStream;
50 import java.io.IOException;
51 import java.io.PrintStream;
52 import java.io.StringReader;
53 import java.security.GeneralSecurityException;
54 import java.security.KeyStore;
55 import java.security.cert.X509Certificate;
56 import java.text.SimpleDateFormat;
58 import javax.net.ssl.HostnameVerifier;
59 import javax.net.ssl.HttpsURLConnection;
60 import javax.net.ssl.KeyManagerFactory;
61 import javax.net.ssl.SSLContext;
62 import javax.net.ssl.SSLSession;
63 import javax.net.ssl.TrustManager;
64 import javax.net.ssl.X509TrustManager;
65 import javax.ws.rs.core.Response;
66 import javax.xml.parsers.DocumentBuilder;
67 import javax.xml.parsers.DocumentBuilderFactory;
68 import javax.xml.parsers.ParserConfigurationException;
70 public class AaiRestClient {
71 public enum MimeType {
72 XML("application/xml"), JSON("application/json");
74 private String httpType;
76 MimeType(String httpType) {
77 this.httpType = httpType;
80 String getHttpHeaderType() {
85 private static String HEADER_TRANS_ID = "X-TransactionId";
86 private static String HEADER_FROM_APP_ID = "X-FromAppId";
87 private static String HEADER_AUTHORIZATION = "Authorization";
88 private static String ML_APP_NAME = "ModelLoader";
89 private static String RESOURCE_VERSION_PARAM = "resource-version";
91 private static SimpleDateFormat dateFormatter = new SimpleDateFormat(
92 "yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
94 private static Logger logger = LoggerFactory.getInstance()
95 .getLogger(AaiRestClient.class.getName());
96 private static Logger metricsLogger = LoggerFactory.getInstance()
97 .getMetricsLogger(AaiRestClient.class.getName());
99 private ModelLoaderConfig config = null;
101 public AaiRestClient(ModelLoaderConfig config) {
102 this.config = config;
106 * Send a PUT request to the A&AI.
113 * - the XML or JSON payload for the request
115 * - the content type (XML or JSON)
116 * @return ClientResponse
118 public ClientResponse putResource(String url, String payload, String transId, MimeType mimeType) {
119 ClientResponse result = null;
120 ByteArrayOutputStream baos = new ByteArrayOutputStream();
121 long startTimeInMs = 0;
122 MdcOverride override = new MdcOverride();
125 Client client = setupClient();
127 baos = new ByteArrayOutputStream();
128 PrintStream ps = new PrintStream(baos);
129 if (logger.isDebugEnabled()) {
130 client.addFilter(new LoggingFilter(ps));
133 // Grab the current time so that we can use it for metrics purposes later.
134 startTimeInMs = System.currentTimeMillis();
135 override.addAttribute(MdcContext.MDC_START_TIME, dateFormatter.format(startTimeInMs));
137 if (useBasicAuth()) {
138 result = client.resource(url).header(HEADER_TRANS_ID, transId)
139 .header(HEADER_FROM_APP_ID, ML_APP_NAME)
140 .header(HEADER_AUTHORIZATION, getAuthenticationCredentials())
141 .type(mimeType.getHttpHeaderType()).put(ClientResponse.class, payload);
143 result = client.resource(url).header(HEADER_TRANS_ID, transId)
144 .header(HEADER_FROM_APP_ID, ML_APP_NAME).type(mimeType.getHttpHeaderType())
145 .put(ClientResponse.class, payload);
147 } catch (Exception ex) {
148 logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "PUT", url, ex.getLocalizedMessage());
151 if (logger.isDebugEnabled()) {
152 logger.debug(baos.toString());
156 if ((result != null) && ((result.getStatus() == Response.Status.CREATED.getStatusCode())
157 || (result.getStatus() == Response.Status.OK.getStatusCode()))) {
158 logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS, "PUT", url,
159 Integer.toString(result.getStatus()));
160 metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS,
161 new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus())
162 .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
163 result.getResponseStatus().toString()),
164 override, "PUT", url, Integer.toString(result.getStatus()));
166 // If response is not 200 OK, then additionally log the reason
167 String respMsg = result.getEntity(String.class);
168 if (respMsg == null) {
169 respMsg = result.getStatusInfo().getReasonPhrase();
171 logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_UNSUCCESSFUL, "PUT", url,
172 Integer.toString(result.getStatus()), respMsg);
173 metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_UNSUCCESSFUL,
174 new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus())
175 .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
176 result.getResponseStatus().toString()),
177 override, "PUT", url, Integer.toString(result.getStatus()), respMsg);
184 * Send a DELETE request to the A&AI.
188 * @param resourceVersion
189 * - the resource-version of the model to delete
192 * @return ClientResponse
194 public ClientResponse deleteResource(String url, String resourceVersion, String transId) {
195 ClientResponse result = null;
196 ByteArrayOutputStream baos = new ByteArrayOutputStream();
197 long startTimeInMs = 0;
198 MdcOverride override = new MdcOverride();
201 Client client = setupClient();
203 baos = new ByteArrayOutputStream();
204 PrintStream ps = new PrintStream(baos);
205 if (logger.isDebugEnabled()) {
206 client.addFilter(new LoggingFilter(ps));
209 // Grab the current time so that we can use it for metrics purposes later.
210 startTimeInMs = System.currentTimeMillis();
211 override.addAttribute(MdcContext.MDC_START_TIME, dateFormatter.format(startTimeInMs));
213 if (useBasicAuth()) {
214 result = client.resource(url).queryParam(RESOURCE_VERSION_PARAM, resourceVersion)
215 .header(HEADER_TRANS_ID, transId).header(HEADER_FROM_APP_ID, ML_APP_NAME)
216 .header(HEADER_AUTHORIZATION, getAuthenticationCredentials())
217 .delete(ClientResponse.class);
219 result = client.resource(url).queryParam(RESOURCE_VERSION_PARAM, resourceVersion)
220 .header(HEADER_TRANS_ID, transId).header(HEADER_FROM_APP_ID, ML_APP_NAME)
221 .delete(ClientResponse.class);
223 } catch (Exception ex) {
224 logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "DELETE", url, ex.getLocalizedMessage());
227 if (logger.isDebugEnabled()) {
228 logger.debug(baos.toString());
232 logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS, "DELETE", url,
233 Integer.toString(result.getStatus()));
234 metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS,
235 new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus()).setField(
236 LogLine.DefinedFields.RESPONSE_DESCRIPTION, result.getResponseStatus().toString()),
237 override, "DELETE", url, Integer.toString(result.getStatus()));
243 * Send a GET request to the A&AI for a resource.
249 * @return ClientResponse
251 public ClientResponse getResource(String url, String transId, MimeType mimeType) {
252 ClientResponse result = null;
253 ByteArrayOutputStream baos = new ByteArrayOutputStream();
254 long startTimeInMs = 0;
255 MdcOverride override = new MdcOverride();
258 Client client = setupClient();
260 baos = new ByteArrayOutputStream();
261 PrintStream ps = new PrintStream(baos);
262 if (logger.isDebugEnabled()) {
263 client.addFilter(new LoggingFilter(ps));
266 // Grab the current time so that we can use it for metrics purposes later.
267 startTimeInMs = System.currentTimeMillis();
268 override.addAttribute(MdcContext.MDC_START_TIME, dateFormatter.format(startTimeInMs));
270 if (useBasicAuth()) {
271 result = client.resource(url).header(HEADER_TRANS_ID, transId)
272 .header(HEADER_FROM_APP_ID, ML_APP_NAME).accept(mimeType.getHttpHeaderType())
273 .header(HEADER_AUTHORIZATION, getAuthenticationCredentials()).get(ClientResponse.class);
275 result = client.resource(url).header(HEADER_TRANS_ID, transId)
276 .header(HEADER_FROM_APP_ID, ML_APP_NAME).accept(mimeType.getHttpHeaderType())
277 .get(ClientResponse.class);
280 } catch (Exception ex) {
281 logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "GET", url, ex.getLocalizedMessage());
284 if (logger.isDebugEnabled()) {
285 logger.debug(baos.toString());
289 logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS, "GET", url,
290 Integer.toString(result.getStatus()));
291 metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS,
292 new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus()).setField(
293 LogLine.DefinedFields.RESPONSE_DESCRIPTION, result.getResponseStatus().toString()),
294 override, "GET", url, Integer.toString(result.getStatus()));
300 * Send a POST request to the A&AI.
307 * - the XML or JSON payload for the request
309 * - the content type (XML or JSON)
310 * @return ClientResponse
312 public ClientResponse postResource(String url, String payload, String transId, MimeType mimeType) {
313 ClientResponse result = null;
314 ByteArrayOutputStream baos = new ByteArrayOutputStream();
315 long startTimeInMs = 0;
316 MdcOverride override = new MdcOverride();
319 Client client = setupClient();
321 baos = new ByteArrayOutputStream();
322 PrintStream ps = new PrintStream(baos);
323 if (logger.isDebugEnabled()) {
324 client.addFilter(new LoggingFilter(ps));
327 // Grab the current time so that we can use it for metrics purposes later.
328 startTimeInMs = System.currentTimeMillis();
329 override.addAttribute(MdcContext.MDC_START_TIME, dateFormatter.format(startTimeInMs));
331 if (useBasicAuth()) {
332 result = client.resource(url).header(HEADER_TRANS_ID, transId)
333 .header(HEADER_FROM_APP_ID, ML_APP_NAME)
334 .header(HEADER_AUTHORIZATION, getAuthenticationCredentials())
335 .type(mimeType.getHttpHeaderType()).post(ClientResponse.class, payload);
337 result = client.resource(url).header(HEADER_TRANS_ID, transId)
338 .header(HEADER_FROM_APP_ID, ML_APP_NAME).type(mimeType.getHttpHeaderType())
339 .post(ClientResponse.class, payload);
341 } catch (Exception ex) {
342 logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "POST", url, ex.getLocalizedMessage());
345 if (logger.isDebugEnabled()) {
346 logger.debug(baos.toString());
350 if ((result != null) && ((result.getStatus() == Response.Status.CREATED.getStatusCode())
351 || (result.getStatus() == Response.Status.OK.getStatusCode()))) {
352 logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS, "POST", url,
353 Integer.toString(result.getStatus()));
354 metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS,
355 new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus())
356 .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
357 result.getResponseStatus().toString()),
358 override, "POST", url, Integer.toString(result.getStatus()));
360 // If response is not 200 OK, then additionally log the reason
361 String respMsg = result.getEntity(String.class);
362 if (respMsg == null) {
363 respMsg = result.getStatusInfo().getReasonPhrase();
365 logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_UNSUCCESSFUL, "POST", url,
366 Integer.toString(result.getStatus()), respMsg);
367 metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_UNSUCCESSFUL,
368 new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus())
369 .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
370 result.getResponseStatus().toString()),
371 override, "POST", url, Integer.toString(result.getStatus()), respMsg);
378 * Does a GET on a resource to retrieve the resource version, and then DELETE
385 * @return ClientResponse
387 public ClientResponse getAndDeleteResource(String url, String transId) {
388 // First, GET the model
389 ClientResponse getResponse = getResource(url, transId, MimeType.XML);
390 if ((getResponse == null) || (getResponse.getStatus() != Response.Status.OK.getStatusCode())) {
394 // Delete the model using the resource version in the response
395 String resVersion = null;
397 resVersion = getResourceVersion(getResponse);
398 } catch (Exception e) {
399 logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "GET", url, e.getLocalizedMessage());
403 return deleteResource(url, resVersion, transId);
406 private Client setupClient() throws IOException, GeneralSecurityException {
407 ClientConfig clientConfig = new DefaultClientConfig();
409 HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
411 public boolean verify(String string, SSLSession ssls) {
416 // Create a trust manager that does not validate certificate chains
417 TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
419 public X509Certificate[] getAcceptedIssuers() {
424 public void checkClientTrusted(X509Certificate[] certs, String authType) {}
427 public void checkServerTrusted(X509Certificate[] certs, String authType) {}
430 SSLContext ctx = SSLContext.getInstance("TLS");
431 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
432 FileInputStream fin = new FileInputStream(config.getAaiKeyStorePath());
433 KeyStore ks = KeyStore.getInstance("PKCS12");
434 char[] pwd = config.getAaiKeyStorePassword().toCharArray();
438 ctx.init(kmf.getKeyManagers(), trustAllCerts, null);
439 clientConfig.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
440 new HTTPSProperties(new HostnameVerifier() {
442 public boolean verify(String theString, SSLSession sslSession) {
447 Client client = Client.create(clientConfig);
452 private String getResourceVersion(ClientResponse response)
453 throws ParserConfigurationException, SAXException, IOException {
454 String respData = response.getEntity(String.class);
456 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
457 DocumentBuilder builder = factory.newDocumentBuilder();
458 InputSource is = new InputSource(new StringReader(respData));
459 Document doc = builder.parse(is);
461 NodeList nodeList = doc.getDocumentElement().getChildNodes();
462 for (int i = 0; i < nodeList.getLength(); i++) {
463 Node currentNode = nodeList.item(i);
464 if (currentNode.getNodeName().equals(RESOURCE_VERSION_PARAM)) {
465 return currentNode.getTextContent();
472 private String getAuthenticationCredentials() {
474 String usernameAndPassword = config.getAaiAuthenticationUser() + ":"
475 + config.getAaiAuthenticationPassword();
476 return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes());
479 public boolean useBasicAuth() {
480 return (config.getAaiAuthenticationUser() != null)
481 && (config.getAaiAuthenticationPassword() != null);