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.sdc.be.servlets;
23 import com.fasterxml.jackson.databind.DeserializationFeature;
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import com.fasterxml.jackson.databind.module.SimpleModule;
26 import com.google.common.annotations.VisibleForTesting;
27 import com.google.gson.Gson;
28 import com.google.gson.GsonBuilder;
29 import com.google.gson.reflect.TypeToken;
30 import fj.data.Either;
31 import io.swagger.v3.oas.annotations.OpenAPIDefinition;
32 import io.swagger.v3.oas.annotations.info.Info;
33 import io.swagger.v3.oas.annotations.servers.Server;
34 import org.json.simple.JSONArray;
35 import org.json.simple.JSONObject;
36 import org.json.simple.parser.JSONParser;
37 import org.json.simple.parser.ParseException;
38 import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
39 import org.openecomp.sdc.be.components.impl.BaseBusinessLogic;
40 import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
41 import org.openecomp.sdc.be.components.impl.ElementBusinessLogic;
42 import org.openecomp.sdc.be.components.impl.GenericArtifactBrowserBusinessLogic;
43 import org.openecomp.sdc.be.components.impl.InputsBusinessLogic;
44 import org.openecomp.sdc.be.components.impl.OutputsBusinessLogic;
45 import org.openecomp.sdc.be.components.impl.PolicyBusinessLogic;
46 import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
47 import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
48 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
49 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
50 import org.openecomp.sdc.be.config.BeEcompErrorManager;
51 import org.openecomp.sdc.be.dao.api.ActionStatus;
52 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
53 import org.openecomp.sdc.be.datatypes.enums.DeclarationTypeEnum;
54 import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition;
55 import org.openecomp.sdc.be.impl.ComponentsUtils;
56 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
57 import org.openecomp.sdc.be.model.ComponentInstInputsMap;
58 import org.openecomp.sdc.be.model.PropertyConstraint;
59 import org.openecomp.sdc.be.model.PropertyDefinition;
60 import org.openecomp.sdc.be.model.InputDefinition;
61 import org.openecomp.sdc.be.model.User;
62 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
63 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintJacksonDeserializer;
64 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
65 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
66 import org.openecomp.sdc.be.user.UserBusinessLogic;
67 import org.openecomp.sdc.common.api.Constants;
68 import org.openecomp.sdc.common.log.wrappers.Logger;
69 import org.openecomp.sdc.common.servlets.BasicServlet;
70 import org.openecomp.sdc.exception.ResponseFormat;
71 import org.springframework.web.context.WebApplicationContext;
73 import javax.servlet.ServletContext;
74 import javax.servlet.http.HttpServletRequest;
75 import javax.ws.rs.core.Context;
76 import javax.ws.rs.core.Response;
77 import javax.ws.rs.core.Response.ResponseBuilder;
78 import java.io.IOException;
79 import java.lang.reflect.Type;
80 import java.util.HashMap;
81 import java.util.Iterator;
82 import java.util.List;
84 import java.util.Map.Entry;
85 import java.util.Objects;
87 import java.util.function.Supplier;
89 @OpenAPIDefinition(info = @Info(title = "SDC API", description = "SDC External, Distribution and Internal APIs"),
90 servers = {@Server(url = "/sdc", description = "SDC External and Distribution APIs"),
91 @Server(url = "/sdc2/rest", description = "SDC Internal APIs")})
92 public class BeGenericServlet extends BasicServlet {
94 public BeGenericServlet(UserBusinessLogic userAdminManager,
95 ComponentsUtils componentsUtils) {
96 this.userAdminManager = userAdminManager;
97 this.componentsUtils = componentsUtils;
101 protected HttpServletRequest servletRequest;
103 private static final Logger log = Logger.getLogger(BeGenericServlet.class);
105 private static final String PROPERTY_NAME_REGEX = "[\\w,\\d,_]+";
107 private UserBusinessLogic userAdminManager;
108 protected ComponentsUtils componentsUtils;
110 /******************** New error response mechanism
111 * @param requestErrorWrapper **************/
113 protected Response buildErrorResponse(ResponseFormat requestErrorWrapper) {
114 return Response.status(requestErrorWrapper.getStatus()).entity(gson.toJson(requestErrorWrapper.getRequestError())).build();
117 protected Response buildGeneralErrorResponse() {
118 return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
121 protected Response buildOkResponse(Object entity) {
122 return buildOkResponseStatic(entity);
125 private static Response buildOkResponseStatic(Object entity) {
126 return Response.status(Response.Status.OK)
131 public HttpServletRequest getServletRequest() {
132 return servletRequest;
136 public void setRequestServlet(HttpServletRequest request) {this.servletRequest = request;}
138 protected Response buildOkResponse(ResponseFormat errorResponseWrapper, Object entity) {
139 return buildOkResponse(errorResponseWrapper, entity, null);
142 protected Response buildOkResponse(ResponseFormat errorResponseWrapper, Object entity, Map<String, String> additionalHeaders) {
143 int status = errorResponseWrapper.getStatus();
144 ResponseBuilder responseBuilder = Response.status(status);
145 if (entity != null) {
146 if (log.isTraceEnabled())
147 log.trace("returned entity is {}", entity.toString());
148 responseBuilder = responseBuilder.entity(entity);
150 if (additionalHeaders != null) {
151 for (Entry<String, String> additionalHeader : additionalHeaders.entrySet()) {
152 String headerName = additionalHeader.getKey();
153 String headerValue = additionalHeader.getValue();
154 if (log.isTraceEnabled())
155 log.trace("Adding header {} with value {} to the response", headerName, headerValue);
156 responseBuilder.header(headerName, headerValue);
159 return responseBuilder.build();
162 /*******************************************************************************************************/
163 protected Either<User, ResponseFormat> getUser(final HttpServletRequest request, String userId) {
166 user = getUserAdminManager(request.getSession().getServletContext()).getUser(userId, false);
167 return Either.left(user);
168 } catch (ComponentException ce) {
169 log.info("createResource method - user is not listed. userId= {}", userId);
170 ResponseFormat errorResponse = getComponentsUtils().getResponseFormat(ce);
171 user = new User("", "", userId, "", null, null);
172 getComponentsUtils().auditResource(errorResponse, user, "", AuditingActionEnum.CHECKOUT_RESOURCE);
173 return Either.right(errorResponse);
177 UserBusinessLogic getUserAdminManager(ServletContext context) {
178 return getClassFromWebAppContext(context, () -> UserBusinessLogic.class);
181 protected GenericArtifactBrowserBusinessLogic getGenericArtifactBrowserBL(ServletContext context) {
182 return getClassFromWebAppContext(context, () -> GenericArtifactBrowserBusinessLogic.class);
185 protected ResourceBusinessLogic getResourceBL(ServletContext context) {
186 return getClassFromWebAppContext(context, () -> ResourceBusinessLogic.class);
189 protected ServiceBusinessLogic getServiceBL(ServletContext context) {
190 return getClassFromWebAppContext(context, () -> ServiceBusinessLogic.class);
193 protected ArtifactsBusinessLogic getArtifactBL(ServletContext context) {
194 return getClassFromWebAppContext(context, () -> ArtifactsBusinessLogic.class);
197 protected ElementBusinessLogic getElementBL(ServletContext context) {
198 return getClassFromWebAppContext(context, () -> ElementBusinessLogic.class);
201 <T> T getClassFromWebAppContext(final ServletContext context, final Supplier<Class<T>> businessLogicClassGen) {
202 return getWebAppContext(context).getBean(businessLogicClassGen.get());
205 protected ComponentInstanceBusinessLogic getComponentInstanceBL(final ServletContext context) {
206 return getClassFromWebAppContext(context, () -> ComponentInstanceBusinessLogic.class);
209 protected ComponentsUtils getComponentsUtils() {
210 final ServletContext context = this.servletRequest.getSession().getServletContext();
211 return getClassFromWebAppContext(context, () -> ComponentsUtils.class);
215 * Used to support Unit Test.<br>
216 * Header Params are not supported in Unit Tests
220 String initHeaderParam(String headerValue, HttpServletRequest request, String headerName) {
222 if (headerValue != null) {
223 retValue = headerValue;
225 retValue = request.getHeader(headerName);
230 protected String getContentDispositionValue(String artifactFileName) {
231 return new StringBuilder().append("attachment; filename=\"").append(artifactFileName).append("\"").toString();
234 <T> T convertJsonToObjectOfClass(String json, Class<T> clazz) {
236 ObjectMapper mapper = new ObjectMapper()
237 .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
238 .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
240 log.trace("Starting to convert json to object. Json=\n{}", json);
242 SimpleModule module = new SimpleModule("customDeserializationModule");
243 module.addDeserializer(PropertyConstraint.class, new PropertyConstraintJacksonDeserializer());
244 mapper.registerModule(module);
246 object = mapper.readValue(json, clazz);
247 if (object != null) {
250 BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
251 log.debug("The object of class {} is null after converting from json. ", clazz);
252 throw new ByActionStatusComponentException(ActionStatus.INVALID_CONTENT);
254 } catch (IOException e) {
255 BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
256 log.debug("The exception {} occurred upon json to object convertation. Json=\n{}", e, json);
257 throw new ByActionStatusComponentException(ActionStatus.INVALID_CONTENT);
261 protected Either<Map<String, PropertyDefinition>, ActionStatus> getPropertyModel(String componentId, String data) {
262 JSONParser parser = new JSONParser();
265 Map<String, PropertyDefinition> properties = new HashMap<>();
266 root = (JSONObject) parser.parse(data);
268 Set entrySet = root.entrySet();
269 Iterator iterator = entrySet.iterator();
270 while (iterator.hasNext()) {
271 Entry next = (Entry) iterator.next();
272 String propertyName = (String) next.getKey();
274 if(!isPropertyNameValid(propertyName)) {
275 return Either.right(ActionStatus.INVALID_PROPERTY_NAME);
278 JSONObject value = (JSONObject) next.getValue();
279 Either<PropertyDefinition, ActionStatus> propertyDefinitionEither =
280 getPropertyDefinitionFromJson(componentId, propertyName, value);
282 if(propertyDefinitionEither.isRight()) {
283 return Either.right(propertyDefinitionEither.right().value());
286 properties.put(propertyName, propertyDefinitionEither.left().value());
289 return Either.left(properties);
290 } catch (ParseException e) {
291 log.info("Property conetnt is invalid - {}", data);
292 return Either.right(ActionStatus.INVALID_CONTENT);
296 protected boolean isPropertyNameValid(String propertyName) {
297 return Objects.nonNull(propertyName)
298 && propertyName.matches(PROPERTY_NAME_REGEX);
302 private Either<PropertyDefinition, ActionStatus> getPropertyDefinitionFromJson(String componentId, String propertyName, JSONObject value) {
303 String jsonString = value.toJSONString();
304 Either<PropertyDefinition, ActionStatus> convertJsonToObject = convertJsonToObject(jsonString, PropertyDefinition.class);
305 if (convertJsonToObject.isRight()) {
306 return Either.right(convertJsonToObject.right().value());
308 PropertyDefinition propertyDefinition = convertJsonToObject.left().value();
309 String uniqueId = UniqueIdBuilder.buildPropertyUniqueId(componentId, propertyName);
310 propertyDefinition.setUniqueId(uniqueId);
312 return Either.left(propertyDefinition);
315 private Either<InputDefinition, ActionStatus> getInputDefinitionFromJson(String componentId, String inputName, JSONObject value) {
316 String jsonString = value.toJSONString();
317 Either<InputDefinition, ActionStatus> convertJsonToObject = convertJsonToObject(jsonString, InputDefinition.class);
318 if (convertJsonToObject.isRight()) {
319 return Either.right(convertJsonToObject.right().value());
321 InputDefinition inputDefinition = convertJsonToObject.left().value();
322 String uniqueId = UniqueIdBuilder.buildPropertyUniqueId(componentId, inputName);
323 inputDefinition.setUniqueId(uniqueId);
325 return Either.left(inputDefinition);
328 protected Either<Map<String, PropertyDefinition>, ActionStatus> getPropertiesListForUpdate(String data) {
330 Map<String, PropertyDefinition> properties = new HashMap<>();
331 JSONParser parser = new JSONParser();
335 jsonArray = (JSONArray) parser.parse(data);
336 for (Object jsonElement : jsonArray) {
337 String propertyAsString = jsonElement.toString();
338 Either<PropertyDefinition, ActionStatus> convertJsonToObject = convertJsonToObject(propertyAsString, PropertyDefinition.class);
340 if (convertJsonToObject.isRight()) {
341 return Either.right(convertJsonToObject.right().value());
344 PropertyDefinition propertyDefinition = convertJsonToObject.left().value();
345 properties.put(propertyDefinition.getName(), propertyDefinition);
348 return Either.left(properties);
349 } catch (Exception e) {
350 log.info("Property content is invalid - {}", data);
351 return Either.right(ActionStatus.INVALID_CONTENT);
356 protected String propertyToJson(Map.Entry<String, PropertyDefinition> property) {
357 JSONObject root = new JSONObject();
358 String propertyName = property.getKey();
359 PropertyDefinition propertyDefinition = property.getValue();
360 JSONObject propertyDefinitionO = getPropertyDefinitionJSONObject(propertyDefinition);
361 root.put(propertyName, propertyDefinitionO);
362 propertyDefinition.getType();
363 return root.toString();
366 private JSONObject getPropertyDefinitionJSONObject(PropertyDefinition propertyDefinition) {
368 Either<String, ActionStatus> either = convertObjectToJson(propertyDefinition);
369 if (either.isRight()) {
370 return new JSONObject();
373 return (JSONObject) new JSONParser().parse(either.left().value());
374 } catch (ParseException e) {
375 log.info("failed to convert input to json");
376 log.error("failed to convert to json", e);
377 return new JSONObject();
382 protected <T> Either<T, ActionStatus> convertJsonToObject(String data, Class<T> clazz) {
384 Type constraintType = new TypeToken<PropertyConstraint>() {
387 gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyOperation.PropertyConstraintDeserialiser()).create();
389 log.trace("convert json to object. json=\n {}", data);
390 t = gson.fromJson(data, clazz);
392 log.info("object is null after converting from json");
393 return Either.right(ActionStatus.INVALID_CONTENT);
395 } catch (Exception e) {
397 log.info("failed to convert from json");
398 log.error("failed to convert from json", e);
399 return Either.right(ActionStatus.INVALID_CONTENT);
401 return Either.left(t);
404 private Either<String, ActionStatus> convertObjectToJson(PropertyDefinition propertyDefinition) {
405 Type constraintType = new TypeToken<PropertyConstraint>() {
407 Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyOperation.PropertyConstraintSerialiser()).create();
409 log.trace("convert object to json. propertyDefinition= {}", propertyDefinition);
410 String json = gson.toJson(propertyDefinition);
412 log.info("object is null after converting to json");
413 return Either.right(ActionStatus.INVALID_CONTENT);
415 return Either.left(json);
416 } catch (Exception e) {
418 log.info("failed to convert to json");
419 log.debug("failed to convert fto json", e);
420 return Either.right(ActionStatus.INVALID_CONTENT);
425 private OutputsBusinessLogic getOutputBL(final ServletContext context) {
426 return getClassFromWebAppContext(context, () -> OutputsBusinessLogic.class);
429 private InputsBusinessLogic getInputBL(final ServletContext context) {
430 return getClassFromWebAppContext(context, () -> InputsBusinessLogic.class);
433 private PolicyBusinessLogic getPolicyBL(final ServletContext context) {
434 return getClassFromWebAppContext(context, () -> PolicyBusinessLogic.class);
437 private WebApplicationContext getWebAppContext(final ServletContext context) {
438 return ((WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)).getWebAppContext(context);
441 protected <T> Either<T, ResponseFormat> parseToComponentInstanceMap(final String componentJson,
443 final ComponentTypeEnum componentType,
444 final Class<T> clazz) {
445 return getComponentsUtils()
446 .convertJsonToObjectUsingObjectMapper(componentJson, user, clazz, AuditingActionEnum.CREATE_RESOURCE, componentType);
449 protected Response declareProperties(String userId, String componentId, String componentType,
450 String componentInstInputsMapObj, DeclarationTypeEnum typeEnum, HttpServletRequest request) {
451 ServletContext context = request.getSession().getServletContext();
452 String url = request.getMethod() + " " + request.getRequestURI();
453 log.debug("(get) Start handle request of {}", url);
456 BaseBusinessLogic businessLogic = getBlForDeclaration(typeEnum, context);
459 User modifier = new User();
460 modifier.setUserId(userId);
461 log.debug("modifier id is {}", userId);
462 ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
463 Either<ComponentInstInputsMap, ResponseFormat> componentInstInputsMapRes = parseToComponentInstanceMap(
464 componentInstInputsMapObj, modifier, componentTypeEnum, ComponentInstInputsMap.class);
465 if (componentInstInputsMapRes.isRight()) {
466 log.debug("failed to parse componentInstInputsMap");
467 return buildErrorResponse(componentInstInputsMapRes.right().value());
470 Either<List<ToscaDataDefinition>, ResponseFormat> propertiesAfterDeclaration = businessLogic
471 .declareProperties(userId, componentId,
473 componentInstInputsMapRes.left().value());
474 if (propertiesAfterDeclaration.isRight()) {
475 log.debug("failed to create inputs for service: {}", componentId);
476 return buildErrorResponse(propertiesAfterDeclaration.right().value());
478 Object properties = RepresentationUtils.toRepresentation(propertiesAfterDeclaration.left().value());
479 return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), properties);
481 } catch (Exception e) {
482 BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create inputs for service with id: " + componentId);
483 log.debug("Properties declaration failed with exception", e);
484 return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
488 public BaseBusinessLogic getBlForDeclaration(final DeclarationTypeEnum typeEnum,
489 final ServletContext context) {
492 return getOutputBL(context);
494 return getPolicyBL(context);
496 return getInputBL(context);
498 throw new IllegalArgumentException("Invalid DeclarationTypeEnum");
502 protected Either<Map<String, InputDefinition>, ActionStatus> getInputModel(String componentId, String data) {
503 JSONParser parser = new JSONParser();
506 Map<String, InputDefinition> inputs = new HashMap<>();
507 root = (JSONObject) parser.parse(data);
509 Set entrySet = root.entrySet();
510 Iterator iterator = entrySet.iterator();
511 while (iterator.hasNext()) {
512 Entry next = (Entry) iterator.next();
513 String inputName = (String) next.getKey();
515 if(!isInputNameValid(inputName)) {
516 return Either.right(ActionStatus.INVALID_INPUT_NAME);
519 JSONObject value = (JSONObject) next.getValue();
520 Either<InputDefinition, ActionStatus> inputDefinitionEither =
521 getInputDefinitionFromJson(componentId, inputName, value);
523 if(inputDefinitionEither.isRight()) {
524 return Either.right(inputDefinitionEither.right().value());
527 inputs.put(inputName, inputDefinitionEither.left().value());
530 return Either.left(inputs);
531 } catch (ParseException e) {
532 log.warn("Input content is invalid - {}", data, e);
533 return Either.right(ActionStatus.INVALID_CONTENT);
537 protected boolean isInputNameValid(String inputName) {
538 return Objects.nonNull(inputName)
539 && inputName.matches(PROPERTY_NAME_REGEX);