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