Remove OpenECOMP from license file
[aai/model-loader.git] / src / main / java / org / openecomp / modelloader / restclient / AaiRestClient.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017 AT&T Intellectual Property.
6  * Copyright © 2017 Amdocs
7  * All rights reserved.
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
12  *
13  *     http://www.apache.org/licenses/LICENSE-2.0
14  *
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=========================================================
21  *
22  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
23  */
24 package org.openecomp.modelloader.restclient;
25
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;
32 import org.openecomp.cl.api.LogFields;
33 import org.openecomp.cl.api.LogLine;
34 import org.openecomp.cl.api.Logger;
35 import org.openecomp.cl.eelf.LoggerFactory;
36 import org.openecomp.cl.mdc.MdcContext;
37 import org.openecomp.cl.mdc.MdcOverride;
38 import org.openecomp.modelloader.config.ModelLoaderConfig;
39 import org.openecomp.modelloader.service.ModelLoaderMsgs;
40 import org.w3c.dom.Document;
41 import org.w3c.dom.Node;
42 import org.w3c.dom.NodeList;
43 import org.xml.sax.InputSource;
44 import org.xml.sax.SAXException;
45
46 import java.io.ByteArrayOutputStream;
47 import java.io.FileInputStream;
48 import java.io.IOException;
49 import java.io.PrintStream;
50 import java.io.StringReader;
51 import java.security.GeneralSecurityException;
52 import java.security.KeyStore;
53 import java.security.cert.X509Certificate;
54 import java.text.SimpleDateFormat;
55
56 import javax.net.ssl.HostnameVerifier;
57 import javax.net.ssl.HttpsURLConnection;
58 import javax.net.ssl.KeyManagerFactory;
59 import javax.net.ssl.SSLContext;
60 import javax.net.ssl.SSLSession;
61 import javax.net.ssl.TrustManager;
62 import javax.net.ssl.X509TrustManager;
63 import javax.ws.rs.core.Response;
64 import javax.xml.parsers.DocumentBuilder;
65 import javax.xml.parsers.DocumentBuilderFactory;
66 import javax.xml.parsers.ParserConfigurationException;
67
68 public class AaiRestClient {
69   public enum MimeType {
70     XML("application/xml"), JSON("application/json");
71
72     private String httpType;
73
74     MimeType(String httpType) {
75       this.httpType = httpType;
76     }
77
78     String getHttpHeaderType() {
79       return httpType;
80     }
81   }
82
83   private static String HEADER_TRANS_ID = "X-TransactionId";
84   private static String HEADER_FROM_APP_ID = "X-FromAppId";
85   private static String HEADER_AUTHORIZATION = "Authorization";
86   private static String ML_APP_NAME = "ModelLoader";
87   private static String RESOURCE_VERSION_PARAM = "resource-version";
88
89   private static SimpleDateFormat dateFormatter = new SimpleDateFormat(
90       "yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
91
92   private static Logger logger = LoggerFactory.getInstance()
93       .getLogger(AaiRestClient.class.getName());
94   private static Logger metricsLogger = LoggerFactory.getInstance()
95       .getMetricsLogger(AaiRestClient.class.getName());
96
97   private ModelLoaderConfig config = null;
98
99   public AaiRestClient(ModelLoaderConfig config) {
100     this.config = config;
101   }
102
103   /**
104    * Send a PUT request to the A&AI.
105    *
106    * @param url
107    *          - the url
108    * @param transId
109    *          - transaction ID
110    * @param payload
111    *          - the XML or JSON payload for the request
112    * @param mimeType
113    *          - the content type (XML or JSON)
114    * @return ClientResponse
115    */
116   public ClientResponse putResource(String url, String payload, String transId, MimeType mimeType) {
117     ClientResponse result = null;
118     ByteArrayOutputStream baos = new ByteArrayOutputStream();
119     long startTimeInMs = 0;
120     MdcOverride override = new MdcOverride();
121
122     try {
123       Client client = setupClient();
124
125       baos = new ByteArrayOutputStream();
126       PrintStream ps = new PrintStream(baos);
127       if (logger.isDebugEnabled()) {
128         client.addFilter(new LoggingFilter(ps));
129       }
130
131       // Grab the current time so that we can use it for metrics purposes later.
132       startTimeInMs = System.currentTimeMillis();
133       override.addAttribute(MdcContext.MDC_START_TIME, dateFormatter.format(startTimeInMs));
134
135       if (useBasicAuth()) {
136         result = client.resource(url).header(HEADER_TRANS_ID, transId)
137             .header(HEADER_FROM_APP_ID, ML_APP_NAME)
138             .header(HEADER_AUTHORIZATION, getAuthenticationCredentials())
139             .type(mimeType.getHttpHeaderType()).put(ClientResponse.class, payload);
140       } else {
141         result = client.resource(url).header(HEADER_TRANS_ID, transId)
142             .header(HEADER_FROM_APP_ID, ML_APP_NAME).type(mimeType.getHttpHeaderType())
143             .put(ClientResponse.class, payload);
144       }
145     } catch (Exception ex) {
146       logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "PUT", url, ex.getLocalizedMessage());
147       return null;
148     } finally {
149       if (logger.isDebugEnabled()) {
150         logger.debug(baos.toString());
151       }
152     }
153
154     if ((result != null) && ((result.getStatus() == Response.Status.CREATED.getStatusCode())
155         || (result.getStatus() == Response.Status.OK.getStatusCode()))) {
156       logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS, "PUT", url,
157           Integer.toString(result.getStatus()));
158       metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS,
159           new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus())
160               .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
161                   result.getResponseStatus().toString()),
162           override, "PUT", url, Integer.toString(result.getStatus()));
163     } else {
164       // If response is not 200 OK, then additionally log the reason
165       String respMsg = result.getEntity(String.class);
166       if (respMsg == null) {
167         respMsg = result.getStatusInfo().getReasonPhrase();
168       }
169       logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_UNSUCCESSFUL, "PUT", url,
170           Integer.toString(result.getStatus()), respMsg);
171       metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_UNSUCCESSFUL,
172           new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus())
173               .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
174                   result.getResponseStatus().toString()),
175           override, "PUT", url, Integer.toString(result.getStatus()), respMsg);
176     }
177
178     return result;
179   }
180
181   /**
182    * Send a DELETE request to the A&AI.
183    *
184    * @param url
185    *          - the url
186    * @param resourceVersion
187    *          - the resource-version of the model to delete
188    * @param transId
189    *          - transaction ID
190    * @return ClientResponse
191    */
192   public ClientResponse deleteResource(String url, String resourceVersion, String transId) {
193     ClientResponse result = null;
194     ByteArrayOutputStream baos = new ByteArrayOutputStream();
195     long startTimeInMs = 0;
196     MdcOverride override = new MdcOverride();
197
198     try {
199       Client client = setupClient();
200
201       baos = new ByteArrayOutputStream();
202       PrintStream ps = new PrintStream(baos);
203       if (logger.isDebugEnabled()) {
204         client.addFilter(new LoggingFilter(ps));
205       }
206
207       // Grab the current time so that we can use it for metrics purposes later.
208       startTimeInMs = System.currentTimeMillis();
209       override.addAttribute(MdcContext.MDC_START_TIME, dateFormatter.format(startTimeInMs));
210
211       if (useBasicAuth()) {
212         result = client.resource(url).queryParam(RESOURCE_VERSION_PARAM, resourceVersion)
213             .header(HEADER_TRANS_ID, transId).header(HEADER_FROM_APP_ID, ML_APP_NAME)
214             .header(HEADER_AUTHORIZATION, getAuthenticationCredentials())
215             .delete(ClientResponse.class);
216       } else {
217         result = client.resource(url).queryParam(RESOURCE_VERSION_PARAM, resourceVersion)
218             .header(HEADER_TRANS_ID, transId).header(HEADER_FROM_APP_ID, ML_APP_NAME)
219             .delete(ClientResponse.class);
220       }
221     } catch (Exception ex) {
222       logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "DELETE", url, ex.getLocalizedMessage());
223       return null;
224     } finally {
225       if (logger.isDebugEnabled()) {
226         logger.debug(baos.toString());
227       }
228     }
229
230     logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS, "DELETE", url,
231         Integer.toString(result.getStatus()));
232     metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS,
233         new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus()).setField(
234             LogLine.DefinedFields.RESPONSE_DESCRIPTION, result.getResponseStatus().toString()),
235         override, "DELETE", url, Integer.toString(result.getStatus()));
236
237     return result;
238   }
239
240   /**
241    * Send a GET request to the A&AI for a resource.
242    *
243    * @param url
244    *          - the url to use
245    * @param transId
246    *          - transaction ID
247    * @return ClientResponse
248    */
249   public ClientResponse getResource(String url, String transId, MimeType mimeType) {
250     ClientResponse result = null;
251     ByteArrayOutputStream baos = new ByteArrayOutputStream();
252     long startTimeInMs = 0;
253     MdcOverride override = new MdcOverride();
254
255     try {
256       Client client = setupClient();
257
258       baos = new ByteArrayOutputStream();
259       PrintStream ps = new PrintStream(baos);
260       if (logger.isDebugEnabled()) {
261         client.addFilter(new LoggingFilter(ps));
262       }
263
264       // Grab the current time so that we can use it for metrics purposes later.
265       startTimeInMs = System.currentTimeMillis();
266       override.addAttribute(MdcContext.MDC_START_TIME, dateFormatter.format(startTimeInMs));
267
268       if (useBasicAuth()) {
269         result = client.resource(url).header(HEADER_TRANS_ID, transId)
270             .header(HEADER_FROM_APP_ID, ML_APP_NAME).accept(mimeType.getHttpHeaderType())
271             .header(HEADER_AUTHORIZATION, getAuthenticationCredentials()).get(ClientResponse.class);
272       } else {
273         result = client.resource(url).header(HEADER_TRANS_ID, transId)
274             .header(HEADER_FROM_APP_ID, ML_APP_NAME).accept(mimeType.getHttpHeaderType())
275             .get(ClientResponse.class);
276
277       }
278     } catch (Exception ex) {
279       logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "GET", url, ex.getLocalizedMessage());
280       return null;
281     } finally {
282       if (logger.isDebugEnabled()) {
283         logger.debug(baos.toString());
284       }
285     }
286
287     logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS, "GET", url,
288         Integer.toString(result.getStatus()));
289     metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS,
290         new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus()).setField(
291             LogLine.DefinedFields.RESPONSE_DESCRIPTION, result.getResponseStatus().toString()),
292         override, "GET", url, Integer.toString(result.getStatus()));
293
294     return result;
295   }
296
297   /**
298    * Send a POST request to the A&AI.
299    *
300    * @param url
301    *          - the url
302    * @param transId
303    *          - transaction ID
304    * @param payload
305    *          - the XML or JSON payload for the request
306    * @param mimeType
307    *          - the content type (XML or JSON)
308    * @return ClientResponse
309    */
310   public ClientResponse postResource(String url, String payload, String transId, MimeType mimeType) {
311     ClientResponse result = null;
312     ByteArrayOutputStream baos = new ByteArrayOutputStream();
313     long startTimeInMs = 0;
314     MdcOverride override = new MdcOverride();
315
316     try {
317       Client client = setupClient();
318
319       baos = new ByteArrayOutputStream();
320       PrintStream ps = new PrintStream(baos);
321       if (logger.isDebugEnabled()) {
322         client.addFilter(new LoggingFilter(ps));
323       }
324
325       // Grab the current time so that we can use it for metrics purposes later.
326       startTimeInMs = System.currentTimeMillis();
327       override.addAttribute(MdcContext.MDC_START_TIME, dateFormatter.format(startTimeInMs));
328
329       if (useBasicAuth()) {
330         result = client.resource(url).header(HEADER_TRANS_ID, transId)
331             .header(HEADER_FROM_APP_ID, ML_APP_NAME)
332             .header(HEADER_AUTHORIZATION, getAuthenticationCredentials())
333             .type(mimeType.getHttpHeaderType()).post(ClientResponse.class, payload);
334       } else {
335         result = client.resource(url).header(HEADER_TRANS_ID, transId)
336             .header(HEADER_FROM_APP_ID, ML_APP_NAME).type(mimeType.getHttpHeaderType())
337             .post(ClientResponse.class, payload);
338       }
339     } catch (Exception ex) {
340       logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "POST", url, ex.getLocalizedMessage());
341       return null;
342     } finally {
343       if (logger.isDebugEnabled()) {
344         logger.debug(baos.toString());
345       }
346     }
347
348     if ((result != null) && ((result.getStatus() == Response.Status.CREATED.getStatusCode())
349         || (result.getStatus() == Response.Status.OK.getStatusCode()))) {
350       logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS, "POST", url,
351           Integer.toString(result.getStatus()));
352       metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS,
353           new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus())
354               .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
355                   result.getResponseStatus().toString()),
356           override, "POST", url, Integer.toString(result.getStatus()));
357     } else {
358       // If response is not 200 OK, then additionally log the reason
359       String respMsg = result.getEntity(String.class);
360       if (respMsg == null) {
361         respMsg = result.getStatusInfo().getReasonPhrase();
362       }
363       logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_UNSUCCESSFUL, "POST", url,
364           Integer.toString(result.getStatus()), respMsg);
365       metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_UNSUCCESSFUL,
366           new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus())
367               .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
368                   result.getResponseStatus().toString()),
369           override, "POST", url, Integer.toString(result.getStatus()), respMsg);
370     }
371
372     return result;
373   }
374   
375   /**
376    * Does a GET on a resource to retrieve the resource version, and then DELETE
377    * that version.
378    *
379    * @param url
380    *          - the url
381    * @param transId
382    *          - transaction ID
383    * @return ClientResponse
384    */
385   public ClientResponse getAndDeleteResource(String url, String transId) {
386     // First, GET the model
387     ClientResponse getResponse = getResource(url, transId, MimeType.XML);
388     if ((getResponse == null) || (getResponse.getStatus() != Response.Status.OK.getStatusCode())) {
389       return getResponse;
390     }
391
392     // Delete the model using the resource version in the response
393     String resVersion = null;
394     try {
395       resVersion = getResourceVersion(getResponse);
396     } catch (Exception e) {
397       logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "GET", url, e.getLocalizedMessage());
398       return null;
399     }
400
401     return deleteResource(url, resVersion, transId);
402   }
403
404   private Client setupClient() throws IOException, GeneralSecurityException {
405     ClientConfig clientConfig = new DefaultClientConfig();
406
407     HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
408       @Override
409       public boolean verify(String string, SSLSession ssls) {
410         return true;
411       }
412     });
413
414     // Create a trust manager that does not validate certificate chains
415     TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
416       @Override
417       public X509Certificate[] getAcceptedIssuers() {
418         return null;
419       }
420
421       @Override
422       public void checkClientTrusted(X509Certificate[] certs, String authType) {}
423
424       @Override
425       public void checkServerTrusted(X509Certificate[] certs, String authType) {}
426     } };
427
428     SSLContext ctx = SSLContext.getInstance("TLS");
429     KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
430     FileInputStream fin = new FileInputStream(config.getAaiKeyStorePath());
431     KeyStore ks = KeyStore.getInstance("PKCS12");
432     char[] pwd = config.getAaiKeyStorePassword().toCharArray();
433     ks.load(fin, pwd);
434     kmf.init(ks, pwd);
435
436     ctx.init(kmf.getKeyManagers(), trustAllCerts, null);
437     clientConfig.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
438         new HTTPSProperties(new HostnameVerifier() {
439           @Override
440           public boolean verify(String theString, SSLSession sslSession) {
441             return true;
442           }
443         }, ctx));
444
445     Client client = Client.create(clientConfig);
446
447     return client;
448   }
449
450   private String getResourceVersion(ClientResponse response)
451       throws ParserConfigurationException, SAXException, IOException {
452     String respData = response.getEntity(String.class);
453
454     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
455     DocumentBuilder builder = factory.newDocumentBuilder();
456     InputSource is = new InputSource(new StringReader(respData));
457     Document doc = builder.parse(is);
458
459     NodeList nodeList = doc.getDocumentElement().getChildNodes();
460     for (int i = 0; i < nodeList.getLength(); i++) {
461       Node currentNode = nodeList.item(i);
462       if (currentNode.getNodeName().equals(RESOURCE_VERSION_PARAM)) {
463         return currentNode.getTextContent();
464       }
465     }
466
467     return null;
468   }
469
470   private String getAuthenticationCredentials() {
471
472     String usernameAndPassword = config.getAaiAuthenticationUser() + ":"
473         + config.getAaiAuthenticationPassword();
474     return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes());
475   }
476
477   public boolean useBasicAuth() {
478     return (config.getAaiAuthenticationUser() != null)
479         && (config.getAaiAuthenticationPassword() != null);
480   }
481 }