2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * 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=========================================================
21 package org.openecomp.modelloader.restclient;
23 import com.sun.jersey.api.client.Client;
24 import com.sun.jersey.api.client.ClientResponse;
25 import com.sun.jersey.api.client.config.ClientConfig;
26 import com.sun.jersey.api.client.config.DefaultClientConfig;
27 import com.sun.jersey.api.client.filter.LoggingFilter;
28 import com.sun.jersey.client.urlconnection.HTTPSProperties;
29 import org.openecomp.cl.api.LogFields;
30 import org.openecomp.cl.api.LogLine;
31 import org.openecomp.cl.api.Logger;
32 import org.openecomp.cl.eelf.LoggerFactory;
33 import org.openecomp.cl.mdc.MdcContext;
34 import org.openecomp.cl.mdc.MdcOverride;
35 import org.openecomp.modelloader.config.ModelLoaderConfig;
36 import org.openecomp.modelloader.service.ModelLoaderMsgs;
37 import org.w3c.dom.Document;
38 import org.w3c.dom.Node;
39 import org.w3c.dom.NodeList;
40 import org.xml.sax.InputSource;
41 import org.xml.sax.SAXException;
43 import java.io.ByteArrayOutputStream;
44 import java.io.FileInputStream;
45 import java.io.IOException;
46 import java.io.PrintStream;
47 import java.io.StringReader;
48 import java.security.GeneralSecurityException;
49 import java.security.KeyStore;
50 import java.security.cert.X509Certificate;
51 import java.text.SimpleDateFormat;
53 import javax.net.ssl.HostnameVerifier;
54 import javax.net.ssl.HttpsURLConnection;
55 import javax.net.ssl.KeyManagerFactory;
56 import javax.net.ssl.SSLContext;
57 import javax.net.ssl.SSLSession;
58 import javax.net.ssl.TrustManager;
59 import javax.net.ssl.X509TrustManager;
60 import javax.ws.rs.core.Response;
61 import javax.xml.parsers.DocumentBuilder;
62 import javax.xml.parsers.DocumentBuilderFactory;
63 import javax.xml.parsers.ParserConfigurationException;
65 public class AaiRestClient {
66 public enum MimeType {
67 XML("application/xml"), JSON("application/json");
69 private String httpType;
71 MimeType(String httpType) {
72 this.httpType = httpType;
75 String getHttpHeaderType() {
80 private static String HEADER_TRANS_ID = "X-TransactionId";
81 private static String HEADER_FROM_APP_ID = "X-FromAppId";
82 private static String HEADER_AUTHORIZATION = "Authorization";
83 private static String ML_APP_NAME = "ModelLoader";
84 private static String RESOURCE_VERSION_PARAM = "resource-version";
86 private static SimpleDateFormat dateFormatter = new SimpleDateFormat(
87 "yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
89 private static Logger logger = LoggerFactory.getInstance()
90 .getLogger(AaiRestClient.class.getName());
91 private static Logger metricsLogger = LoggerFactory.getInstance()
92 .getMetricsLogger(AaiRestClient.class.getName());
94 private ModelLoaderConfig config = null;
96 public AaiRestClient(ModelLoaderConfig config) {
101 * Send a PUT request to the A&AI.
108 * - the XML or JSON payload for the request
110 * - the content type (XML or JSON)
111 * @return ClientResponse
113 public ClientResponse putResource(String url, String payload, String transId, MimeType mimeType) {
114 ClientResponse result = null;
115 ByteArrayOutputStream baos = new ByteArrayOutputStream();
116 long startTimeInMs = 0;
117 MdcOverride override = new MdcOverride();
120 Client client = setupClient();
122 baos = new ByteArrayOutputStream();
123 PrintStream ps = new PrintStream(baos);
124 if (logger.isDebugEnabled()) {
125 client.addFilter(new LoggingFilter(ps));
128 // Grab the current time so that we can use it for metrics purposes later.
129 startTimeInMs = System.currentTimeMillis();
130 override.addAttribute(MdcContext.MDC_START_TIME, dateFormatter.format(startTimeInMs));
132 if (useBasicAuth()) {
133 result = client.resource(url).header(HEADER_TRANS_ID, transId)
134 .header(HEADER_FROM_APP_ID, ML_APP_NAME)
135 .header(HEADER_AUTHORIZATION, getAuthenticationCredentials())
136 .type(mimeType.getHttpHeaderType()).put(ClientResponse.class, payload);
138 result = client.resource(url).header(HEADER_TRANS_ID, transId)
139 .header(HEADER_FROM_APP_ID, ML_APP_NAME).type(mimeType.getHttpHeaderType())
140 .put(ClientResponse.class, payload);
142 } catch (Exception ex) {
143 logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "PUT", url, ex.getLocalizedMessage());
146 if (logger.isDebugEnabled()) {
147 logger.debug(baos.toString());
151 if ((result != null) && ((result.getStatus() == Response.Status.CREATED.getStatusCode())
152 || (result.getStatus() == Response.Status.OK.getStatusCode()))) {
153 logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS, "PUT", url,
154 Integer.toString(result.getStatus()));
155 metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS,
156 new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus())
157 .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
158 result.getResponseStatus().toString()),
159 override, "PUT", url, Integer.toString(result.getStatus()));
161 // If response is not 200 OK, then additionally log the reason
162 String respMsg = result.getEntity(String.class);
163 if (respMsg == null) {
164 respMsg = result.getStatusInfo().getReasonPhrase();
166 logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_UNSUCCESSFUL, "PUT", url,
167 Integer.toString(result.getStatus()), respMsg);
168 metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_UNSUCCESSFUL,
169 new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus())
170 .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION,
171 result.getResponseStatus().toString()),
172 override, "PUT", url, Integer.toString(result.getStatus()), respMsg);
179 * Send a DELETE request to the A&AI.
183 * @param resourceVersion
184 * - the resource-version of the model to delete
187 * @return ClientResponse
189 public ClientResponse deleteResource(String url, String resourceVersion, String transId) {
190 ClientResponse result = null;
191 ByteArrayOutputStream baos = new ByteArrayOutputStream();
192 long startTimeInMs = 0;
193 MdcOverride override = new MdcOverride();
196 Client client = setupClient();
198 baos = new ByteArrayOutputStream();
199 PrintStream ps = new PrintStream(baos);
200 if (logger.isDebugEnabled()) {
201 client.addFilter(new LoggingFilter(ps));
204 // Grab the current time so that we can use it for metrics purposes later.
205 startTimeInMs = System.currentTimeMillis();
206 override.addAttribute(MdcContext.MDC_START_TIME, dateFormatter.format(startTimeInMs));
208 if (useBasicAuth()) {
209 result = client.resource(url).queryParam(RESOURCE_VERSION_PARAM, resourceVersion)
210 .header(HEADER_TRANS_ID, transId).header(HEADER_FROM_APP_ID, ML_APP_NAME)
211 .header(HEADER_AUTHORIZATION, getAuthenticationCredentials())
212 .delete(ClientResponse.class);
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 .delete(ClientResponse.class);
218 } catch (Exception ex) {
219 logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "DELETE", url, ex.getLocalizedMessage());
222 if (logger.isDebugEnabled()) {
223 logger.debug(baos.toString());
227 logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS, "DELETE", url,
228 Integer.toString(result.getStatus()));
229 metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS,
230 new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus()).setField(
231 LogLine.DefinedFields.RESPONSE_DESCRIPTION, result.getResponseStatus().toString()),
232 override, "DELETE", url, Integer.toString(result.getStatus()));
238 * Send a GET request to the A&AI for a resource.
244 * @return ClientResponse
246 public ClientResponse getResource(String url, String transId, MimeType mimeType) {
247 ClientResponse result = null;
248 ByteArrayOutputStream baos = new ByteArrayOutputStream();
249 long startTimeInMs = 0;
250 MdcOverride override = new MdcOverride();
253 Client client = setupClient();
255 baos = new ByteArrayOutputStream();
256 PrintStream ps = new PrintStream(baos);
257 if (logger.isDebugEnabled()) {
258 client.addFilter(new LoggingFilter(ps));
261 // Grab the current time so that we can use it for metrics purposes later.
262 startTimeInMs = System.currentTimeMillis();
263 override.addAttribute(MdcContext.MDC_START_TIME, dateFormatter.format(startTimeInMs));
265 if (useBasicAuth()) {
266 result = client.resource(url).header(HEADER_TRANS_ID, transId)
267 .header(HEADER_FROM_APP_ID, ML_APP_NAME).accept(mimeType.getHttpHeaderType())
268 .header(HEADER_AUTHORIZATION, getAuthenticationCredentials()).get(ClientResponse.class);
270 result = client.resource(url).header(HEADER_TRANS_ID, transId)
271 .header(HEADER_FROM_APP_ID, ML_APP_NAME).accept(mimeType.getHttpHeaderType())
272 .get(ClientResponse.class);
275 } catch (Exception ex) {
276 logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "GET", url, ex.getLocalizedMessage());
279 if (logger.isDebugEnabled()) {
280 logger.debug(baos.toString());
284 logger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS, "GET", url,
285 Integer.toString(result.getStatus()));
286 metricsLogger.info(ModelLoaderMsgs.AAI_REST_REQUEST_SUCCESS,
287 new LogFields().setField(LogLine.DefinedFields.RESPONSE_CODE, result.getStatus()).setField(
288 LogLine.DefinedFields.RESPONSE_DESCRIPTION, result.getResponseStatus().toString()),
289 override, "GET", url, Integer.toString(result.getStatus()));
295 * Does a GET on a resource to retrieve the resource version, and then DELETE
302 * @return ClientResponse
304 public ClientResponse getAndDeleteResource(String url, String transId) {
305 // First, GET the model
306 ClientResponse getResponse = getResource(url, transId, MimeType.XML);
307 if ((getResponse == null) || (getResponse.getStatus() != Response.Status.OK.getStatusCode())) {
311 // Delete the model using the resource version in the response
312 String resVersion = null;
314 resVersion = getResourceVersion(getResponse);
315 } catch (Exception e) {
316 logger.error(ModelLoaderMsgs.AAI_REST_REQUEST_ERROR, "GET", url, e.getLocalizedMessage());
320 return deleteResource(url, resVersion, transId);
323 private Client setupClient() throws IOException, GeneralSecurityException {
324 ClientConfig clientConfig = new DefaultClientConfig();
326 HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
328 public boolean verify(String string, SSLSession ssls) {
333 // Create a trust manager that does not validate certificate chains
334 TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
336 public X509Certificate[] getAcceptedIssuers() {
341 public void checkClientTrusted(X509Certificate[] certs, String authType) {}
344 public void checkServerTrusted(X509Certificate[] certs, String authType) {}
347 SSLContext ctx = SSLContext.getInstance("TLS");
348 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
349 FileInputStream fin = new FileInputStream(config.getAaiKeyStorePath());
350 KeyStore ks = KeyStore.getInstance("PKCS12");
351 char[] pwd = config.getAaiKeyStorePassword().toCharArray();
355 ctx.init(kmf.getKeyManagers(), trustAllCerts, null);
356 clientConfig.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
357 new HTTPSProperties(new HostnameVerifier() {
359 public boolean verify(String theString, SSLSession sslSession) {
364 Client client = Client.create(clientConfig);
369 private String getResourceVersion(ClientResponse response)
370 throws ParserConfigurationException, SAXException, IOException {
371 String respData = response.getEntity(String.class);
373 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
374 DocumentBuilder builder = factory.newDocumentBuilder();
375 InputSource is = new InputSource(new StringReader(respData));
376 Document doc = builder.parse(is);
378 NodeList nodeList = doc.getDocumentElement().getChildNodes();
379 for (int i = 0; i < nodeList.getLength(); i++) {
380 Node currentNode = nodeList.item(i);
381 if (currentNode.getNodeName().equals(RESOURCE_VERSION_PARAM)) {
382 return currentNode.getTextContent();
389 private String getAuthenticationCredentials() {
391 String usernameAndPassword = config.getAaiAuthenticationUser() + ":"
392 + config.getAaiAuthenticationPassword();
393 return "Basic " + java.util.Base64.getEncoder().encodeToString(usernameAndPassword.getBytes());
396 public boolean useBasicAuth() {
397 return (config.getAaiAuthenticationUser() != null)
398 && (config.getAaiAuthenticationPassword() != null);