d29f23eef8da0534fb7ed7f3bc5b7b04a61bdca9
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / lifecycle / LifecycleBusinessLogic.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  * Modifications copyright (c) 2019 Nokia
20  * ================================================================================
21  */
22
23 package org.openecomp.sdc.be.components.lifecycle;
24
25 import com.google.common.annotations.VisibleForTesting;
26 import fj.data.Either;
27 import org.openecomp.sdc.be.catalog.enums.ChangeTypeEnum;
28 import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
29 import org.openecomp.sdc.be.components.impl.ProductBusinessLogic;
30 import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
31 import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
32 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
33 import org.openecomp.sdc.be.components.impl.exceptions.ByResponseFormatComponentException;
34 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
35 import org.openecomp.sdc.be.components.impl.version.VesionUpdateHandler;
36 import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction.LifecycleChanceActionEnum;
37 import org.openecomp.sdc.be.dao.api.ActionStatus;
38 import org.openecomp.sdc.be.dao.jsongraph.JanusGraphDao;
39 import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
40 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
41 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
42 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
43 import org.openecomp.sdc.be.facade.operations.CatalogOperation;
44 import org.openecomp.sdc.be.impl.ComponentsUtils;
45 import org.openecomp.sdc.be.model.Component;
46 import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
47 import org.openecomp.sdc.be.model.LifecycleStateEnum;
48 import org.openecomp.sdc.be.model.Resource;
49 import org.openecomp.sdc.be.model.User;
50 import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.ToscaElement;
51 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.NodeTemplateOperation;
52 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaElementLifecycleOperation;
53 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade;
54 import org.openecomp.sdc.be.model.jsonjanusgraph.utils.ModelConverter;
55 import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
56 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
57 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
58 import org.openecomp.sdc.be.resources.data.auditing.model.ResourceCommonInfo;
59 import org.openecomp.sdc.be.resources.data.auditing.model.ResourceVersionInfo;
60 import org.openecomp.sdc.common.api.Constants;
61 import org.openecomp.sdc.common.log.wrappers.Logger;
62 import org.openecomp.sdc.common.util.ValidationUtils;
63 import org.openecomp.sdc.exception.ResponseFormat;
64 import org.springframework.beans.factory.annotation.Autowired;
65 import org.springframework.context.annotation.Lazy;
66
67 import javax.annotation.PostConstruct;
68 import java.util.HashMap;
69 import java.util.Map;
70
71 @org.springframework.stereotype.Component("lifecycleBusinessLogic")
72 public class LifecycleBusinessLogic {
73
74     private static final String COMMENT = "comment";
75
76     @Autowired
77     private IGraphLockOperation graphLockOperation = null;
78
79     @Autowired
80     private JanusGraphDao janusGraphDao;
81
82     private static final Logger log = Logger.getLogger(LifecycleBusinessLogic.class);
83
84     @javax.annotation.Resource
85     private ComponentsUtils componentUtils;
86
87     @javax.annotation.Resource
88     private ToscaElementLifecycleOperation lifecycleOperation;
89
90     @Autowired
91     @Lazy
92     private ServiceBusinessLogic serviceBusinessLogic;
93
94     @Autowired
95     @Lazy
96     private ResourceBusinessLogic resourceBusinessLogic;
97
98     @Autowired
99     @Lazy
100     private ProductBusinessLogic productBusinessLogic;
101
102     @Autowired
103     ToscaOperationFacade toscaOperationFacade;
104     
105     @Autowired
106     NodeTemplateOperation nodeTemplateOperation;
107
108     @Autowired
109     CatalogOperation catalogOperations;
110
111     @Autowired
112     VesionUpdateHandler groupUpdateHandler;
113
114     private Map<String, LifeCycleTransition> stateTransitions;
115
116     @PostConstruct
117     public void init() {
118        initStateOperations();
119     }
120
121     private void initStateOperations() {
122         stateTransitions = new HashMap<>();
123
124         LifeCycleTransition checkoutOp = new CheckoutTransition(componentUtils, lifecycleOperation, toscaOperationFacade,
125             janusGraphDao);
126         stateTransitions.put(checkoutOp.getName().name(), checkoutOp);
127
128         UndoCheckoutTransition undoCheckoutOp = new UndoCheckoutTransition(componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao);
129         undoCheckoutOp.setCatalogOperations(catalogOperations);
130         stateTransitions.put(undoCheckoutOp.getName().name(), undoCheckoutOp);
131
132         LifeCycleTransition checkinOp = new CheckinTransition(componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao, groupUpdateHandler);
133         stateTransitions.put(checkinOp.getName().name(), checkinOp);
134
135         CertificationChangeTransition successCertification = new CertificationChangeTransition(serviceBusinessLogic, LifeCycleTransitionEnum.CERTIFY, componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao);
136         successCertification.setNodeTemplateOperation(nodeTemplateOperation);
137         stateTransitions.put(successCertification.getName().name(), successCertification);
138     }
139
140     @VisibleForTesting
141     Map<String, LifeCycleTransition> getStartTransition() {
142         return stateTransitions;
143     }
144
145     // TODO: rhalili - should use changeComponentState when possible
146     public Either<Resource, ResponseFormat> changeState(String resourceId, User modifier, LifeCycleTransitionEnum transitionEnum, LifecycleChangeInfoWithAction changeInfo, boolean inTransaction, boolean needLock) {
147         return (Either<Resource, ResponseFormat>) changeComponentState(ComponentTypeEnum.RESOURCE, resourceId, modifier, transitionEnum, changeInfo, inTransaction, needLock);
148     }
149
150     private boolean isComponentVFCMT(Component component, ComponentTypeEnum componentType) {
151         if (componentType.equals(ComponentTypeEnum.RESOURCE)) {
152             ResourceTypeEnum resourceType = ((ResourceMetadataDataDefinition) component.getComponentMetadataDefinition().getMetadataDataDefinition()).getResourceType();
153             if (resourceType.equals(ResourceTypeEnum.VFCMT)) {
154                 return true;
155             }
156         }
157         return false;
158     }
159
160     public Either<? extends Component, ResponseFormat> changeComponentState(ComponentTypeEnum componentType, String componentId, User modifier, LifeCycleTransitionEnum transitionEnum, LifecycleChangeInfoWithAction changeInfo, boolean inTransaction,
161             boolean needLock) {
162
163         LifeCycleTransition lifeCycleTransition = stateTransitions.get(transitionEnum.name());
164         if (lifeCycleTransition == null) {
165             log.debug("state operation is not valid. operations allowed are: {}", LifeCycleTransitionEnum.valuesAsString());
166             ResponseFormat error = componentUtils.getInvalidContentErrorAndAudit(modifier, componentId, AuditingActionEnum.CHECKOUT_RESOURCE);
167             return Either.right(error);
168         }
169         Component component;
170         log.debug("get resource from graph");
171         ResponseFormat errorResponse;
172
173         Either<? extends Component, ResponseFormat> eitherResourceResponse = getComponentForChange(componentType, componentId, modifier, lifeCycleTransition, changeInfo);
174         if (eitherResourceResponse.isRight()) {
175             return eitherResourceResponse;
176         }
177         component = eitherResourceResponse.left().value();
178         String resourceCurrVersion = component.getVersion();
179         LifecycleStateEnum resourceCurrState = component.getLifecycleState();
180
181         // lock resource
182         if (!inTransaction && needLock) {
183             log.debug("lock component {}", componentId);
184             try {
185                 lockComponent(componentType, component);
186             }catch (ComponentException e){
187                 errorResponse = e.getResponseFormat();
188                 componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
189                         new ResourceCommonInfo(componentType.getValue()),
190                         ResourceVersionInfo.newBuilder()
191                                 .state(resourceCurrState.name())
192                                 .version(resourceCurrVersion)
193                                 .build());
194
195
196                 log.error("lock component {} failed", componentId);
197                 return Either.right(errorResponse);
198             }
199             log.debug("after lock component {}", componentId);
200         }
201         try {
202             Either<String, ResponseFormat> commentValidationResult = validateComment(changeInfo, transitionEnum);
203             if (commentValidationResult.isRight()) {
204                 errorResponse = commentValidationResult.right().value();
205                 componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
206                         new ResourceCommonInfo(componentType.getValue()),
207                         ResourceVersionInfo.newBuilder()
208                                 .state(resourceCurrState.name())
209                                 .version(resourceCurrVersion)
210                                 .build(),
211                         changeInfo.getUserRemarks());
212                 return Either.right(errorResponse);
213             }
214             changeInfo.setUserRemarks(commentValidationResult.left().value());
215             log.debug("after validate component");
216             Either<Boolean, ResponseFormat> validateHighestVersion = validateHighestVersion(modifier, lifeCycleTransition, component, resourceCurrVersion, componentType);
217             if (validateHighestVersion.isRight()) {
218                 return Either.right(validateHighestVersion.right().value());
219             }
220             log.debug("after validate Highest Version");
221             final Component oldComponent = component;
222             Either<? extends Component, ResponseFormat> checkedInComponentEither = checkInBeforeCertifyIfNeeded(componentType, modifier, transitionEnum, changeInfo, inTransaction, component);
223             if(checkedInComponentEither.isRight()) {
224                 return Either.right(checkedInComponentEither.right().value());
225             }
226             component = checkedInComponentEither.left().value();
227             return changeState(component, lifeCycleTransition, componentType, modifier, changeInfo, inTransaction)
228                                         .left()
229                                         .bind(c -> updateCatalog(c, oldComponent, ChangeTypeEnum.LIFECYCLE));
230
231
232         } finally {
233             component.setUniqueId(componentId);
234             if (!inTransaction && needLock) {
235                 log.info("unlock component {}", componentId);
236                 NodeTypeEnum nodeType = componentType.getNodeType();
237                 log.info("During change state, another component {} has been created/updated", componentId);
238                 graphLockOperation.unlockComponent(componentId, nodeType);
239
240             }
241         }
242
243     }
244
245     private Either<Component, ResponseFormat>  updateCatalog(Component component,  Component oldComponent, ChangeTypeEnum changeStatus){
246
247         log.debug("updateCatalog start");
248         Component result = component == null? oldComponent : component;
249             if(component != null){
250                 ActionStatus status =  catalogOperations.updateCatalog(changeStatus,component);
251                 if(status != ActionStatus.OK){
252                     return Either.right( componentUtils.getResponseFormat(status));
253                 }
254         }
255
256        return Either.left(result);
257     }
258
259     private Either<? extends Component, ResponseFormat> checkInBeforeCertifyIfNeeded(ComponentTypeEnum componentType, User modifier, LifeCycleTransitionEnum transitionEnum, LifecycleChangeInfoWithAction changeInfo, boolean inTransaction,
260                                               Component component) {
261
262         LifecycleStateEnum oldState = component.getLifecycleState();
263         Component updatedComponent = component;
264         log.debug("Certification request for resource {} ", component.getUniqueId());
265         if (oldState == LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT && transitionEnum == LifeCycleTransitionEnum.CERTIFY) {
266             log.debug("Resource {} is in Checkout state perform checkin", component.getUniqueId());
267             Either<? extends Component, ResponseFormat> actionResponse = changeState(component, stateTransitions.get(LifeCycleTransitionEnum.CHECKIN.name()), componentType, modifier, changeInfo, inTransaction);
268             if (actionResponse.isRight()) {
269                 log.debug("Failed to check in Resource {} error {}", component.getUniqueId(), actionResponse.right().value());
270             }
271             return actionResponse;
272         }
273
274         return Either.left(updatedComponent);
275     }
276
277     private Either<? extends Component, ResponseFormat> changeState(Component component, LifeCycleTransition lifeCycleTransition, ComponentTypeEnum componentType, User modifier, LifecycleChangeInfoWithAction changeInfo, boolean inTransaction) {
278         ResponseFormat errorResponse;
279
280         LifecycleStateEnum oldState = component.getLifecycleState();
281         String resourceCurrVersion = component.getVersion();
282         ComponentBusinessLogic bl = getComponentBL(componentType);
283
284         Either<User, ResponseFormat> ownerResult = lifeCycleTransition.getComponentOwner(component, componentType);
285         if (ownerResult.isRight()) {
286             return Either.right(ownerResult.right().value());
287         }
288         User owner = ownerResult.left().value();
289         log.info("owner of resource {} is {}", component.getUniqueId(), owner.getUserId());
290
291         Either<Boolean, ResponseFormat> stateValidationResult = lifeCycleTransition.validateBeforeTransition(component, componentType, modifier, owner, oldState, changeInfo);
292         if (stateValidationResult.isRight()) {
293             log.error("Failed to validateBeforeTransition");
294             errorResponse = stateValidationResult.right().value();
295             componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
296                     new ResourceCommonInfo(componentType.getValue()),
297                     ResourceVersionInfo.newBuilder()
298                             .version(resourceCurrVersion)
299                             .state(oldState.name())
300                             .build(),
301                     changeInfo.getUserRemarks());
302             return Either.right(errorResponse);
303         }
304         
305         Either<? extends Component, ResponseFormat> operationResult = lifeCycleTransition.changeState(componentType, component, bl, modifier, owner, false, inTransaction);
306
307         if (operationResult.isRight()) {
308             errorResponse = operationResult.right().value();
309             log.info("audit before sending error response");
310             componentUtils.auditComponentAdmin(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(), componentType,
311                      ResourceVersionInfo.newBuilder()
312                              .state(oldState.name())
313                              .version(resourceCurrVersion)
314                              .build());
315
316             return Either.right(errorResponse);
317         }
318         Component resourceAfterOperation = operationResult.left().value() == null? component: operationResult.left().value() ;
319         componentUtils.auditComponent(componentUtils.getResponseFormat(ActionStatus.OK), modifier, resourceAfterOperation,
320                 lifeCycleTransition.getAuditingAction(), new ResourceCommonInfo(componentType.getValue()),
321                 ResourceVersionInfo.newBuilder()
322                         .state(oldState.name())
323                         .version(resourceCurrVersion)
324                         .build(),
325                 changeInfo.getUserRemarks());
326         return operationResult;
327
328     }
329
330
331         private Either<? extends Component, ResponseFormat> getComponentForChange(ComponentTypeEnum componentType, String componentId, User modifier, LifeCycleTransition lifeCycleTransition, LifecycleChangeInfoWithAction changeInfo) {
332
333         Either<? extends Component, StorageOperationStatus> eitherResourceResponse = toscaOperationFacade.getToscaElement(componentId);
334
335         ResponseFormat errorResponse;
336         if (eitherResourceResponse.isRight()) {
337             ActionStatus actionStatus = componentUtils.convertFromStorageResponse(eitherResourceResponse.right().value(), componentType);
338             errorResponse = componentUtils.getResponseFormat(actionStatus, Constants.EMPTY_STRING);
339             log.debug("audit before sending response");
340             componentUtils.auditComponent(errorResponse, modifier, lifeCycleTransition.getAuditingAction(),
341                     new ResourceCommonInfo(componentId, componentType.getValue()), changeInfo.getUserRemarks());
342
343             return Either.right(errorResponse);
344         }
345         return Either.left(eitherResourceResponse.left().value());
346     }
347
348     private Either<Boolean, ResponseFormat> validateHighestVersion(User modifier, LifeCycleTransition lifeCycleTransition, Component component, String resourceCurrVersion, ComponentTypeEnum componentType) {
349         ResponseFormat errorResponse;
350         if (!component.isHighestVersion()) {
351             log.debug("Component version {} is not the last version of component {}", component.getComponentMetadataDefinition().getMetadataDataDefinition().getVersion(),
352                     component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
353             errorResponse = componentUtils.getResponseFormat(ActionStatus.COMPONENT_HAS_NEWER_VERSION, component.getComponentMetadataDefinition().getMetadataDataDefinition().getName(), componentType.getValue().toLowerCase());
354             componentUtils.auditComponentAdmin(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(), componentType,
355                     ResourceVersionInfo.newBuilder()
356                             .state(component.getLifecycleState().name())
357                             .version(resourceCurrVersion)
358                             .build());
359             return Either.right(errorResponse);
360         }
361         return Either.left(true);
362     }
363
364     private Boolean lockComponent(ComponentTypeEnum componentType, Component component) {
365         NodeTypeEnum nodeType = componentType.getNodeType();
366         StorageOperationStatus lockResourceStatus = graphLockOperation.lockComponent(component.getUniqueId(), nodeType);
367
368         if (lockResourceStatus.equals(StorageOperationStatus.OK)) {
369             return true;
370         } else {
371             ActionStatus actionStatus = componentUtils.convertFromStorageResponse(lockResourceStatus);
372             throw new ByActionStatusComponentException(actionStatus, component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
373         }
374     }
375
376     private Either<String, ResponseFormat> validateComment(LifecycleChangeInfoWithAction changeInfo, LifeCycleTransitionEnum transitionEnum) {
377         String comment = changeInfo.getUserRemarks();
378         if (LifeCycleTransitionEnum.CERTIFY == transitionEnum || LifeCycleTransitionEnum.CHECKIN == transitionEnum
379         // import?
380         ) {
381
382             if (!ValidationUtils.validateStringNotEmpty(comment)) {
383                 log.debug("user comment cannot be empty or null.");
384                 ResponseFormat errorResponse = componentUtils.getResponseFormat(ActionStatus.MISSING_DATA, COMMENT);
385                 return Either.right(errorResponse);
386             }
387
388             comment = ValidationUtils.removeNoneUtf8Chars(comment);
389             comment = ValidationUtils.removeHtmlTags(comment);
390             comment = ValidationUtils.normaliseWhitespace(comment);
391             comment = ValidationUtils.stripOctets(comment);
392
393             if (!ValidationUtils.validateLength(comment, ValidationUtils.COMMENT_MAX_LENGTH)) {
394                 log.debug("user comment exceeds limit.");
395                 return Either.right(componentUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, COMMENT, String.valueOf(ValidationUtils.COMMENT_MAX_LENGTH)));
396             }
397             if (!ValidationUtils.validateIsEnglish(comment)) {
398                 return Either.right(componentUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
399             }
400         }
401         return Either.left(comment);
402     }
403
404     private ComponentBusinessLogic getComponentBL(ComponentTypeEnum componentTypeEnum) {
405         ComponentBusinessLogic businessLogic;
406         switch (componentTypeEnum) {
407         case RESOURCE:
408             businessLogic = this.resourceBusinessLogic;
409             break;
410         case SERVICE:
411             businessLogic = this.serviceBusinessLogic;
412             break;
413         case PRODUCT:
414             businessLogic = this.productBusinessLogic;
415             break;
416         default:
417             throw new IllegalArgumentException("Illegal component type:" + componentTypeEnum.getValue());
418         }
419         return businessLogic;
420     }
421
422     public Either<Component, ResponseFormat> getLatestComponentByUuid(ComponentTypeEnum componentTypeEnum, String uuid) {
423
424         Either<Component, StorageOperationStatus> latestVersionEither = toscaOperationFacade.getLatestComponentByUuid(uuid);
425
426         if (latestVersionEither.isRight()) {
427
428             return Either.right(componentUtils.getResponseFormat(componentUtils.convertFromStorageResponse(latestVersionEither.right().value(), componentTypeEnum), uuid));
429         }
430
431         Component latestComponent = latestVersionEither.left().value();
432
433         return Either.left(latestComponent);
434     }
435
436     /**
437      * Performs Force certification. Note that a Force certification is allowed for the first certification only, as only a state and a version is promoted due a Force certification, skipping other actions required if a previous certified version
438      * exists.
439      * 
440      * @param resource
441      * @param user
442      * @param lifecycleChangeInfo
443      * @param inTransaction
444      * @param needLock
445      * @return
446      */
447     public Resource forceResourceCertification(Resource resource, User user, LifecycleChangeInfoWithAction lifecycleChangeInfo, boolean inTransaction, boolean needLock) {
448         Resource result = null;
449         Either<ToscaElement, StorageOperationStatus> certifyResourceRes = null;
450         if (lifecycleChangeInfo.getAction() != LifecycleChanceActionEnum.CREATE_FROM_CSAR) {
451             log.debug("Force certification is not allowed for the action {}. ", lifecycleChangeInfo.getAction());
452             throw new ByActionStatusComponentException(ActionStatus.NOT_ALLOWED);
453         }
454         if (!isFirstCertification(resource.getVersion())) {
455             log.debug("Failed to perform a force certification of resource{}. Force certification is allowed for the first certification only. ", resource.getName());
456             throw new ByActionStatusComponentException(ActionStatus.NOT_ALLOWED);
457         }
458         // lock resource
459         if (!inTransaction && needLock) {
460             log.info("lock component {}", resource.getUniqueId());
461             lockComponent(resource.getComponentType(), resource);
462             log.info("after lock component {}", resource.getUniqueId());
463         }
464         try {
465             certifyResourceRes = lifecycleOperation.forceCerificationOfToscaElement(resource.getUniqueId(), user.getUserId(), user.getUserId(), resource.getVersion());
466             if (certifyResourceRes.isRight()) {
467                 StorageOperationStatus status = certifyResourceRes.right().value();
468                 log.debug("Failed to perform a force certification of resource {}. The status is {}. ", resource.getName(), status);
469                 throw new ByResponseFormatComponentException(componentUtils.getResponseFormatByResource(componentUtils.convertFromStorageResponse(status), resource));
470             }
471             result = ModelConverter.convertFromToscaElement(certifyResourceRes.left().value());
472             resource.setMetadataDefinition(result.getComponentMetadataDefinition());
473         } finally {
474             log.info("unlock component {}", resource.getUniqueId());
475             if (!inTransaction) {
476                 if (result != null) {
477                     janusGraphDao.commit();
478                 } else {
479                     janusGraphDao.rollback();
480                 }
481                 if (needLock) {
482                     NodeTypeEnum nodeType = resource.getComponentType().getNodeType();
483                     log.info("During change state, another component {} has been created/updated", resource.getUniqueId());
484                     graphLockOperation.unlockComponent(resource.getUniqueId(), nodeType);
485                 }
486             }
487         }
488         return result;
489     }
490
491     public boolean isFirstCertification(String previousVersion) {
492         return previousVersion.split("\\.")[0].equals("0");
493     }
494
495 }