/******************************************************************************* * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2019) * * contact.vitam@culture.gouv.fr * * This software is a computer program whose purpose is to implement a digital archiving back-office system managing * high volumetry securely and efficiently. * * This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free * software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as * circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, * users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the * successive licensors have only limited liability. * * In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or * developing or reproducing the software by the user in light of its specific status of free software, that may mean * that it is complicated to manipulate, and that also therefore means that it is reserved for developers and * experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the * software's suitability as regards their requirements in conditions enabling the security of their systems and/or data * to be ensured and, more generally, to use and operate it in the same conditions as regards security. * * The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you * accept its terms. *******************************************************************************/ package fr.gouv.vitam.logbook.lifecycles.core; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.fasterxml.jackson.databind.JsonNode; import com.mongodb.client.MongoCursor; import fr.gouv.vitam.common.ParametersChecker; import fr.gouv.vitam.common.database.builder.query.QueryHelper; import fr.gouv.vitam.common.database.builder.request.exception.InvalidCreateOperationException; import fr.gouv.vitam.common.database.builder.request.single.Select; import fr.gouv.vitam.common.database.parser.request.single.SelectParserSingle; import fr.gouv.vitam.common.exception.InvalidParseOperationException; import fr.gouv.vitam.common.guid.GUIDFactory; import fr.gouv.vitam.common.logging.VitamLogger; import fr.gouv.vitam.common.logging.VitamLoggerFactory; import fr.gouv.vitam.common.model.LifeCycleStatusCode; import fr.gouv.vitam.logbook.common.parameters.LogbookLifeCycleObjectGroupParameters; import fr.gouv.vitam.logbook.common.parameters.LogbookLifeCycleParameters; import fr.gouv.vitam.logbook.common.parameters.LogbookLifeCycleUnitParameters; import fr.gouv.vitam.logbook.common.parameters.LogbookParameterName; import fr.gouv.vitam.logbook.common.server.LogbookDbAccess; import fr.gouv.vitam.logbook.common.server.database.collections.LogbookCollections; import fr.gouv.vitam.logbook.common.server.database.collections.LogbookDocument; import fr.gouv.vitam.logbook.common.server.database.collections.LogbookLifeCycle; import fr.gouv.vitam.logbook.common.server.database.collections.LogbookLifeCycleObjectGroup; import fr.gouv.vitam.logbook.common.server.database.collections.LogbookLifeCycleObjectGroupInProcess; import fr.gouv.vitam.logbook.common.server.database.collections.LogbookLifeCycleUnit; import fr.gouv.vitam.logbook.common.server.database.collections.LogbookLifeCycleUnitInProcess; import fr.gouv.vitam.logbook.common.server.database.collections.LogbookMongoDbName; import fr.gouv.vitam.logbook.common.server.database.collections.request.LogbookVarNameAdapter; import fr.gouv.vitam.logbook.common.server.exception.LogbookAlreadyExistsException; import fr.gouv.vitam.logbook.common.server.exception.LogbookDatabaseException; import fr.gouv.vitam.logbook.common.server.exception.LogbookNotFoundException; import fr.gouv.vitam.logbook.lifecycles.api.LogbookLifeCycles; /** * Logbook LifeCycles implementation base class */ public class LogbookLifeCyclesImpl implements LogbookLifeCycles { private static final VitamLogger LOGGER = VitamLoggerFactory.getInstance(LogbookLifeCyclesImpl.class); /** * This is valid as Static final since this has to be shared among all requests and Concurrent for Thread safety */ private static final Map<String, MongoCursor<?>> mapXCursor = new ConcurrentHashMap<>(); private static final Map<String, LogbookCollections> mapXCursorByCollection = new ConcurrentHashMap<>(); private final LogbookDbAccess mongoDbAccess; /** * Constructor * * @param mongoDbAccess */ public LogbookLifeCyclesImpl(LogbookDbAccess mongoDbAccess) { this.mongoDbAccess = mongoDbAccess; } @Override public void createUnit(String idOperation, String idLc, LogbookLifeCycleUnitParameters parameters) throws LogbookAlreadyExistsException, LogbookDatabaseException, IllegalArgumentException { checkLifeCyclesUnitArgument(idOperation, idLc, parameters); mongoDbAccess.createLogbookLifeCycleUnit(idOperation, parameters); } @Override public void createObjectGroup(String idOperation, String idLc, LogbookLifeCycleObjectGroupParameters parameters) throws LogbookAlreadyExistsException, LogbookDatabaseException, IllegalArgumentException { checkLifeCyclesObjectGroupArgument(idOperation, idLc, parameters); mongoDbAccess.createLogbookLifeCycleObjectGroup(idOperation, parameters); } @Override public void updateUnit(String idOperation, String idLc, LogbookLifeCycleUnitParameters parameters) throws LogbookDatabaseException, IllegalArgumentException, LogbookNotFoundException, LogbookAlreadyExistsException { checkLifeCyclesUnitArgument(idOperation, idLc, parameters); mongoDbAccess.updateLogbookLifeCycleUnit(idOperation, parameters); } @Override public void updateObjectGroup(String idOperation, String idLc, LogbookLifeCycleObjectGroupParameters parameters) throws LogbookNotFoundException, LogbookDatabaseException, IllegalArgumentException, LogbookAlreadyExistsException { checkLifeCyclesObjectGroupArgument(idOperation, idLc, parameters); mongoDbAccess.updateLogbookLifeCycleObjectGroup(idOperation, parameters); } @Override public LogbookLifeCycleUnit getUnitByOperationIdAndByUnitId(String idOperation, String idLc) throws LogbookDatabaseException, LogbookNotFoundException, InvalidParseOperationException { return mongoDbAccess.getLogbookLifeCycleUnit(idOperation, idLc); } @Override public LogbookLifeCycleObjectGroup getObjectGroupByOperationIdAndByObjectGroupId(String idOperation, String idLc) throws LogbookDatabaseException, LogbookNotFoundException, InvalidParseOperationException, IllegalArgumentException { return mongoDbAccess.getLogbookLifeCycleObjectGroup(idOperation, idLc); } @Override public List<LogbookLifeCycle> selectUnit(JsonNode select, LogbookCollections collection) throws LogbookDatabaseException, LogbookNotFoundException, InvalidParseOperationException { return selectUnit(select, true, collection); } @Override public List<LogbookLifeCycle> selectUnit(JsonNode select, boolean sliced, LogbookCollections collection) throws LogbookDatabaseException, LogbookNotFoundException, InvalidParseOperationException { try (final MongoCursor<LogbookLifeCycle> logbook = mongoDbAccess.getLogbookLifeCycleUnits(select, sliced, collection)) { final List<LogbookLifeCycle> result = new ArrayList<>(); if (!logbook.hasNext()) { throw new LogbookNotFoundException("Logbook entry not found"); } while (logbook.hasNext()) { result.add(logbook.next()); } return result; } } @Override public String createCursorUnit(String operationId, JsonNode select, LogbookCollections logbookCollection) throws LogbookDatabaseException { String newxcursorid; // First time call newxcursorid = GUIDFactory.newGUID().toString(); MongoCursor<LogbookLifeCycleUnit> cursor; try { final SelectParserSingle parser = new SelectParserSingle(new LogbookVarNameAdapter()); parser.parse(select); parser.addCondition(QueryHelper.or() .add(QueryHelper.eq(LogbookMongoDbName.eventIdentifierProcess.getDbname(), operationId)) .add(QueryHelper.eq( LogbookDocument.EVENTS + '.' + LogbookMongoDbName.eventIdentifierProcess.getDbname(), operationId))); final Select selectRequest = parser.getRequest(); cursor = mongoDbAccess.getLogbookLifeCycleUnitsFull(logbookCollection, selectRequest); mapXCursor.put(newxcursorid, cursor); } catch (InvalidParseOperationException | InvalidCreateOperationException e) { throw new LogbookDatabaseException(e); } return newxcursorid; } @SuppressWarnings("rawtypes") @Override public LogbookLifeCycle getCursorUnitNext(String cursorId) throws LogbookNotFoundException, LogbookDatabaseException { try { @SuppressWarnings("unchecked") final MongoCursor<LogbookLifeCycle> cursor = (MongoCursor<LogbookLifeCycle>) mapXCursor.get(cursorId); if (cursor != null) { if (cursor.hasNext()) { return cursor.next(); } cursor.close(); mapXCursor.remove(cursorId); throw new LogbookNotFoundException("No more entries"); } } catch (final ClassCastException e) { throw new LogbookDatabaseException("Cursor not linked to Unit", e); } throw new LogbookDatabaseException("Cursor already closed"); } @Override public String createCursorObjectGroup(String operationId, JsonNode select, LogbookCollections collection) throws LogbookDatabaseException { String newxcursorid; // First time call newxcursorid = GUIDFactory.newGUID().toString(); MongoCursor<?> cursor; try { final SelectParserSingle parser = new SelectParserSingle(new LogbookVarNameAdapter()); parser.parse(select); parser.addCondition(QueryHelper.or() .add(QueryHelper.eq(LogbookMongoDbName.eventIdentifierProcess.getDbname(), operationId)) .add(QueryHelper.eq( LogbookDocument.EVENTS + '.' + LogbookMongoDbName.eventIdentifierProcess.getDbname(), operationId))); final Select selectRequest = parser.getRequest(); cursor = mongoDbAccess.getLogbookLifeCycleObjectGroupsFull(collection, selectRequest); mapXCursor.put(newxcursorid, cursor); } catch (InvalidParseOperationException | InvalidCreateOperationException e) { throw new LogbookDatabaseException(e); } return newxcursorid; } @SuppressWarnings("rawtypes") @Override public LogbookLifeCycle getCursorObjectGroupNext(String cursorId) throws LogbookNotFoundException, LogbookDatabaseException { try { final MongoCursor<?> cursor = (MongoCursor<?>) mapXCursor.get(cursorId); if (cursor != null) { if (cursor.hasNext()) { return (LogbookLifeCycle) cursor.next(); } cursor.close(); mapXCursor.remove(cursorId); mapXCursorByCollection.remove(cursorId); throw new LogbookNotFoundException("No more entries"); } } catch (final ClassCastException e) { throw new LogbookDatabaseException("Cursor not linked to ObjectGroup", e); } throw new LogbookDatabaseException("Cursor already closed"); } @Override public void finalizeCursor(String cursorId) { final MongoCursor<?> cursor = mapXCursor.get(cursorId); if (cursor != null) { cursor.close(); mapXCursor.remove(cursorId); mapXCursorByCollection.remove(cursorId); return; } } @Override public List<LogbookLifeCycle> selectObjectGroup(JsonNode select, LogbookCollections collection) throws LogbookDatabaseException, LogbookNotFoundException, InvalidParseOperationException { return selectObjectGroup(select, true, collection); } @Override public List<LogbookLifeCycle> selectObjectGroup(JsonNode select, boolean sliced, LogbookCollections collection) throws LogbookDatabaseException, LogbookNotFoundException, InvalidParseOperationException { try (final MongoCursor<LogbookLifeCycle> logbook = mongoDbAccess.getLogbookLifeCycleObjectGroups(select, sliced, collection)) { final List<LogbookLifeCycle> result = new ArrayList<>(); if (!logbook.hasNext()) { throw new LogbookNotFoundException("Logbook entry not found"); } while (logbook.hasNext()) { result.add(logbook.next()); } return result; } } @Override public void rollbackUnit(String idOperation, String idLc) throws LogbookNotFoundException, LogbookDatabaseException, IllegalArgumentException { mongoDbAccess.rollbackLogbookLifeCycleUnit(idOperation, idLc); } @Override public void rollbackObjectGroup(String idOperation, String idLc) throws LogbookNotFoundException, LogbookDatabaseException, IllegalArgumentException { mongoDbAccess.rollbackLogbookLifeCycleObjectGroup(idOperation, idLc); } @Override public LogbookLifeCycleUnit getUnitById(String idUnit) throws LogbookDatabaseException, LogbookNotFoundException { return mongoDbAccess.getLogbookLifeCycleUnit(idUnit); } @Override public LogbookLifeCycle getUnitById(JsonNode queryDsl, LogbookCollections collection) throws LogbookDatabaseException, LogbookNotFoundException { return mongoDbAccess.getLogbookLifeCycleUnit(queryDsl, collection); } @Override public LogbookLifeCycleObjectGroup getObjectGroupById(String idObjectGroup) throws LogbookDatabaseException, LogbookNotFoundException { return mongoDbAccess.getLogbookLifeCycleObjectGroup(idObjectGroup); } private void checkLifeCyclesUnitArgument(String idOperation, String idLcUnit, LogbookLifeCycleUnitParameters parameters) throws IllegalArgumentException { ParametersChecker.checkParameter("idOperation or idLifeCycle should not be null or empty", idOperation, idLcUnit); if (!parameters.getParameterValue(LogbookParameterName.eventIdentifierProcess).equals(idOperation)) { LOGGER.error("incoherence entry for idOperation"); throw new IllegalArgumentException("incoherence entry for idOperation"); } if (!parameters.getParameterValue(LogbookParameterName.objectIdentifier).equals(idLcUnit)) { LOGGER.error("incoherence entry for idLifeCycles"); throw new IllegalArgumentException("incoherence entry for idLifeCycles"); } } private void checkLifeCyclesObjectGroupArgument(String idOperation, String idLcObjectGroup, LogbookLifeCycleObjectGroupParameters parameters) throws IllegalArgumentException { ParametersChecker.checkParameter("idOperation or idLifeCycleObjectGroup should not be null or empty", idOperation, idLcObjectGroup); if (!parameters.getParameterValue(LogbookParameterName.eventIdentifierProcess).equals(idOperation)) { LOGGER.error("incoherence entry for idOperation"); throw new IllegalArgumentException("incoherence entry for idOperation"); } if (!parameters.getParameterValue(LogbookParameterName.objectIdentifier).equals(idLcObjectGroup)) { LOGGER.error("incoherence entry for idLifeCyclesObjectGroup"); throw new IllegalArgumentException("incoherence entry for idLifeCyclesObjectGroup"); } } @Override public void createBulkLogbookLifecycle(String idOp, LogbookLifeCycleParameters[] lifecycleArray) throws LogbookDatabaseException, LogbookAlreadyExistsException { ParametersChecker.checkParameter("idOperation should not be null or empty", idOp); if (lifecycleArray == null || lifecycleArray.length == 0) { throw new IllegalArgumentException("No LifeCycle Logbook"); } if (!lifecycleArray[0].getParameterValue(LogbookParameterName.eventIdentifierProcess).equals(idOp)) { LOGGER.error("incoherence entry for idOperation"); throw new IllegalArgumentException("incoherence entry for idOperation"); } if (lifecycleArray instanceof LogbookLifeCycleUnitParameters[]) { mongoDbAccess.createBulkLogbookLifeCycleUnit((LogbookLifeCycleUnitParameters[]) lifecycleArray); } else { mongoDbAccess .createBulkLogbookLifeCycleObjectGroup((LogbookLifeCycleObjectGroupParameters[]) lifecycleArray); } } @Override public void updateBulkLogbookLifecycle(String idOp, LogbookLifeCycleParameters[] lifecycleArray) throws LogbookDatabaseException, LogbookNotFoundException, LogbookAlreadyExistsException { ParametersChecker.checkParameter("idOperation should not be null or empty", idOp); if (lifecycleArray == null || lifecycleArray.length == 0) { throw new IllegalArgumentException("No LifeCycle Logbook"); } if (!lifecycleArray[0].getParameterValue(LogbookParameterName.eventIdentifierProcess).equals(idOp)) { LOGGER.error("incoherence entry for idOperation"); throw new IllegalArgumentException("incoherence entry for idOperation"); } if (lifecycleArray instanceof LogbookLifeCycleUnitParameters[]) { mongoDbAccess.updateBulkLogbookLifeCycleUnit((LogbookLifeCycleUnitParameters[]) lifecycleArray); } else { mongoDbAccess .updateBulkLogbookLifeCycleObjectGroup((LogbookLifeCycleObjectGroupParameters[]) lifecycleArray); } } @Override public void commitUnit(String idOperation, String idLc) throws LogbookDatabaseException, LogbookNotFoundException, LogbookAlreadyExistsException { // 1- Find temporary unit lifeCycle LogbookLifeCycleUnitInProcess logbookLifeCycleUnitInProcess = mongoDbAccess.getLogbookLifeCycleUnitInProcess(idLc); if (logbookLifeCycleUnitInProcess == null) { LOGGER.error("The temporary lifeCycle wasn't found"); throw new LogbookNotFoundException("The temporary lifeCycle wasn't found"); } // 2- Check if it is a creation or an update commit boolean isLifeCycleExist = mongoDbAccess.existsLogbookLifeCycleUnit(idLc); // 3- Copy to Production Collection if (!isLifeCycleExist) { // Creation Mode from LogbookLifeCycleUnitInProcess instance mongoDbAccess.createLogbookLifeCycleUnit(logbookLifeCycleUnitInProcess); } else { // Update existing lifeCycle mongoDbAccess.updateLogbookLifeCycleUnit(logbookLifeCycleUnitInProcess); } } @Override public void commitObjectGroup(String idOperation, String idLc) throws LogbookDatabaseException, LogbookNotFoundException, LogbookAlreadyExistsException { // 1- Find temporary unit lifeCycle LogbookLifeCycleObjectGroupInProcess logbookLifeCycleObjectGroupInProcess = mongoDbAccess.getLogbookLifeCycleObjectGroupInProcess(idLc); if (logbookLifeCycleObjectGroupInProcess == null) { LOGGER.error("The temporary lifeCycle wasn't found"); throw new LogbookNotFoundException("The temporary lifeCycle wasn't found"); } // 2- Check if it is a creation or an update commit boolean isLifeCycleExist = mongoDbAccess.existsLogbookLifeCycleObjectGroup(idLc); // 3- Copy to Production Collection if (!isLifeCycleExist) { // Creation Mode from LogbookLifeCycleObjectGroupInProcess instance mongoDbAccess.createLogbookLifeCycleObjectGroup(logbookLifeCycleObjectGroupInProcess); } else { // Update existing lifeCycle mongoDbAccess.updateLogbookLifeCycleObjectGroup(logbookLifeCycleObjectGroupInProcess); } } @Override public void rollBackUnitsByOperation(String idOperation) throws LogbookNotFoundException, LogbookDatabaseException { mongoDbAccess.rollBackUnitLifeCyclesByOperation(idOperation); } @Override public void rollBackObjectGroupsByOperation(String idOperation) throws LogbookNotFoundException, LogbookDatabaseException { mongoDbAccess.rollBackObjectGroupLifeCyclesByOperation(idOperation); } @Override public LifeCycleStatusCode getUnitLifeCycleStatus(String unitId) throws LogbookDatabaseException, LogbookNotFoundException { // 1- First, check if the lifeCycle exists in the working collection boolean isInProcessLfc = mongoDbAccess.existsLogbookLifeCycleUnitInProcess(unitId); if (isInProcessLfc) { return LifeCycleStatusCode.LIFE_CYCLE_IN_PROCESS; } // 2- If it doesn't exist, then check in the production collection boolean isCommittedLfc = mongoDbAccess.existsLogbookLifeCycleUnit(unitId); if (isCommittedLfc) { return LifeCycleStatusCode.LIFE_CYCLE_COMMITTED; } // Else, lifeCycle wasn't found return null; } @Override public LifeCycleStatusCode getObjectGroupLifeCycleStatus(String objectGroupId) throws LogbookDatabaseException, LogbookNotFoundException { // 1- First, check if the lifeCycle exists in the working collection boolean isInProcessLfc = mongoDbAccess.existsLogbookLifeCycleObjectGroupInProcess(objectGroupId); if (isInProcessLfc) { return LifeCycleStatusCode.LIFE_CYCLE_IN_PROCESS; } // 2- If it doesn't exist, then check in the production collection boolean isCommittedLfc = mongoDbAccess.existsLogbookLifeCycleObjectGroup(objectGroupId); if (isCommittedLfc) { return LifeCycleStatusCode.LIFE_CYCLE_COMMITTED; } // Else, lifeCycle wasn't found return null; } }