54b915fe9bac4a3656243c0ef4d593c4d6d47e6a
[portal.git] / ecomp-portal-BE-common / src / main / java / org / onap / portalapp / portal / service / ApplicationsRestClientServiceImpl.java
1 /*-
2  * ============LICENSE_START==========================================
3  * ONAP Portal
4  * ===================================================================
5  * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ===================================================================
7  *
8  * Unless otherwise specified, all software contained herein is licensed
9  * under the Apache License, Version 2.0 (the "License");
10  * you may not use this software 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  *
21  * Unless otherwise specified, all documentation contained herein is licensed
22  * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
23  * you may not use this documentation except in compliance with the License.
24  * You may obtain a copy of the License at
25  *
26  *             https://creativecommons.org/licenses/by/4.0/
27  *
28  * Unless required by applicable law or agreed to in writing, documentation
29  * distributed under the License is distributed on an "AS IS" BASIS,
30  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31  * See the License for the specific language governing permissions and
32  * limitations under the License.
33  *
34  * ============LICENSE_END============================================
35  *
36  * 
37  */
38 package org.onap.portalapp.portal.service;
39
40 import static com.att.eelf.configuration.Configuration.MDC_KEY_REQUEST_ID;
41
42 import java.lang.reflect.Type;
43 import java.net.MalformedURLException;
44 import java.net.URL;
45 import java.util.Base64;
46 import java.util.Date;
47
48 import javax.annotation.PostConstruct;
49 import javax.servlet.http.HttpServletResponse;
50 import javax.ws.rs.core.MediaType;
51 import javax.ws.rs.core.Response;
52
53 import org.apache.commons.lang.StringUtils;
54 import org.apache.cxf.jaxrs.client.WebClient;
55 import org.apache.cxf.jaxrs.impl.ResponseImpl;
56 import org.apache.cxf.transport.http.HTTPException;
57 import org.onap.portalapp.portal.domain.EPApp;
58 import org.onap.portalapp.portal.logging.aop.EPAuditLog;
59 import org.onap.portalapp.portal.logging.aop.EPMetricsLog;
60 import org.onap.portalapp.portal.logging.format.EPAppMessagesEnum;
61 import org.onap.portalapp.portal.logging.logic.EPLogUtil;
62 import org.onap.portalapp.portal.utils.EPCommonSystemProperties;
63 import org.onap.portalapp.portal.utils.EcompPortalUtils;
64 import org.onap.portalapp.util.SystemType;
65 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
66 import org.onap.portalsdk.core.onboarding.util.CipherUtil;
67 import org.onap.portalsdk.core.util.SystemProperties;
68 import org.slf4j.MDC;
69 import org.springframework.beans.factory.annotation.Autowired;
70 import org.springframework.context.annotation.EnableAspectJAutoProxy;
71 import org.springframework.stereotype.Service;
72
73 import com.fasterxml.jackson.databind.ObjectMapper;
74 import com.google.gson.Gson;
75 import com.google.gson.GsonBuilder;
76 import com.google.gson.JsonDeserializationContext;
77 import com.google.gson.JsonDeserializer;
78 import com.google.gson.JsonElement;
79 import com.google.gson.JsonParseException;
80
81 @Service("applicationsRestClientService")
82 @org.springframework.context.annotation.Configuration
83 @EnableAspectJAutoProxy
84 @EPAuditLog
85 public class ApplicationsRestClientServiceImpl implements ApplicationsRestClientService {
86
87         private static final String PASSWORD_HEADER = "password";
88
89         private static final String APP_USERNAME_HEADER = "username";
90
91         private static final String BASIC_AUTHENTICATION_HEADER = "Authorization";
92
93         private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(ApplicationsRestClientServiceImpl.class);
94
95         @Autowired
96         private AppsCacheService appsCacheService;
97
98         Gson gson = null;
99
100         private final ObjectMapper mapper = new ObjectMapper();
101
102         @PostConstruct
103         private void init() {
104                 logger.debug(EELFLoggerDelegate.debugLogger, "initializing");
105                 GsonBuilder builder = new GsonBuilder();
106
107                 // Register an adapter to manage the date types as long values
108                 builder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
109                         public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
110                                         throws JsonParseException {
111                                 return new Date(json.getAsJsonPrimitive().getAsLong());
112                         }
113                 });
114
115                 gson = builder.create();
116         }
117
118         // TODO: do we need to do additional logging for remote API calls?
119         private static WebClient createClientForPath(String baseUri, String path) {
120                 logger.info(EELFLoggerDelegate.debugLogger, "Creating web client for " + baseUri + "   +   " + path);
121                 WebClient client = WebClient.create(baseUri);
122                 client.type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON);
123                 client.path(path);
124                 return client;
125         }
126
127         @EPMetricsLog
128         private void verifyResponse(Response response) throws HTTPException {
129                 int status = response.getStatus();
130                 logger.debug(EELFLoggerDelegate.debugLogger, "http response status=" + status);
131                 MDC.put(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE, Integer.toString(status));
132                 if (!isHttpSuccess(status)) {
133                         String errMsg = "Failed. Status=" + status + "; [" + ((ResponseImpl)response).getStatusInfo().getReasonPhrase().toString()
134                                         + "]";
135                         URL url = null;
136                         try {
137                                 // must not be null to avoid NPE in HTTPException constructor
138                                 url = new URL("http://null");
139                                 if (((ResponseImpl)response).getLocation() != null)
140                                         url = ((ResponseImpl)response).getLocation().toURL();
141                         } catch (MalformedURLException e) {
142                                 // never mind. it is only for the debug message.
143                                 logger.warn(EELFLoggerDelegate.errorLogger, "Failed to build URL", e);
144                         }
145                         logger.error(EELFLoggerDelegate.errorLogger, "http response failed. " + errMsg + "; url=" + url);
146                         EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeIncorrectHttpStatusError);
147                         throw new HTTPException(status, errMsg, url);
148                 }
149         }
150
151         private static boolean isHttpSuccess(int status) {
152                 return status / 100 == 2;
153         }
154         
155         
156         private WebClient createClientForApp(long appId, String restPath) {
157                 return createClientFor(appId, restPath, SystemType.APPLICATION);
158         }
159
160         //TODO Need to implement the mylogins once the endpoint is confirmed
161         @EPMetricsLog
162         private WebClient createClientFor(long appSystemId, String restPath, SystemType type) {
163                 logger.debug(EELFLoggerDelegate.debugLogger, "creating client for appId=" + appSystemId + "; restPath=" + restPath);
164                 EPApp externalApp = null;
165                 
166                 if(type == SystemType.APPLICATION){
167                 externalApp = appsCacheService.getApp(appSystemId);
168                 }else{
169                         // TO DO 
170                 }
171                 
172                 if (externalApp != null) {
173                         String appBaseUri = (type == SystemType.APPLICATION) ? externalApp.getAppRestEndpoint() : "";
174                         String username = (type == SystemType.APPLICATION) ? externalApp.getUsername(): "";
175                         String encriptedPwd = (type == SystemType.APPLICATION) ? externalApp.getAppPassword(): "";
176                         String appName = (type == SystemType.APPLICATION) ? externalApp.getName(): "";
177                         String decreptedAppPwd = StringUtils.EMPTY;
178
179                         // Set local context
180                         MDC.put(EPCommonSystemProperties.PROTOCOL, EPCommonSystemProperties.HTTP);
181                         if (appBaseUri != null && appBaseUri.contains("https")) {
182                                 MDC.put(EPCommonSystemProperties.PROTOCOL, EPCommonSystemProperties.HTTPS);
183                         }
184                         MDC.put(EPCommonSystemProperties.FULL_URL, appBaseUri + restPath);
185                         MDC.put(EPCommonSystemProperties.TARGET_ENTITY, appName);
186                         MDC.put(EPCommonSystemProperties.TARGET_SERVICE_NAME, restPath);
187
188                         try {
189                                 decreptedAppPwd = CipherUtil.decryptPKC(encriptedPwd,
190                                                 SystemProperties.getProperty(SystemProperties.Decryption_Key));
191                         } catch (Exception e) {
192                                 logger.error(EELFLoggerDelegate.errorLogger, "createClientFor failed to decrypt", e);
193                         }
194                         
195                         WebClient client = createClientForPath(appBaseUri, restPath);
196
197                         // support basic authentication for some partners
198                         String encoding = Base64.getEncoder().encodeToString((username + ":" + decreptedAppPwd).getBytes());
199                         String encodingStr = "Basic " + encoding;
200                         client.header(BASIC_AUTHENTICATION_HEADER, encodingStr);
201
202                         // But still keep code downward compatible for non compliant apps
203                         client.header(APP_USERNAME_HEADER, username);
204                         client.header(PASSWORD_HEADER, decreptedAppPwd);
205
206                         client.header(SystemProperties.ECOMP_REQUEST_ID, MDC.get(MDC_KEY_REQUEST_ID));
207                         client.header(SystemProperties.USERAGENT_NAME, EPCommonSystemProperties.ECOMP_PORTAL_BE);
208                         
209                         logger.debug(EELFLoggerDelegate.debugLogger,
210                                         String.format("App %d found, baseUri=[%s], Headers: [%s=%s]", appSystemId, appBaseUri,
211                                                         APP_USERNAME_HEADER, username));
212
213                         return client;
214                 }
215                 return null;
216         }
217
218         @Override
219         public <T> T get(Class<T> clazz, long appId, String restPath) throws HTTPException {
220                 T t = null;
221                 Response response = getResponse(appId, restPath);
222
223                 if (response != null) {
224                         verifyResponse(response);
225                         
226                         /* It is not recommendable to use the implementation class org.apache.cxf.jaxrs.impl.ResponseImpl in the code, 
227                         but had to force this in-order to prevent conflict with the ResponseImpl class of Jersey Client which 
228                         doesn't work as expected. Created Portal-253 for tracking */
229                         String str = ((ResponseImpl)response).readEntity(String.class);
230                         
231                         EcompPortalUtils.logAndSerializeObject(logger, restPath, "GET result =", str);
232                         try {
233                                 t = gson.fromJson(str, clazz);
234                         } catch (Exception e) {
235                                 EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e);
236                         }
237                 }
238
239                 return t;
240         }
241         
242         @Override
243         public String getIncomingJsonString(long appId, String restPath) throws HTTPException {
244                 Response response = getResponse(appId, restPath);
245
246                 if (response != null) {
247                         verifyResponse(response);
248                         
249                         /* It is not recommendable to use the implementation class org.apache.cxf.jaxrs.impl.ResponseImpl in the code, 
250                         but had to force this in-order to prevent conflict with the ResponseImpl class of Jersey Client which 
251                         doesn't work as expected. Created Portal-253 for tracking  */
252                         String incomingJson = ((ResponseImpl)response).readEntity(String.class);
253                         return incomingJson;
254                 }
255                 
256                 return "";
257         }
258
259
260         /*
261          * (non-Javadoc)
262          * 
263          * @see
264          * org.onap.portalapp.portal.service.ApplicationsRestClientService#get(
265          * java.lang.Class, long, java.lang.String, boolean)
266          */
267         @Override
268         public <T> T get(Class<T> clazz, long appId, String restPath, boolean useJacksonMapper) throws HTTPException {
269
270                 if (!useJacksonMapper)
271                         return get(clazz, appId, restPath);
272
273                 T t = null;
274                 Response response = getResponse(appId, restPath);
275
276                 if (response != null) {
277                         verifyResponse(response);
278                         String str = ((ResponseImpl)response).readEntity(String.class);
279                         EcompPortalUtils.logAndSerializeObject(logger, restPath, "GET result =", str);
280
281                         try {
282                                 t = mapper.readValue(str, clazz);
283                         } catch (Exception e) {
284                                 e.printStackTrace();
285                                 EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e);
286                         }
287                 }
288
289                 return t;
290         }
291
292         protected Response getResponse(long appId, String restPath) {
293                 WebClient webClient = null;
294                 Response response = null;
295
296                 webClient = createClientForApp(appId, restPath);
297                 EcompPortalUtils.logAndSerializeObject(logger, restPath, "GET request =", "no-payload");
298
299                 try {
300                         if (webClient != null) {
301                                 response = webClient.get();
302                         } else {
303                                 logger.error(EELFLoggerDelegate.errorLogger,
304                                                 "Unable to create the Webclient to make the '" + restPath + "' API call.");
305                         }
306                 } catch (Exception e) {
307                         MDC.put(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE,
308                                         Integer.toString(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
309                         EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiGeneralError, e);
310                         logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while making the GET REST API call", e);
311                 }
312                 return response;
313         }
314         
315         
316         @Override
317         public <T> T post(Class<T> clazz, long appId, Object payload, String restPath, SystemType type) throws HTTPException {
318                 WebClient client = null;
319                 Response response = null;
320                 T t = null;
321
322                 client = createClientFor(appId, restPath, type);
323                 EcompPortalUtils.logAndSerializeObject(logger, restPath, "POST request =", payload);
324
325                 try {
326                         if (client != null) {
327                                 response = client.post(payload);
328                         } else {
329                                 logger.error(EELFLoggerDelegate.errorLogger,
330                                                 "Unable to create the Webclient to make the '" + restPath + "' API call.");
331                         }
332                 } catch (Exception e) {
333                         MDC.put(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE,
334                                         Integer.toString(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
335                         EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiGeneralError, e);
336                         logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while making the POST REST API call", e);
337                 }
338
339                 if (response != null) {
340                         verifyResponse(response);
341
342                         // String contentType = response.getHeaderString("Content-Type");
343                         if (clazz != null) {
344                                 String str = ((ResponseImpl)response).readEntity(String.class);
345                                 EcompPortalUtils.logAndSerializeObject(logger, restPath, "POST result =", str);
346                                 try {
347                                         t = gson.fromJson(str, clazz);
348                                 } catch (Exception e) {
349                                         EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e);
350                                 }
351                         }
352                 }
353                 return t;
354         }
355
356         @Override
357         public <T> T post(Class<T> clazz, long appId, Object payload, String restPath) throws HTTPException {
358                 return post( clazz, appId, payload, restPath, SystemType.APPLICATION);
359         }
360
361         
362         @Override
363         public <T> T put(Class<T> clazz, long appId, Object payload, String restPath) throws HTTPException {
364                 WebClient client = null;
365                 Response response = null;
366                 T t = null;
367
368                 logger.debug(EELFLoggerDelegate.debugLogger, "Entering to createClientForApp method for payload: {} and restPath: {} and appId: {}", payload.toString(), restPath, appId);
369
370                 client = createClientForApp(appId, restPath);
371                 EcompPortalUtils.logAndSerializeObject(logger, restPath, "PUT request =", payload);
372                 
373                 logger.debug(EELFLoggerDelegate.debugLogger, "Finished createClientForApp method for payload: {} and restPath: {} and appId: {}", payload.toString(), restPath, appId);
374
375                 try {
376                         if (client != null) {
377                                 logger.debug(EELFLoggerDelegate.debugLogger, "Entering to PUT for payload: {} and restPath: {} and appId: {}", payload.toString(), restPath, appId);
378
379                                 response = client.put(payload);
380                                 
381                                 logger.debug(EELFLoggerDelegate.debugLogger, "Finished to PUT for payload: {} and restPath: {} and appId: {}", payload.toString(), restPath, appId);
382
383                         } else {
384                                 logger.error(EELFLoggerDelegate.errorLogger,
385                                                 "Unable to create the Webclient to make the '" + restPath + "' API call.");
386                         }
387                 } catch (Exception e) {
388                         MDC.put(EPCommonSystemProperties.EXTERNAL_API_RESPONSE_CODE,
389                                         Integer.toString(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
390                         EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeRestApiGeneralError, e);
391                         logger.error(EELFLoggerDelegate.errorLogger, "Exception occurred while making the PUT REST API call", e);
392                 }
393
394                 if (response != null) {
395                         verifyResponse(response);
396                         String str = ((ResponseImpl)response).readEntity(String.class);
397                         EcompPortalUtils.logAndSerializeObject(logger, restPath, "PUT result =", str);
398                         try {
399                                 t = gson.fromJson(str, clazz);
400                         } catch (Exception e) {
401                                 EPLogUtil.logEcompError(logger, EPAppMessagesEnum.BeInvalidJsonInput, e);
402                         }
403                 }
404                 return t;
405         }
406 }