0439dd5ef6abdd68703f528a7a35d5246d2b51c4
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / PropertyBusinessLogic.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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=========================================================
19  */
20
21 package org.openecomp.sdc.be.components.impl;
22
23 import com.google.gson.JsonElement;
24 import fj.data.Either;
25 import org.apache.commons.collections.CollectionUtils;
26 import org.apache.commons.collections.MapUtils;
27 import org.apache.commons.lang3.tuple.ImmutablePair;
28 import org.openecomp.sdc.be.config.BeEcompErrorManager;
29 import org.openecomp.sdc.be.dao.api.ActionStatus;
30 import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
31 import org.openecomp.sdc.be.datatypes.elements.*;
32 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
33 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
34 import org.openecomp.sdc.be.model.*;
35 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
36 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
37 import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
38 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
39 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
40 import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
41 import org.openecomp.sdc.be.resources.data.EntryData;
42 import org.openecomp.sdc.common.api.Constants;
43 import org.openecomp.sdc.common.log.wrappers.Logger;
44 import org.openecomp.sdc.exception.ResponseFormat;
45 import org.springframework.web.context.WebApplicationContext;
46
47 import javax.servlet.ServletContext;
48 import java.util.*;
49 import java.util.function.Supplier;
50
51 @org.springframework.stereotype.Component("propertyBusinessLogic")
52 public class PropertyBusinessLogic extends BaseBusinessLogic {
53
54     private static final String CREATE_PROPERTY = "CreateProperty";
55
56     private static final Logger log = Logger.getLogger(PropertyBusinessLogic.class);
57
58     private static final String EMPTY_VALUE = null;
59
60     protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
61         WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
62
63         WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
64
65         return webApplicationContext.getBean(class1);
66     }
67
68     public Either<Map<String, DataTypeDefinition>, ResponseFormat> getAllDataTypes() {
69         return getAllDataTypes(applicationDataTypeCache);
70     }
71
72     /**
73      * Create new property on component in graph
74      *
75      * @param componentId
76      * @param propertyName
77      * @param newPropertyDefinition
78      * @param userId
79      * @return either properties or response format
80      */
81
82     public Either<EntryData<String, PropertyDefinition>, ResponseFormat> addPropertyToComponent(String componentId,
83                                                                                                 String propertyName,
84                                                                                                 PropertyDefinition newPropertyDefinition,
85                                                                                                 String userId) {
86         Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
87
88         validateUserExists(userId, "create Property", false);
89
90         Either<Component, StorageOperationStatus> serviceElement =
91                 toscaOperationFacade.getToscaElement(componentId);
92         if (serviceElement.isRight()) {
93             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
94             return result;
95         }
96         Component component = serviceElement.left().value();
97         NodeTypeEnum nodeType = component.getComponentType().getNodeType();
98         StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType );
99         if (!lockResult.equals(StorageOperationStatus.OK)) {
100             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(), componentId);
101             log.info("Failed to lock component {}. Error - {}", componentId, lockResult);
102             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
103             return result;
104         }
105
106         try {
107             if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
108                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
109                 return result;
110             }
111
112             List<PropertyDefinition> properties = component.getProperties();
113
114             if(CollectionUtils.isEmpty(properties)) {
115                 properties = new ArrayList<>();
116             }
117
118             if(isPropertyExistInComponent(properties, propertyName)) {
119
120                 result =
121                     Either.right(componentsUtils.getResponseFormat(ActionStatus
122                         .PROPERTY_ALREADY_EXIST, propertyName));
123                 return result;
124
125             } else {
126
127                 Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
128                 if (allDataTypes.isRight()) {
129                     result = Either.right(allDataTypes.right().value());
130                     return result;
131                 }
132
133                 Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
134
135                 // validate property default values
136                 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
137                 if (defaultValuesValidation.isRight()) {
138                     result = Either.right(defaultValuesValidation.right().value());
139                     return result;
140                 }
141                 // convert property
142                 ToscaPropertyType type = getType(newPropertyDefinition.getType());
143                 if (type != null) {
144                     PropertyValueConverter converter = type.getConverter();
145                     // get inner type
146                     String innerType = null;
147                     if (newPropertyDefinition != null) {
148                         SchemaDefinition schema = newPropertyDefinition.getSchema();
149                         if (schema != null) {
150                             PropertyDataDefinition prop = schema.getProperty();
151                             if (prop != null) {
152                                 innerType = prop.getType();
153                             }
154                         }
155                         String convertedValue = null;
156                         if (newPropertyDefinition.getDefaultValue() != null) {
157                             convertedValue = converter.convert(
158                                 newPropertyDefinition.getDefaultValue(), innerType, allDataTypes.left().value());
159                             newPropertyDefinition.setDefaultValue(convertedValue);
160                         }
161                     }
162                 }
163                 Either<PropertyDefinition, StorageOperationStatus> addPropertyEither =
164                     toscaOperationFacade
165                         .addPropertyToComponent(propertyName, newPropertyDefinition, component);
166
167                 if (addPropertyEither.isRight()) {
168                     log.info("Failed to add new property {}. Error - {}", componentId,
169                         addPropertyEither.right().value());
170                     result = Either.right(componentsUtils.getResponseFormat(ActionStatus
171                         .GENERAL_ERROR));
172                     return result;
173                 }
174             }
175
176             result = Either.left(new EntryData<>(propertyName, newPropertyDefinition));
177             return result;
178
179         } finally {
180             commitOrRollback(result);
181             // unlock component
182             graphLockOperation.unlockComponent(componentId, nodeType);
183         }
184
185     }
186
187     /**
188      * Get property of component
189      *
190      * @param componentId
191      * @param propertyId
192      * @param userId
193      * @return either properties or response format
194      */
195
196     public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> getComponentProperty(String componentId, String propertyId, String userId) {
197
198         validateUserExists(userId, "create Component Instance", false);
199         // Get the resource from DB
200         Either<Component, StorageOperationStatus> status =
201             toscaOperationFacade.getToscaElement(componentId);
202         if (status.isRight()) {
203             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
204         }
205         Component component = status.left().value();
206         List<PropertyDefinition> properties = component.getProperties();
207         if(CollectionUtils.isEmpty(properties)) {
208             return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
209         }
210
211         for(PropertyDefinition property : properties) {
212             if(property.getUniqueId().equals(propertyId)) {
213                 return Either.left(new EntryData<>(property.getName(), property));
214             }
215         }
216         return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
217     }
218
219
220     public Either<List<PropertyDefinition>, ResponseFormat> getPropertiesList(String componentId,
221                                                                               String userId) {
222         validateUserExists(userId, "create Component Instance", false);
223
224         // Get the resource from DB
225         ComponentParametersView filter = new ComponentParametersView(true);
226         filter.setIgnoreProperties(false);
227         Either<Component, StorageOperationStatus> status =
228             toscaOperationFacade.getToscaElement(componentId);
229         if (status.isRight()) {
230             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
231         }
232         Component component = status.left().value();
233         List<PropertyDefinition> properties = component.getProperties();
234
235         return Either.left(properties);
236     }
237
238
239     /**
240      * delete property of component from graph
241      *
242      * @param componentId
243      * @param propertyId
244      * @param userId
245      * @return either properties or response format
246      */
247
248     public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> deletePropertyFromComponent(String componentId, String propertyId, String userId) {
249
250         Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> result = null;
251
252         validateUserExists(userId, "delete Property", false);
253
254         // Get the resource from DB
255         Either<Component, StorageOperationStatus> getComponentRes = toscaOperationFacade.getToscaElement(componentId);
256         if (getComponentRes.isRight()) {
257             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
258             return result;
259         }
260         Component component = getComponentRes.left().value();
261         NodeTypeEnum nodeType = component.getComponentType().getNodeType();
262         StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
263         if (!lockResult.equals(StorageOperationStatus.OK)) {
264             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(),
265                     componentId);
266             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
267             return result;
268         }
269
270         try {
271             // verify that resource is checked-out and the user is the last
272             // updater
273             if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
274                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
275                 return result;
276             }
277
278             // verify property exist in resource
279             Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty =
280                 getComponentProperty(componentId, propertyId, userId);
281             if (statusGetProperty.isRight()) {
282                 result = Either.right(statusGetProperty.right().value());
283                 return result;
284             }
285
286             Map.Entry<String, PropertyDefinition> propertyDefinitionEntry = statusGetProperty.left().value();
287
288             // verify that the property is not used by operation
289             if (isPropertyUsedByOperation(component, propertyDefinitionEntry.getValue())) {
290                 return Either.right(componentsUtils.getResponseFormat(ActionStatus
291                     .PROPERTY_USED_BY_OPERATION));
292             }
293
294             StorageOperationStatus status =
295                 toscaOperationFacade.deletePropertyOfComponent(component, propertyDefinitionEntry.getKey());
296             if (status != StorageOperationStatus.OK) {
297                 result = Either.right(componentsUtils.getResponseFormat(componentsUtils
298                     .convertFromStorageResponse(status), component.getName()));
299                 return result;
300             }
301             result = Either.left(propertyDefinitionEntry);
302             return result;
303
304         } finally {
305             commitOrRollback(result);
306             // unlock component
307             graphLockOperation.unlockComponent(componentId, nodeType);
308         }
309     }
310
311     public boolean isPropertyUsedByOperation(Component component,
312                                              PropertyDefinition propertyDefinitionEntry) {
313
314         // Component's own interfaces
315         Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
316         if(MapUtils.isNotEmpty(interfaces)){
317           for(Map.Entry<String, InterfaceDefinition> interfaceEntry : interfaces.entrySet()) {
318             if (isPropertyExistInOperationInterface(propertyDefinitionEntry, interfaceEntry.getValue())) {
319               return true;
320             }
321           }
322         }
323
324         // Component's child's component interfaces
325         if(isPropertyUsedInCIInterfaces(component.getComponentInstancesInterfaces(), propertyDefinitionEntry)){
326             return true;
327         }
328
329         // Component's parent's component interfaces
330         Either<List<Component>, StorageOperationStatus> componentList = toscaOperationFacade.getParentComponents(component.getUniqueId());
331         if(componentList.isLeft()){
332             for (Component parentComponent : componentList.left().value()) {
333                 if(isPropertyUsedInCIInterfaces(parentComponent.getComponentInstancesInterfaces(), propertyDefinitionEntry)){
334                     return true;
335                 }
336             }
337         }
338
339         return false;
340     }
341
342     private boolean isPropertyUsedInCIInterfaces(Map<String, List<ComponentInstanceInterface>> componentInstanceInterfaces, PropertyDefinition propertyDefinitionEntry){
343         Optional<ComponentInstanceInterface> isPropertyExistInOperationInterface = Optional.empty();
344         if(MapUtils.isNotEmpty(componentInstanceInterfaces)){
345             isPropertyExistInOperationInterface = componentInstanceInterfaces.entrySet().stream()
346                     .flatMap(interfaceEntry -> interfaceEntry.getValue().stream())
347                     .filter(instanceInterface -> isPropertyExistInOperationInterface(propertyDefinitionEntry, instanceInterface))
348                     .findAny();
349         }
350         return isPropertyExistInOperationInterface.isPresent();
351     }
352
353     private boolean isPropertyExistInOperationInterface(PropertyDefinition propertyDefinition,
354                                                         InterfaceDefinition interfaceDefinition) {
355         Map<String, OperationDataDefinition> operations =
356             interfaceDefinition.getOperations();
357         for(Map.Entry<String, OperationDataDefinition> operationEntry : operations
358             .entrySet()) {
359             Optional<OperationInputDefinition> inputWithDeletedPropertyCandidate =
360                 getInputWithDeclaredProperty(propertyDefinition, operationEntry);
361
362             if(inputWithDeletedPropertyCandidate.isPresent()) {
363                 return true;
364             }
365         }
366         return false;
367     }
368
369     private Optional<OperationInputDefinition> getInputWithDeclaredProperty(PropertyDefinition propertyDefinition,
370                                                                             Map.Entry<String, OperationDataDefinition> operationEntry) {
371         ListDataDefinition<OperationInputDefinition> inputs =
372             operationEntry.getValue().getInputs();
373         List<OperationInputDefinition> operationInputsList =
374             Objects.isNull(inputs) ? null : inputs.getListToscaDataDefinition();
375
376         if(CollectionUtils.isEmpty(operationInputsList)) {
377             return Optional.empty();
378         }
379
380         return operationInputsList.stream().filter(input -> input.getInputId().equals(propertyDefinition.getUniqueId())).findAny();
381     }
382
383     /**
384      * update property
385      *
386      * @param componentId
387      * @param propertyId
388      * @param newPropertyDefinition
389      * @param userId
390      * @return either properties or response format
391      */
392
393     public Either<EntryData<String, PropertyDefinition>, ResponseFormat> updateComponentProperty(String componentId,
394                                                                         String propertyId,
395                                                                         PropertyDefinition newPropertyDefinition,
396                                                                         String userId) {
397
398         Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
399
400         Either<Component, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(
401                 componentId);
402         if (status.isRight()) {
403             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
404         }
405         Component component = status.left().value();
406         NodeTypeEnum nodeType = component.getComponentType().getNodeType();
407
408         if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
409             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
410         }
411
412         StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
413         if (!lockResult.equals(StorageOperationStatus.OK)) {
414             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(),
415                     componentId);
416             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
417             return result;
418         }
419
420         try {
421             Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty =
422                 getComponentProperty(componentId, propertyId, userId);
423             if (statusGetProperty.isRight()) {
424                 result = Either.right(statusGetProperty.right().value());
425                 return result;
426             }
427             String propertyName = statusGetProperty.left().value().getKey();
428
429             Either<PropertyDefinition, StorageOperationStatus> either =
430                 toscaOperationFacade.updatePropertyOfComponent(component, newPropertyDefinition);
431             if (either.isRight()) {
432                 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), component.getName()));
433                 return result;
434             }
435
436             EntryData<String, PropertyDefinition> property = new EntryData<>(propertyName, either.left().value());
437             result = Either.left(property);
438             return result;
439
440         } finally {
441             commitOrRollback(result);
442             graphLockOperation.unlockComponent(componentId, nodeType);
443         }
444
445     }
446
447     private boolean isPropertyExistInComponent(List<PropertyDefinition> properties, String propertyName) {
448         if(CollectionUtils.isEmpty(properties)) {
449             return false;
450         }
451
452         Optional<PropertyDefinition> propertyCandidate =
453             properties.stream().filter(property -> property.getName().equals(propertyName))
454                 .findAny();
455
456         return propertyCandidate.isPresent();
457     }
458
459     private boolean isPropertyExist(List<PropertyDefinition> properties, String resourceUid, String propertyName, String propertyType) {
460         boolean result = false;
461         if (!CollectionUtils.isEmpty(properties)) {
462             for (PropertyDefinition propertyDefinition : properties) {
463
464                 if ( propertyDefinition.getName().equals(propertyName) &&
465                     (propertyDefinition.getParentUniqueId().equals(resourceUid) || !propertyDefinition.getType().equals(propertyType)) ) {
466                     result = true;
467                     break;
468                 }
469             }
470         }
471         return result;
472     }
473
474     private Either<PropertyDefinition, StorageOperationStatus> handleProperty(PropertyDefinition newPropertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
475
476         StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(newPropertyDefinition, dataTypes);
477         if (validateAndUpdateProperty != StorageOperationStatus.OK) {
478             return Either.right(validateAndUpdateProperty);
479         }
480
481         return Either.left(newPropertyDefinition);
482     }
483
484     private StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
485
486         log.trace("Going to validate property type and value. {}", propertyDefinition);
487
488         String propertyType = propertyDefinition.getType();
489         String value = propertyDefinition.getDefaultValue();
490
491         ToscaPropertyType type = getType(propertyType);
492
493         if (type == null) {
494             DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
495             if (dataTypeDefinition == null) {
496                 log.debug("The type {} of property cannot be found.", propertyType);
497                 return StorageOperationStatus.INVALID_TYPE;
498             }
499             return validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
500         }
501         String innerType;
502
503         Either<String, TitanOperationStatus> checkInnerType = getInnerType(type, propertyDefinition::getSchema);
504         if (checkInnerType.isRight()) {
505             return StorageOperationStatus.INVALID_TYPE;
506         }
507         innerType = checkInnerType.left().value();
508
509         log.trace("After validating property type {}", propertyType);
510
511         boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
512         if (!isValidProperty) {
513             log.info("The value {} of property from type {} is invalid", value, type);
514             return StorageOperationStatus.INVALID_VALUE;
515         }
516
517         PropertyValueConverter converter = type.getConverter();
518
519         if (isEmptyValue(value)) {
520             log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
521             propertyDefinition.setDefaultValue(EMPTY_VALUE);
522         } else if (!isEmptyValue(value)) {
523             String convertedValue = converter.convert(value, innerType, dataTypes);
524             propertyDefinition.setDefaultValue(convertedValue);
525         }
526         return StorageOperationStatus.OK;
527     }
528
529     private StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
530                                                                  String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
531
532         ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
533
534         if (validateResult.right) {
535             log.debug("The value {} of property from type {} is invalid", propertyType, propertyType);
536             return StorageOperationStatus.INVALID_VALUE;
537         }
538
539         JsonElement jsonElement = validateResult.left;
540
541         log.trace("Going to update value in property definition {} {}" , propertyDefinition.getName() , jsonElement);
542
543         updateValue(propertyDefinition, jsonElement);
544
545         return StorageOperationStatus.OK;
546     }
547
548     private void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
549
550         propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
551
552     }
553
554     @Override
555     protected String getValueFromJsonElement(JsonElement jsonElement) {
556         if (jsonElement == null || jsonElement.isJsonNull()) {
557             return EMPTY_VALUE;
558         }
559         if(jsonElement.toString().isEmpty()){
560             return "";
561         }
562         return jsonElement.toString();
563     }
564
565     private Either<String, TitanOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
566         String innerType = null;
567         if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
568
569             SchemaDefinition def = schemeGen.get();
570             if (def == null) {
571                 log.debug("Schema doesn't exists for property of type {}", type);
572                 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
573             }
574             PropertyDataDefinition propDef = def.getProperty();
575             if (propDef == null) {
576                 log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
577                 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
578             }
579             innerType = propDef.getType();
580         }
581         return Either.left(innerType);
582     }
583
584     @Override
585     protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
586         if (isEmptyValue(value)) {
587             return true;
588         }
589         PropertyTypeValidator validator = type.getValidator();
590         return validator.isValid(value, innerType, dataTypes);
591     }
592
593     @Override
594     public boolean isEmptyValue(String value) {
595         return value == null;
596     }
597 }