18baebfcaeb77dbbf525f9c8084ab471dca177b1
[sdc.git] / openecomp-be / lib / openecomp-sdc-versioning-lib / openecomp-sdc-versioning-core / src / main / java / org / openecomp / sdc / versioning / impl / VersioningManagerImpl.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.versioning.impl;
22
23 import org.openecomp.sdc.common.errors.CoreException;
24 import org.openecomp.sdc.common.errors.ErrorCategory;
25 import org.openecomp.sdc.common.errors.ErrorCode;
26 import org.openecomp.sdc.datatypes.error.ErrorLevel;
27 import org.openecomp.sdc.logging.api.Logger;
28 import org.openecomp.sdc.logging.api.LoggerFactory;
29 import org.openecomp.sdc.logging.context.impl.MdcDataErrorMessage;
30 import org.openecomp.sdc.logging.types.LoggerConstants;
31 import org.openecomp.sdc.logging.types.LoggerErrorCode;
32 import org.openecomp.sdc.logging.types.LoggerErrorDescription;
33 import org.openecomp.sdc.logging.types.LoggerTragetServiceName;
34 import org.openecomp.sdc.versioning.ItemManager;
35 import org.openecomp.sdc.versioning.VersionCalculator;
36 import org.openecomp.sdc.versioning.VersioningManager;
37 import org.openecomp.sdc.versioning.dao.VersionDao;
38 import org.openecomp.sdc.versioning.dao.VersionInfoDao;
39 import org.openecomp.sdc.versioning.dao.VersionInfoDeletedDao;
40 import org.openecomp.sdc.versioning.dao.VersionableEntityDaoFactory;
41 import org.openecomp.sdc.versioning.dao.types.Revision;
42 import org.openecomp.sdc.versioning.dao.types.SynchronizationState;
43 import org.openecomp.sdc.versioning.dao.types.UserCandidateVersion;
44 import org.openecomp.sdc.versioning.dao.types.Version;
45 import org.openecomp.sdc.versioning.dao.types.VersionInfoDeletedEntity;
46 import org.openecomp.sdc.versioning.dao.types.VersionInfoEntity;
47 import org.openecomp.sdc.versioning.dao.types.VersionStatus;
48 import org.openecomp.sdc.versioning.errors.CheckinOnEntityLockedByOtherErrorBuilder;
49 import org.openecomp.sdc.versioning.errors.CheckinOnUnlockedEntityErrorBuilder;
50 import org.openecomp.sdc.versioning.errors.CheckoutOnLockedEntityErrorBuilder;
51 import org.openecomp.sdc.versioning.errors.DeleteOnLockedEntityErrorBuilder;
52 import org.openecomp.sdc.versioning.errors.EditOnEntityLockedByOtherErrorBuilder;
53 import org.openecomp.sdc.versioning.errors.EditOnUnlockedEntityErrorBuilder;
54 import org.openecomp.sdc.versioning.errors.EntityAlreadyExistErrorBuilder;
55 import org.openecomp.sdc.versioning.errors.EntityAlreadyFinalizedErrorBuilder;
56 import org.openecomp.sdc.versioning.errors.EntityNotExistErrorBuilder;
57 import org.openecomp.sdc.versioning.errors.SubmitLockedEntityNotAllowedErrorBuilder;
58 import org.openecomp.sdc.versioning.errors.UndoCheckoutOnEntityLockedByOtherErrorBuilder;
59 import org.openecomp.sdc.versioning.errors.UndoCheckoutOnUnlockedEntityErrorBuilder;
60 import org.openecomp.sdc.versioning.types.VersionCreationMethod;
61 import org.openecomp.sdc.versioning.types.VersionInfo;
62 import org.openecomp.sdc.versioning.types.VersionableEntityAction;
63 import org.openecomp.sdc.versioning.types.VersionableEntityMetadata;
64 import org.slf4j.MDC;
65
66 import java.util.Collection;
67 import java.util.HashMap;
68 import java.util.HashSet;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.Set;
72 import java.util.stream.Collectors;
73
74 public class VersioningManagerImpl implements VersioningManager {
75   private static final Logger LOGGER = LoggerFactory.getLogger(VersioningManagerImpl.class);
76   private static final Version INITIAL_ACTIVE_VERSION = new Version(0, 0);
77   private static final Map<String, Set<VersionableEntityMetadata>> VERSIONABLE_ENTITIES =
78       new HashMap<>();
79
80   private final VersionInfoDao versionInfoDao;
81   private final VersionInfoDeletedDao versionInfoDeletedDao;
82   private VersionDao versionDao;
83   private VersionCalculator versionCalculator;
84   private ItemManager itemManager;
85
86   public VersioningManagerImpl(VersionInfoDao versionInfoDao,
87                                VersionInfoDeletedDao versionInfoDeletedDao,
88                                VersionDao versionDao,
89                                VersionCalculator versionCalculator,
90                                ItemManager itemManager) {
91     this.versionInfoDao = versionInfoDao;
92     this.versionInfoDeletedDao = versionInfoDeletedDao;
93     this.versionDao = versionDao;
94     this.versionCalculator = versionCalculator;
95     this.itemManager = itemManager;
96   }
97
98   private static VersionInfo getVersionInfo(VersionInfoEntity versionInfoEntity, String user,
99                                             VersionableEntityAction action) {
100     return getVersionInfo(versionInfoEntity.getEntityId(),
101         versionInfoEntity.getEntityType(),
102         versionInfoEntity.getActiveVersion(),
103         versionInfoEntity.getCandidate(),
104         versionInfoEntity.getStatus(),
105         versionInfoEntity.getLatestFinalVersion(),
106         versionInfoEntity.getViewableVersions(),
107         action,
108         user);
109   }
110
111   private static VersionInfo getVersionInfo(VersionInfoDeletedEntity versionInfoEntity, String user,
112                                             VersionableEntityAction action) {
113     return getVersionInfo(versionInfoEntity.getEntityId(),
114         versionInfoEntity.getEntityType(),
115         versionInfoEntity.getActiveVersion(),
116         versionInfoEntity.getCandidate(),
117         versionInfoEntity.getStatus(),
118         versionInfoEntity.getLatestFinalVersion(),
119         versionInfoEntity.getViewableVersions(),
120         action,
121         user);
122   }
123
124   private static VersionInfo getVersionInfo(String entityId, String entityType, Version activeVer,
125                                             UserCandidateVersion candidate, VersionStatus status,
126                                             Version latestFinalVersion,
127                                             Set<Version> viewableVersions,
128                                             VersionableEntityAction action, String user) {
129     Version activeVersion;
130
131     if (action == VersionableEntityAction.Write) {
132       if (candidate != null) {
133         if (user.equals(candidate.getUser())) {
134           activeVersion = candidate.getVersion();
135         } else {
136           MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
137               LoggerTragetServiceName.GET_VERSION_INFO, ErrorLevel.ERROR.name(),
138               LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't get entity version info");
139           throw new CoreException(
140               new EditOnEntityLockedByOtherErrorBuilder(entityType, entityId, candidate.getUser())
141                   .build());
142         }
143       } else {
144         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
145             LoggerTragetServiceName.GET_VERSION_INFO, ErrorLevel.ERROR.name(),
146             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't get entity version info");
147         throw new CoreException(new EditOnUnlockedEntityErrorBuilder(entityType, entityId).build());
148       }
149     } else {
150       if (candidate != null && user.equals(candidate.getUser())) {
151         activeVersion = candidate.getVersion();
152       } else {
153         activeVersion = activeVer;
154       }
155     }
156
157     VersionInfo versionInfo = new VersionInfo();
158     versionInfo.setStatus(status);
159     activeVersion.setStatus(status);
160     if (latestFinalVersion != null) {
161       latestFinalVersion.setStatus(status);
162     }
163     if (viewableVersions != null) {
164       viewableVersions.forEach(version -> version.setStatus(status));
165     }
166     versionInfo.setActiveVersion(activeVersion);
167     versionInfo.setLatestFinalVersion(latestFinalVersion);
168     versionInfo.setViewableVersions(toSortedList(viewableVersions));
169     versionInfo.setFinalVersions(getFinalVersions(viewableVersions));
170     if (candidate != null) {
171       candidate.getVersion().setStatus(status);
172       versionInfo.setLockingUser(candidate.getUser());
173       if (user.equals(candidate.getUser())) {
174         versionInfo.getViewableVersions().add(candidate.getVersion());
175       }
176     }
177     return versionInfo;
178   }
179
180   private static List<Version> toSortedList(
181       Set<Version> versions) { // changing the Set to List in DB will require migration...
182     return versions.stream().sorted((o1, o2) -> o1.getMajor() > o2.getMajor() ? 1
183         : o1.getMajor() == o2.getMajor() ? (o1.getMinor() > o2.getMinor() ? 1
184             : o1.getMinor() == o2.getMinor() ? 0 : -1) : -1).collect(Collectors.toList());
185   }
186
187   private static List<Version> getFinalVersions(Set<Version> versions) {
188     return versions.stream().filter(Version::isFinal).collect(Collectors.toList());
189   }
190
191   @Override
192   public void register(String entityType, VersionableEntityMetadata entityMetadata) {
193     Set<VersionableEntityMetadata> entitiesMetadata =
194         VERSIONABLE_ENTITIES.computeIfAbsent(entityType, k -> new HashSet<>());
195     entitiesMetadata.add(entityMetadata);
196   }
197
198   @Override
199   public Version create(String entityType, String entityId, String user) {
200     VersionInfoEntity
201         versionInfoEntity = versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
202     if (versionInfoEntity != null) {
203       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
204           LoggerTragetServiceName.CREATE_ENTITY, ErrorLevel.ERROR.name(),
205           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't create versionable entity");
206       throw new CoreException(new EntityAlreadyExistErrorBuilder(entityType, entityId).build());
207     }
208
209     versionInfoEntity = new VersionInfoEntity(entityType, entityId);
210     versionInfoEntity.setActiveVersion(INITIAL_ACTIVE_VERSION);
211     markAsCheckedOut(versionInfoEntity, user);
212     versionInfoDao.create(versionInfoEntity);
213
214     return versionInfoEntity.getCandidate().getVersion();
215   }
216
217   @Override
218   public void delete(String entityType, String entityId, String user) {
219     VersionInfoEntity versionInfoEntity =
220         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
221     if (versionInfoEntity == null) {
222       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
223           LoggerTragetServiceName.DELETE_ENTITY, ErrorLevel.ERROR.name(),
224           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't delete versionable entity");
225       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
226     }
227
228     switch (versionInfoEntity.getStatus()) {
229       case Locked:
230         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
231             LoggerTragetServiceName.DELETE_ENTITY, ErrorLevel.ERROR.name(),
232             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't delete versionable entity");
233         throw new CoreException(new DeleteOnLockedEntityErrorBuilder(entityType, entityId,
234             versionInfoEntity.getCandidate().getUser()).build());
235       default:
236         //do nothing
237         break;
238     }
239
240     doDelete(versionInfoEntity);
241   }
242
243   @Override
244   public void undoDelete(String entityType, String entityId, String user) {
245     VersionInfoDeletedEntity versionInfoDeletedEntity =
246         versionInfoDeletedDao.get(new VersionInfoDeletedEntity(entityType, entityId));
247     if (versionInfoDeletedEntity == null) {
248       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
249           LoggerTragetServiceName.UNDO_DELETE_ENTITY, ErrorLevel.ERROR.name(),
250           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't undo delete for versionable entity");
251       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
252     }
253
254     doUndoDelete(versionInfoDeletedEntity);
255   }
256
257   @Override
258   public Version checkout(String entityType, String entityId, String user) {
259     VersionInfoEntity versionInfoEntity =
260         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
261     MDC.put(LoggerConstants.TARGET_SERVICE_NAME, LoggerTragetServiceName.CHECKOUT_ENTITY);
262     if (versionInfoEntity == null) {
263       MDC.put(LoggerConstants.ERROR_CATEGORY, ErrorLevel.ERROR.name());
264       MDC.put(LoggerConstants.TARGET_ENTITY, LoggerConstants.TARGET_ENTITY_DB);
265       MDC.put(LoggerConstants.ERROR_DESCRIPTION, LoggerErrorDescription.CHECKOUT_ENTITY);
266       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
267     }
268
269     Version checkoutVersion = null;
270     switch (versionInfoEntity.getStatus()) {
271       case Locked:
272         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
273             LoggerTragetServiceName.CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
274             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't checkout versionable entity");
275         throw new CoreException(new CheckoutOnLockedEntityErrorBuilder(entityType, entityId,
276             versionInfoEntity.getCandidate().getUser()).build());
277       case Certified:
278       case Draft:
279         checkoutVersion = doCheckout(versionInfoEntity, user);
280         break;
281       default:
282         //do nothing
283         break;
284     }
285
286     return checkoutVersion;
287   }
288
289   @Override
290   public Version undoCheckout(String entityType, String entityId, String user) {
291     VersionInfoEntity versionInfoEntity =
292         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
293     if (versionInfoEntity == null) {
294       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
295           LoggerTragetServiceName.UNDO_CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
296           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't undo checkout for versionable entity");
297       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
298     }
299
300     Version activeVersion = null;
301     switch (versionInfoEntity.getStatus()) {
302       case Locked:
303         if (!user.equals(versionInfoEntity.getCandidate().getUser())) {
304           MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
305               LoggerTragetServiceName.UNDO_CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
306               LoggerErrorCode.PERMISSION_ERROR.getErrorCode(),
307               "Can't undo checkout for versionable entity");
308           throw new CoreException(
309               new UndoCheckoutOnEntityLockedByOtherErrorBuilder(entityType, entityId,
310                   versionInfoEntity.getCandidate().getUser()).build());
311         }
312         activeVersion = undoCheckout(versionInfoEntity);
313         break;
314       case Certified:
315       case Draft:
316         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
317             LoggerTragetServiceName.UNDO_CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
318             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(),
319             "Can't undo checkout for versionable entity");
320         throw new CoreException(
321             new UndoCheckoutOnUnlockedEntityErrorBuilder(entityType, entityId).build());
322       default:
323         //do nothing
324         break;
325     }
326
327     return activeVersion;
328   }
329
330   private Version undoCheckout(VersionInfoEntity versionInfoEntity) {
331     deleteVersionFromEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
332         versionInfoEntity.getCandidate().getVersion(), versionInfoEntity.getActiveVersion());
333
334     versionInfoEntity
335         .setStatus(versionInfoEntity.getActiveVersion().isFinal() ? VersionStatus.Certified
336             : VersionStatus.Draft);
337     versionInfoEntity.setCandidate(null);
338     versionInfoDao.update(versionInfoEntity);
339     return versionInfoEntity.getActiveVersion();
340   }
341
342   @Override
343   public Version checkin(String entityType, String entityId, String user,
344                          String checkinDescription) {
345     VersionInfoEntity versionInfoEntity =
346         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
347     if (versionInfoEntity == null) {
348       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
349           LoggerTragetServiceName.CHECKIN_ENTITY, ErrorLevel.ERROR.name(),
350           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't checkin versionable entity");
351       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
352     }
353
354     Version checkedInVersion = null;
355     switch (versionInfoEntity.getStatus()) {
356       case Draft:
357       case Certified:
358         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
359             LoggerTragetServiceName.CHECKIN_ENTITY, ErrorLevel.ERROR.name(),
360             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't checkin versionable entity");
361         throw new CoreException(
362             new CheckinOnUnlockedEntityErrorBuilder(entityType, entityId).build());
363       case Locked:
364         if (!user.equals(versionInfoEntity.getCandidate().getUser())) {
365           MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
366               LoggerTragetServiceName.CHECKIN_ENTITY, ErrorLevel.ERROR.name(),
367               LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't checkin versionable entity");
368           throw new CoreException(new CheckinOnEntityLockedByOtherErrorBuilder(entityType, entityId,
369               versionInfoEntity.getCandidate().getUser()).build());
370         }
371         checkedInVersion = doCheckin(versionInfoEntity, checkinDescription);
372         break;
373       default:
374         //do nothing
375         break;
376     }
377
378     return checkedInVersion;
379   }
380
381   @Override
382   public Version submit(String entityType, String entityId, String user, String submitDescription) {
383     VersionInfoEntity versionInfoEntity =
384         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
385     if (versionInfoEntity == null) {
386       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
387           LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
388           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't submit versionable entity");
389       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
390     }
391
392     Version submitVersion = null;
393     switch (versionInfoEntity.getStatus()) {
394       case Certified:
395         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
396             LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
397             LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't submit versionable entity");
398         throw new CoreException(
399             new EntityAlreadyFinalizedErrorBuilder(entityType, entityId).build());
400       case Locked:
401         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
402             LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
403             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't submit versionable entity");
404         throw new CoreException(new SubmitLockedEntityNotAllowedErrorBuilder(entityType, entityId,
405             versionInfoEntity.getCandidate().getUser()).build());
406       case Draft:
407         submitVersion = doSubmit(versionInfoEntity, user, submitDescription);
408         break;
409       default:
410         //do nothing
411         break;
412     }
413
414     return submitVersion;
415   }
416
417   @Override
418   public VersionInfo getEntityVersionInfo(String entityType, String entityId, String user,
419                                           VersionableEntityAction action) {
420     VersionInfoEntity versionInfoEntity =
421         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
422     if (versionInfoEntity == null) {
423       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
424           LoggerTragetServiceName.GET_VERSION_INFO, ErrorLevel.ERROR.name(),
425           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't get entity version info");
426       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
427     }
428     return getVersionInfo(versionInfoEntity, user, action);
429   }
430
431   @Override
432   public Map<String, VersionInfo> listEntitiesVersionInfo(String entityType, String user,
433                                                           VersionableEntityAction action) {
434     Collection<VersionInfoEntity> versionInfoEntities =
435         versionInfoDao.list(new VersionInfoEntity(entityType, null));
436     Map<String, VersionInfo> activeVersions = new HashMap<>();
437     for (VersionInfoEntity versionInfoEntity : versionInfoEntities) {
438       activeVersions
439           .put(versionInfoEntity.getEntityId(), getVersionInfo(versionInfoEntity, user, action));
440     }
441     return activeVersions;
442   }
443
444   @Override
445   public Map<String, VersionInfo> listDeletedEntitiesVersionInfo(String entityType, String user,
446                                                                  VersionableEntityAction action) {
447     Collection<VersionInfoDeletedEntity> versionInfoDeletedEntities =
448         versionInfoDeletedDao.list(new VersionInfoDeletedEntity(entityType, null));
449     Map<String, VersionInfo> activeVersions = new HashMap<>();
450
451
452     for (VersionInfoDeletedEntity versionInfoDeletedEntity : versionInfoDeletedEntities) {
453       activeVersions.put(versionInfoDeletedEntity.getEntityId(),
454           getVersionInfo(versionInfoDeletedEntity, user, action));
455     }
456     return activeVersions;
457   }
458
459   @Override
460   public List<Version> list(String itemId) {
461
462     List<Version> versions = versionDao.list(itemId);
463     Set<String> versionsNames = versions.stream().map(Version::getName).collect(Collectors.toSet());
464     versions.forEach(version -> {
465       version.setAdditionalInfo(new HashMap<>());
466       versionCalculator.injectAdditionalInfo(version, versionsNames);
467     });
468     return versions;
469   }
470
471   @Override
472   public Version get(String itemId, Version version) {
473     return versionDao.get(itemId, version)
474         .map(retrievedVersion -> getUpdateRetrievedVersion(itemId, retrievedVersion))
475         .orElseGet(() -> getSyncedVersion(itemId, version));
476   }
477
478   private Version getUpdateRetrievedVersion(String itemId, Version version) {
479     if (version.getStatus() == VersionStatus.Certified &&
480         (version.getState().getSynchronizationState() == SynchronizationState.OutOfSync ||
481             version.getState().isDirty())) {
482       forceSync(itemId, version);
483       LOGGER.info("Item Id {}, version Id {}: Force sync is done", itemId, version.getId());
484       version = versionDao.get(itemId, version)
485           .orElseThrow(() -> new IllegalStateException(
486               "Get version after a successful force sync must return the version"));
487     }
488     return version;
489   }
490
491   private Version getSyncedVersion(String itemId, Version version) {
492     sync(itemId, version);
493     LOGGER.info("Item Id {}, version Id {}: First time sync is done", itemId, version.getId());
494     return versionDao.get(itemId, version)
495         .orElseThrow(() -> new IllegalStateException(
496             "Get version after a successful sync must return the version"));
497   }
498
499   @Override
500   public Version create(String itemId, Version version, VersionCreationMethod creationMethod) {
501     String baseVersionName = null;
502     if (version.getBaseId() == null) {
503       version.setDescription("Initial version");
504     } else {
505       baseVersionName = get(itemId, new Version(version.getBaseId())).getName();
506     }
507     String versionName = versionCalculator.calculate(baseVersionName, creationMethod);
508     validateVersionName(itemId, versionName);
509     version.setName(versionName);
510
511     versionDao.create(itemId, version);
512     itemManager.updateVersionStatus(itemId, VersionStatus.Draft, null);
513
514     publish(itemId, version, String.format("Create version: %s", version.getName()));
515     return version;
516   }
517
518   private void validateVersionName(String itemId, String versionName) {
519     if (versionDao.list(itemId).stream()
520         .anyMatch(version -> versionName.equals(version.getName()))) {
521       String errorDescription = String
522           .format("Item %s: create version failed, a version with the name %s already exist",
523               itemId, versionName);
524
525       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY,
526           LoggerTragetServiceName.CREATE_VERSION, ErrorLevel.ERROR.name(),
527           LoggerErrorCode.DATA_ERROR.getErrorCode(), errorDescription);
528
529       throw new CoreException(new ErrorCode.ErrorCodeBuilder()
530           .withCategory(ErrorCategory.APPLICATION)
531           .withId("VERSION_NAME_ALREADY_EXIST")
532           .withMessage(errorDescription)
533           .build());
534     }
535   }
536
537   @Override
538   public void submit(String itemId, Version version, String submitDescription) {
539     version = get(itemId, version);
540
541     validateSubmit(itemId, version);
542
543     version.setStatus(VersionStatus.Certified);
544     versionDao.update(itemId, version);
545
546     publish(itemId, version, submitDescription);
547
548     itemManager.updateVersionStatus(itemId, VersionStatus.Certified, VersionStatus.Draft);
549   }
550
551   private void validateSubmit(String itemId, Version version) {
552     if (version.getStatus() == VersionStatus.Certified) {
553       String errorDescription = String
554           .format("Item %s: submit version failed, version %s is already Certified", itemId,
555               version.getId());
556
557       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
558           LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
559           LoggerErrorCode.DATA_ERROR.getErrorCode(), errorDescription);
560
561       throw new CoreException(new ErrorCode.ErrorCodeBuilder()
562           .withCategory(ErrorCategory.APPLICATION)
563           .withId("VERSION_ALREADY_CERTIFIED")
564           .withMessage(errorDescription)
565           .build());
566     }
567   }
568
569   @Override
570   public void publish(String itemId, Version version, String message) {
571     versionDao.publish(itemId, version, message);
572   }
573
574
575   @Override
576   public void sync(String itemId, Version version) {
577     versionDao.sync(itemId, version);
578   }
579
580   @Override
581   public void forceSync(String itemId, Version version) {
582     versionDao.forceSync(itemId, version);
583   }
584
585   @Override
586   public void revert(String itemId, Version version, String revisionId) {
587     versionDao.revert(itemId, version, revisionId);
588   }
589
590   @Override
591   public List<Revision> listRevisions(String itemId, Version version) {
592     return versionDao.listRevisions(itemId, version);
593   }
594
595   private void markAsCheckedOut(VersionInfoEntity versionInfoEntity, String checkingOutUser) {
596     versionInfoEntity.setStatus(VersionStatus.Locked);
597     versionInfoEntity.setCandidate(new UserCandidateVersion(checkingOutUser,
598         versionInfoEntity.getActiveVersion().calculateNextCandidate()));
599   }
600
601   private Version doCheckout(VersionInfoEntity versionInfoEntity, String user) {
602     markAsCheckedOut(versionInfoEntity, user);
603     versionInfoDao.update(versionInfoEntity);
604
605     initVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
606         versionInfoEntity.getActiveVersion(), versionInfoEntity.getCandidate().getVersion());
607
608     return versionInfoEntity.getCandidate().getVersion();
609   }
610
611   private void doDelete(VersionInfoEntity versionInfoEntity) {
612     VersionInfoDeletedEntity versionInfoDeletedEntity = new VersionInfoDeletedEntity();
613     versionInfoDeletedEntity.setStatus(versionInfoEntity.getStatus());
614     versionInfoDeletedEntity.setViewableVersions(versionInfoEntity.getViewableVersions());
615     versionInfoDeletedEntity.setActiveVersion(versionInfoEntity.getActiveVersion());
616     versionInfoDeletedEntity.setCandidate(versionInfoEntity.getCandidate());
617     versionInfoDeletedEntity.setEntityId(versionInfoEntity.getEntityId());
618     versionInfoDeletedEntity.setEntityType(versionInfoEntity.getEntityType());
619     versionInfoDeletedEntity.setLatestFinalVersion(versionInfoEntity.getLatestFinalVersion());
620     versionInfoDeletedDao.create(versionInfoDeletedEntity);
621     versionInfoDao.delete(versionInfoEntity);
622   }
623
624   private void doUndoDelete(VersionInfoDeletedEntity versionInfoDeletedEntity) {
625     VersionInfoEntity versionInfoEntity = new VersionInfoEntity();
626     versionInfoEntity.setStatus(versionInfoDeletedEntity.getStatus());
627     versionInfoEntity.setViewableVersions(versionInfoDeletedEntity.getViewableVersions());
628     versionInfoEntity.setActiveVersion(versionInfoDeletedEntity.getActiveVersion());
629     versionInfoEntity.setCandidate(versionInfoDeletedEntity.getCandidate());
630     versionInfoEntity.setEntityId(versionInfoDeletedEntity.getEntityId());
631     versionInfoEntity.setEntityType(versionInfoDeletedEntity.getEntityType());
632     versionInfoEntity.setLatestFinalVersion(versionInfoDeletedEntity.getLatestFinalVersion());
633     versionInfoDao.create(versionInfoEntity);
634     versionInfoDeletedDao.delete(versionInfoDeletedEntity);
635   }
636
637   private Version doCheckin(VersionInfoEntity versionInfoEntity, String checkinDescription) {
638     UserCandidateVersion userCandidateVersion = versionInfoEntity.getCandidate();
639     versionInfoEntity.setCandidate(null);
640     versionInfoEntity.setActiveVersion(userCandidateVersion.getVersion());
641     versionInfoEntity.getViewableVersions().add(versionInfoEntity.getActiveVersion());
642     versionInfoEntity.setStatus(VersionStatus.Draft);
643
644     closeVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
645         versionInfoEntity.getActiveVersion());
646
647     versionInfoDao.update(versionInfoEntity);
648
649     return versionInfoEntity.getActiveVersion();
650   }
651
652   private Version doSubmit(VersionInfoEntity versionInfoEntity, String submittingUser,
653                            String submitDescription) {
654     Version finalVersion = versionInfoEntity.getActiveVersion().calculateNextFinal();
655     initVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
656         versionInfoEntity.getActiveVersion(), finalVersion);
657     closeVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
658         finalVersion);
659
660     Set<Version> viewableVersions = new HashSet<>();
661     for (Version version : versionInfoEntity.getViewableVersions()) {
662       if (version.isFinal()) {
663         viewableVersions.add(version);
664       }
665     }
666     viewableVersions.add(finalVersion);
667     versionInfoEntity.setViewableVersions(viewableVersions);
668     versionInfoEntity.setActiveVersion(finalVersion);
669     versionInfoEntity.setLatestFinalVersion(finalVersion);
670     versionInfoEntity.setStatus(VersionStatus.Certified);
671     versionInfoDao.update(versionInfoEntity);
672
673     return finalVersion;
674   }
675
676   private void initVersionOnEntity(String entityType, String entityId, Version baseVersion,
677                                    Version newVersion) {
678     Set<VersionableEntityMetadata> entityMetadatas = VERSIONABLE_ENTITIES.get(entityType);
679     if (entityMetadatas != null) {
680       for (VersionableEntityMetadata entityMetadata : entityMetadatas) {
681         VersionableEntityDaoFactory.getInstance().createInterface(entityMetadata.getStoreType())
682             .initVersion(entityMetadata, entityId, baseVersion, newVersion);
683       }
684     }
685   }
686
687   private void deleteVersionFromEntity(String entityType, String entityId,
688                                        Version versionToDelete, Version backToVersion) {
689     Set<VersionableEntityMetadata> entityMetadatas = VERSIONABLE_ENTITIES.get(entityType);
690     if (entityMetadatas != null) {
691       for (VersionableEntityMetadata entityMetadata : entityMetadatas) {
692         VersionableEntityDaoFactory.getInstance().createInterface(entityMetadata.getStoreType())
693             .deleteVersion(entityMetadata, entityId, versionToDelete, backToVersion);
694       }
695     }
696   }
697
698   private void closeVersionOnEntity(String entityType, String entityId, Version versionToClose) {
699     Set<VersionableEntityMetadata> entityMetadatas = VERSIONABLE_ENTITIES.get(entityType);
700     if (entityMetadatas != null) {
701       for (VersionableEntityMetadata entityMetadata : entityMetadatas) {
702         VersionableEntityDaoFactory.getInstance().createInterface(entityMetadata.getStoreType())
703             .closeVersion(entityMetadata, entityId, versionToClose);
704       }
705     }
706   }
707 }