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