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=========================================================
20 package org.openecomp.sdc.be.servlets;
22 import com.fasterxml.jackson.databind.DeserializationFeature;
23 import com.fasterxml.jackson.databind.ObjectMapper;
24 import com.fasterxml.jackson.databind.module.SimpleModule;
25 import com.google.common.annotations.VisibleForTesting;
26 import com.google.gson.Gson;
27 import com.google.gson.GsonBuilder;
28 import com.google.gson.reflect.TypeToken;
29 import fj.data.Either;
30 import java.io.IOException;
31 import java.lang.reflect.Type;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
36 import java.util.Map.Entry;
37 import java.util.Objects;
39 import java.util.function.Supplier;
40 import javax.servlet.ServletContext;
41 import javax.servlet.http.HttpServletRequest;
42 import javax.ws.rs.core.Context;
43 import javax.ws.rs.core.Response;
44 import javax.ws.rs.core.Response.ResponseBuilder;
45 import org.json.simple.JSONArray;
46 import org.json.simple.JSONObject;
47 import org.json.simple.parser.JSONParser;
48 import org.json.simple.parser.ParseException;
49 import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
50 import org.openecomp.sdc.be.components.impl.BaseBusinessLogic;
51 import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
52 import org.openecomp.sdc.be.components.impl.ElementBusinessLogic;
53 import org.openecomp.sdc.be.components.impl.GenericArtifactBrowserBusinessLogic;
54 import org.openecomp.sdc.be.components.impl.InputsBusinessLogic;
55 import org.openecomp.sdc.be.components.impl.OutputsBusinessLogic;
56 import org.openecomp.sdc.be.components.impl.PolicyBusinessLogic;
57 import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
58 import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
59 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
60 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
61 import org.openecomp.sdc.be.config.BeEcompErrorManager;
62 import org.openecomp.sdc.be.dao.api.ActionStatus;
63 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
64 import org.openecomp.sdc.be.datatypes.enums.DeclarationTypeEnum;
65 import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition;
66 import org.openecomp.sdc.be.impl.ComponentsUtils;
67 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
68 import org.openecomp.sdc.be.model.ComponentInstInputsMap;
69 import org.openecomp.sdc.be.model.InputDefinition;
70 import org.openecomp.sdc.be.model.PropertyConstraint;
71 import org.openecomp.sdc.be.model.PropertyDefinition;
72 import org.openecomp.sdc.be.model.User;
73 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
74 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintJacksonDeserializer;
75 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
76 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
77 import org.openecomp.sdc.be.user.UserBusinessLogic;
78 import org.openecomp.sdc.common.api.Constants;
79 import org.openecomp.sdc.common.log.wrappers.Logger;
80 import org.openecomp.sdc.common.servlets.BasicServlet;
81 import org.openecomp.sdc.exception.ResponseFormat;
82 import org.springframework.web.context.WebApplicationContext;
84 public class BeGenericServlet extends BasicServlet {
86 private static final Logger log = Logger.getLogger(BeGenericServlet.class);
87 private static final String PROPERTY_NAME_REGEX = "[\\w,\\d,_:]+";
89 protected HttpServletRequest servletRequest;
90 protected ComponentsUtils componentsUtils;
92 public BeGenericServlet(ComponentsUtils componentsUtils) {
93 this.componentsUtils = componentsUtils;
96 private static Response buildOkResponseStatic(Object entity) {
97 return Response.status(Response.Status.OK).entity(entity).build();
100 /******************** New error response mechanism
101 * @param requestErrorWrapper **************/
102 protected Response buildErrorResponse(ResponseFormat requestErrorWrapper) {
103 return Response.status(requestErrorWrapper.getStatus()).entity(gson.toJson(requestErrorWrapper.getRequestError())).build();
106 protected Response buildGeneralErrorResponse() {
107 return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
110 protected Response buildOkResponse(Object entity) {
111 return buildOkResponseStatic(entity);
114 public HttpServletRequest getServletRequest() {
115 return servletRequest;
119 public void setRequestServlet(HttpServletRequest request) {
120 this.servletRequest = request;
123 protected Response buildOkResponse(ResponseFormat errorResponseWrapper, Object entity) {
124 return buildOkResponse(errorResponseWrapper, entity, null);
127 protected Response buildOkResponse(ResponseFormat errorResponseWrapper, Object entity, Map<String, String> additionalHeaders) {
128 int status = errorResponseWrapper.getStatus();
129 ResponseBuilder responseBuilder = Response.status(status);
130 if (entity != null) {
131 if (log.isTraceEnabled()) {
132 log.trace("returned entity is {}", entity.toString());
134 responseBuilder = responseBuilder.entity(entity);
136 if (additionalHeaders != null) {
137 for (Entry<String, String> additionalHeader : additionalHeaders.entrySet()) {
138 String headerName = additionalHeader.getKey();
139 String headerValue = additionalHeader.getValue();
140 if (log.isTraceEnabled()) {
141 log.trace("Adding header {} with value {} to the response", headerName, headerValue);
143 responseBuilder.header(headerName, headerValue);
146 return responseBuilder.build();
149 /*******************************************************************************************************/
150 protected Either<User, ResponseFormat> getUser(final HttpServletRequest request, String userId) {
153 user = getUserAdminManager(request.getSession().getServletContext()).getUser(userId, false);
154 return Either.left(user);
155 } catch (ComponentException ce) {
156 log.info("createResource method - user is not listed. userId= {}", userId);
157 ResponseFormat errorResponse = getComponentsUtils().getResponseFormat(ce);
158 user = new User("", "", userId, "", null, null);
159 getComponentsUtils().auditResource(errorResponse, user, "", AuditingActionEnum.CHECKOUT_RESOURCE);
160 return Either.right(errorResponse);
164 UserBusinessLogic getUserAdminManager(ServletContext context) {
165 return getClassFromWebAppContext(context, () -> UserBusinessLogic.class);
168 protected GenericArtifactBrowserBusinessLogic getGenericArtifactBrowserBL(ServletContext context) {
169 return getClassFromWebAppContext(context, () -> GenericArtifactBrowserBusinessLogic.class);
172 protected ResourceBusinessLogic getResourceBL(ServletContext context) {
173 return getClassFromWebAppContext(context, () -> ResourceBusinessLogic.class);
176 protected ServiceBusinessLogic getServiceBL(ServletContext context) {
177 return getClassFromWebAppContext(context, () -> ServiceBusinessLogic.class);
180 protected ArtifactsBusinessLogic getArtifactBL(ServletContext context) {
181 return getClassFromWebAppContext(context, () -> ArtifactsBusinessLogic.class);
184 protected ElementBusinessLogic getElementBL(ServletContext context) {
185 return getClassFromWebAppContext(context, () -> ElementBusinessLogic.class);
188 <T> T getClassFromWebAppContext(final ServletContext context, final Supplier<Class<T>> businessLogicClassGen) {
189 return getWebAppContext(context).getBean(businessLogicClassGen.get());
192 protected ComponentInstanceBusinessLogic getComponentInstanceBL(final ServletContext context) {
193 return getClassFromWebAppContext(context, () -> ComponentInstanceBusinessLogic.class);
196 protected ComponentsUtils getComponentsUtils() {
197 final ServletContext context = this.servletRequest.getSession().getServletContext();
198 return getClassFromWebAppContext(context, () -> ComponentsUtils.class);
202 * Used to support Unit Test.<br> Header Params are not supported in Unit Tests
206 String initHeaderParam(String headerValue, HttpServletRequest request, String headerName) {
208 if (headerValue != null) {
209 retValue = headerValue;
211 retValue = request.getHeader(headerName);
216 protected String getContentDispositionValue(String artifactFileName) {
217 return new StringBuilder().append("attachment; filename=\"").append(artifactFileName).append("\"").toString();
220 <T> T convertJsonToObjectOfClass(String json, Class<T> clazz) {
222 ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
223 .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
225 log.trace("Starting to convert json to object. Json=\n{}", json);
226 SimpleModule module = new SimpleModule("customDeserializationModule");
227 module.addDeserializer(PropertyConstraint.class, new PropertyConstraintJacksonDeserializer());
228 mapper.registerModule(module);
229 object = mapper.readValue(json, clazz);
230 if (object != null) {
233 BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
234 log.debug("The object of class {} is null after converting from json. ", clazz);
235 throw new ByActionStatusComponentException(ActionStatus.INVALID_CONTENT);
237 } catch (IOException e) {
238 BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
239 log.debug("The exception {} occurred upon json to object convertation. Json=\n{}", e, json);
240 throw new ByActionStatusComponentException(ActionStatus.INVALID_CONTENT);
244 protected Either<Map<String, PropertyDefinition>, ActionStatus> getPropertyModel(String componentId, String data) {
245 JSONParser parser = new JSONParser();
248 Map<String, PropertyDefinition> properties = new HashMap<>();
249 root = (JSONObject) parser.parse(data);
250 Set entrySet = root.entrySet();
251 Iterator iterator = entrySet.iterator();
252 while (iterator.hasNext()) {
253 Entry next = (Entry) iterator.next();
254 String propertyName = (String) next.getKey();
255 if (!isPropertyNameValid(propertyName)) {
256 return Either.right(ActionStatus.INVALID_PROPERTY_NAME);
258 JSONObject value = (JSONObject) next.getValue();
259 Either<PropertyDefinition, ActionStatus> propertyDefinitionEither = getPropertyDefinitionFromJson(componentId, propertyName, value);
260 if (propertyDefinitionEither.isRight()) {
261 return Either.right(propertyDefinitionEither.right().value());
263 properties.put(propertyName, propertyDefinitionEither.left().value());
265 return Either.left(properties);
266 } catch (ParseException e) {
267 log.info("Property conetnt is invalid - {}", data);
268 return Either.right(ActionStatus.INVALID_CONTENT);
272 protected boolean isPropertyNameValid(String propertyName) {
273 return Objects.nonNull(propertyName) && propertyName.matches(PROPERTY_NAME_REGEX);
276 private Either<PropertyDefinition, ActionStatus> getPropertyDefinitionFromJson(String componentId, String propertyName, JSONObject value) {
277 String jsonString = value.toJSONString();
278 Either<PropertyDefinition, ActionStatus> convertJsonToObject = convertJsonToObject(jsonString, PropertyDefinition.class);
279 if (convertJsonToObject.isRight()) {
280 return Either.right(convertJsonToObject.right().value());
282 PropertyDefinition propertyDefinition = convertJsonToObject.left().value();
283 String uniqueId = UniqueIdBuilder.buildPropertyUniqueId(componentId, propertyName);
284 propertyDefinition.setUniqueId(uniqueId);
285 return Either.left(propertyDefinition);
288 private Either<InputDefinition, ActionStatus> getInputDefinitionFromJson(String componentId, String inputName, JSONObject value) {
289 String jsonString = value.toJSONString();
290 Either<InputDefinition, ActionStatus> convertJsonToObject = convertJsonToObject(jsonString, InputDefinition.class);
291 if (convertJsonToObject.isRight()) {
292 return Either.right(convertJsonToObject.right().value());
294 InputDefinition inputDefinition = convertJsonToObject.left().value();
295 String uniqueId = UniqueIdBuilder.buildPropertyUniqueId(componentId, inputName);
296 inputDefinition.setUniqueId(uniqueId);
297 return Either.left(inputDefinition);
300 protected Either<Map<String, PropertyDefinition>, ActionStatus> getPropertiesListForUpdate(String data) {
301 Map<String, PropertyDefinition> properties = new HashMap<>();
302 JSONParser parser = new JSONParser();
305 jsonArray = (JSONArray) parser.parse(data);
306 for (Object jsonElement : jsonArray) {
307 String propertyAsString = jsonElement.toString();
308 Either<PropertyDefinition, ActionStatus> convertJsonToObject = convertJsonToObject(propertyAsString, PropertyDefinition.class);
309 if (convertJsonToObject.isRight()) {
310 return Either.right(convertJsonToObject.right().value());
312 PropertyDefinition propertyDefinition = convertJsonToObject.left().value();
313 properties.put(propertyDefinition.getName(), propertyDefinition);
315 return Either.left(properties);
316 } catch (Exception e) {
317 log.info("Property content is invalid - {}", data);
318 return Either.right(ActionStatus.INVALID_CONTENT);
322 protected String propertyToJson(Map.Entry<String, PropertyDefinition> property) {
323 JSONObject root = new JSONObject();
324 String propertyName = property.getKey();
325 PropertyDefinition propertyDefinition = property.getValue();
326 JSONObject propertyDefinitionO = getPropertyDefinitionJSONObject(propertyDefinition);
327 root.put(propertyName, propertyDefinitionO);
328 propertyDefinition.getType();
329 return root.toString();
332 private JSONObject getPropertyDefinitionJSONObject(PropertyDefinition propertyDefinition) {
333 Either<String, ActionStatus> either = convertObjectToJson(propertyDefinition);
334 if (either.isRight()) {
335 return new JSONObject();
338 return (JSONObject) new JSONParser().parse(either.left().value());
339 } catch (ParseException e) {
340 log.info("failed to convert input to json");
341 log.error("failed to convert to json", e);
342 return new JSONObject();
346 protected <T> Either<T, ActionStatus> convertJsonToObject(String data, Class<T> clazz) {
348 Type constraintType = new TypeToken<PropertyConstraint>() {
350 Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyOperation.PropertyConstraintDeserialiser()).create();
352 log.trace("convert json to object. json=\n {}", data);
353 t = gson.fromJson(data, clazz);
355 log.info("object is null after converting from json");
356 return Either.right(ActionStatus.INVALID_CONTENT);
358 } catch (Exception e) {
360 log.info("failed to convert from json");
361 log.error("failed to convert from json", e);
362 return Either.right(ActionStatus.INVALID_CONTENT);
364 return Either.left(t);
367 private Either<String, ActionStatus> convertObjectToJson(PropertyDefinition propertyDefinition) {
368 Type constraintType = new TypeToken<PropertyConstraint>() {
370 Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyOperation.PropertyConstraintSerialiser()).create();
372 log.trace("convert object to json. propertyDefinition= {}", propertyDefinition);
373 String json = gson.toJson(propertyDefinition);
375 log.info("object is null after converting to json");
376 return Either.right(ActionStatus.INVALID_CONTENT);
378 return Either.left(json);
379 } catch (Exception e) {
381 log.info("failed to convert to json");
382 log.debug("failed to convert fto json", e);
383 return Either.right(ActionStatus.INVALID_CONTENT);
387 private OutputsBusinessLogic getOutputBL(final ServletContext context) {
388 return getClassFromWebAppContext(context, () -> OutputsBusinessLogic.class);
391 private InputsBusinessLogic getInputBL(final ServletContext context) {
392 return getClassFromWebAppContext(context, () -> InputsBusinessLogic.class);
395 private PolicyBusinessLogic getPolicyBL(final ServletContext context) {
396 return getClassFromWebAppContext(context, () -> PolicyBusinessLogic.class);
399 private WebApplicationContext getWebAppContext(final ServletContext context) {
400 return ((WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)).getWebAppContext(context);
403 protected <T> Either<T, ResponseFormat> parseToComponentInstanceMap(final String componentJson, final User user,
404 final ComponentTypeEnum componentType, final Class<T> clazz) {
405 return getComponentsUtils()
406 .convertJsonToObjectUsingObjectMapper(componentJson, user, clazz, AuditingActionEnum.CREATE_RESOURCE, componentType);
409 protected Response declareProperties(String userId, String componentId, String componentType, String componentInstInputsMapObj,
410 DeclarationTypeEnum typeEnum, HttpServletRequest request) {
411 ServletContext context = request.getSession().getServletContext();
412 String url = request.getMethod() + " " + request.getRequestURI();
413 log.debug("(get) Start handle request of {}", url);
415 BaseBusinessLogic businessLogic = getBlForDeclaration(typeEnum, context);
417 User modifier = new User();
418 modifier.setUserId(userId);
419 log.debug("modifier id is {}", userId);
420 ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
421 Either<ComponentInstInputsMap, ResponseFormat> componentInstInputsMapRes = parseToComponentInstanceMap(componentInstInputsMapObj,
422 modifier, componentTypeEnum, ComponentInstInputsMap.class);
423 if (componentInstInputsMapRes.isRight()) {
424 log.debug("failed to parse componentInstInputsMap");
425 return buildErrorResponse(componentInstInputsMapRes.right().value());
427 Either<List<ToscaDataDefinition>, ResponseFormat> propertiesAfterDeclaration = businessLogic
428 .declareProperties(userId, componentId, componentTypeEnum, componentInstInputsMapRes.left().value());
429 if (propertiesAfterDeclaration.isRight()) {
430 log.debug("failed to create inputs for service: {}", componentId);
431 return buildErrorResponse(propertiesAfterDeclaration.right().value());
433 Object properties = RepresentationUtils.toRepresentation(propertiesAfterDeclaration.left().value());
434 return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), properties);
435 } catch (Exception e) {
436 BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create inputs for service with id: " + componentId);
437 log.debug("Properties declaration failed with exception", e);
438 return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
442 public BaseBusinessLogic getBlForDeclaration(final DeclarationTypeEnum typeEnum, final ServletContext context) {
445 return getOutputBL(context);
447 return getPolicyBL(context);
449 return getInputBL(context);
451 throw new IllegalArgumentException("Invalid DeclarationTypeEnum");
455 protected Either<Map<String, InputDefinition>, ActionStatus> getInputModel(String componentId, String data) {
456 JSONParser parser = new JSONParser();
459 Map<String, InputDefinition> inputs = new HashMap<>();
460 root = (JSONObject) parser.parse(data);
461 Set entrySet = root.entrySet();
462 Iterator iterator = entrySet.iterator();
463 while (iterator.hasNext()) {
464 Entry next = (Entry) iterator.next();
465 String inputName = (String) next.getKey();
466 if (!isInputNameValid(inputName)) {
467 return Either.right(ActionStatus.INVALID_INPUT_NAME);
469 JSONObject value = (JSONObject) next.getValue();
470 Either<InputDefinition, ActionStatus> inputDefinitionEither = getInputDefinitionFromJson(componentId, inputName, value);
471 if (inputDefinitionEither.isRight()) {
472 return Either.right(inputDefinitionEither.right().value());
474 inputs.put(inputName, inputDefinitionEither.left().value());
476 return Either.left(inputs);
477 } catch (ParseException e) {
478 log.warn("Input content is invalid - {}", data, e);
479 return Either.right(ActionStatus.INVALID_CONTENT);
483 protected boolean isInputNameValid(String inputName) {
484 return Objects.nonNull(inputName) && inputName.matches(PROPERTY_NAME_REGEX);