/******************************************************************************* * 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.worker.core.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import fr.gouv.vitam.common.ParametersChecker; import fr.gouv.vitam.common.exception.InvalidGuidOperationException; import fr.gouv.vitam.common.guid.GUIDFactory; import fr.gouv.vitam.common.guid.GUIDReader; import fr.gouv.vitam.common.i18n.VitamLogbookMessages; import fr.gouv.vitam.common.logging.VitamLogger; import fr.gouv.vitam.common.logging.VitamLoggerFactory; import fr.gouv.vitam.common.model.ItemStatus; import fr.gouv.vitam.common.model.StatusCode; import fr.gouv.vitam.common.parameter.ParameterHelper; import fr.gouv.vitam.logbook.common.exception.LogbookClientBadRequestException; import fr.gouv.vitam.logbook.common.exception.LogbookClientNotFoundException; import fr.gouv.vitam.logbook.common.exception.LogbookClientServerException; import fr.gouv.vitam.logbook.common.parameters.LogbookLifeCycleParameters; import fr.gouv.vitam.logbook.common.parameters.LogbookLifeCyclesClientHelper; import fr.gouv.vitam.logbook.common.parameters.LogbookParameterName; import fr.gouv.vitam.logbook.common.parameters.LogbookParametersFactory; import fr.gouv.vitam.logbook.common.parameters.LogbookType; import fr.gouv.vitam.logbook.common.parameters.LogbookTypeProcess; import fr.gouv.vitam.logbook.lifecycles.client.LogbookLifeCyclesClient; import fr.gouv.vitam.logbook.lifecycles.client.LogbookLifeCyclesClientFactory; import fr.gouv.vitam.processing.common.exception.HandlerNotFoundException; import fr.gouv.vitam.processing.common.exception.PluginNotFoundException; import fr.gouv.vitam.processing.common.exception.ProcessingException; import fr.gouv.vitam.processing.common.model.Action; import fr.gouv.vitam.processing.common.model.ActionDefinition; import fr.gouv.vitam.processing.common.model.DistributionKind; import fr.gouv.vitam.processing.common.model.ProcessBehavior; import fr.gouv.vitam.processing.common.model.Step; import fr.gouv.vitam.processing.common.parameter.WorkerParameters; import fr.gouv.vitam.worker.common.HandlerIO; import fr.gouv.vitam.worker.common.utils.LogbookLifecycleWorkerHelper; import fr.gouv.vitam.worker.core.api.Worker; import fr.gouv.vitam.worker.core.handler.AccessionRegisterActionHandler; import fr.gouv.vitam.worker.core.handler.ActionHandler; import fr.gouv.vitam.worker.core.handler.CheckObjectUnitConsistencyActionHandler; import fr.gouv.vitam.worker.core.handler.CheckObjectsNumberActionHandler; import fr.gouv.vitam.worker.core.handler.CheckSedaActionHandler; import fr.gouv.vitam.worker.core.handler.CheckStorageAvailabilityActionHandler; import fr.gouv.vitam.worker.core.handler.CheckVersionActionHandler; import fr.gouv.vitam.worker.core.handler.CommitLifeCycleObjectGroupActionHandler; import fr.gouv.vitam.worker.core.handler.CommitLifeCycleUnitActionHandler; import fr.gouv.vitam.worker.core.handler.DummyHandler; import fr.gouv.vitam.worker.core.handler.ExtractSedaActionHandler; import fr.gouv.vitam.worker.core.handler.RollBackActionHandler; import fr.gouv.vitam.worker.core.handler.TransferNotificationActionHandler; import fr.gouv.vitam.worker.core.plugin.PluginLoader; import fr.gouv.vitam.workspace.api.exception.ContentAddressableStorageServerException; /** * WorkerImpl class implements Worker interface * <p> * manages and executes actions by step */ public class WorkerImpl implements Worker { private static final VitamLogger LOGGER = VitamLoggerFactory.getInstance(WorkerImpl.class); private static final String EMPTY_LIST = "null or Empty Action list"; private static final String STEP_NULL = "step paramaters is null"; private static final String HANDLER_NOT_FOUND = ": handler not found exception: "; private final Map<String, ActionHandler> actions = new HashMap<>(); private final String workerId; private final PluginLoader pluginLoader; /** * Constructor * * @param pluginLoader */ public WorkerImpl(PluginLoader pluginLoader) { this.pluginLoader = pluginLoader; workerId = GUIDFactory.newGUID().toString(); /* * temporary init: will be managed by spring annotation */ init(); } /** * Add an actionhandler in the pool of action * * @param actionName action name * @param actionHandler action handler * @return WorkerImpl */ @Override public WorkerImpl addActionHandler(String actionName, ActionHandler actionHandler) { ParametersChecker.checkParameter("actionName is a mandatory parameter", actionName); ParametersChecker.checkParameter("actionHandler is a mandatory parameter", actionHandler); actions.put(actionName, actionHandler); return this; } private void init() { /* * Pool of action 's object */ actions.put(ExtractSedaActionHandler.getId(), new ExtractSedaActionHandler()); actions.put(CheckSedaActionHandler.getId(), new CheckSedaActionHandler()); actions.put(CheckObjectsNumberActionHandler.getId(), new CheckObjectsNumberActionHandler()); actions.put(CheckVersionActionHandler.getId(), new CheckVersionActionHandler()); actions.put(CheckStorageAvailabilityActionHandler.getId(), new CheckStorageAvailabilityActionHandler()); actions.put(CheckObjectUnitConsistencyActionHandler.getId(), new CheckObjectUnitConsistencyActionHandler()); actions.put(AccessionRegisterActionHandler.getId(), new AccessionRegisterActionHandler()); actions.put(TransferNotificationActionHandler.getId(), new TransferNotificationActionHandler()); actions.put(DummyHandler.getId(), new DummyHandler()); actions.put(CommitLifeCycleUnitActionHandler.getId(), new CommitLifeCycleUnitActionHandler()); actions.put(CommitLifeCycleObjectGroupActionHandler.getId(), new CommitLifeCycleObjectGroupActionHandler()); actions.put(RollBackActionHandler.getId(), new RollBackActionHandler()); } @Override public ItemStatus run(WorkerParameters workParams, Step step) throws IllegalArgumentException, ProcessingException, ContentAddressableStorageServerException { // mandatory check ParameterHelper.checkNullOrEmptyParameters(workParams); if (step == null) { throw new IllegalArgumentException(STEP_NULL); } if (step.getActions() == null || step.getActions().isEmpty()) { throw new IllegalArgumentException(EMPTY_LIST); } final ItemStatus responses = new ItemStatus(step.getStepName()); try (final HandlerIO handlerIO = new HandlerIOImpl(workParams.getContainerName(), workerId); LogbookLifeCyclesClient logbookLfcClient = LogbookLifeCyclesClientFactory.getInstance().getClient()) { for (final Action action : step.getActions()) { // Reset handlerIO for next execution handlerIO.reset(); ActionDefinition actionDefinition = action.getActionDefinition(); if (actionDefinition.getIn() != null) { handlerIO.addInIOParameters(actionDefinition.getIn()); } if (actionDefinition.getOut() != null) { handlerIO.addOutIOParameters(actionDefinition.getOut()); } String handlerName = actionDefinition.getActionKey(); ItemStatus actionResponse; // TODO Généralisation tous les workers // If this is a plugin if (pluginLoader.contains(handlerName)) { try (ActionHandler actionPlugin = pluginLoader.newInstance(handlerName)) { ItemStatus pluginResponse; LOGGER.debug("START plugin ", actionDefinition.getActionKey(), step.getStepName()); boolean shouldWriteLFC = step.getDistribution().getKind().equals(DistributionKind.LIST); if (shouldWriteLFC) { LogbookLifeCycleParameters lfcParam = createStartLogbookLfc(step, handlerName, workParams); logbookLfcClient.update(lfcParam); pluginResponse = actionPlugin.execute(workParams, handlerIO); writeLogBookLfcFromResponse(handlerName, logbookLfcClient, pluginResponse, lfcParam); } else { pluginResponse = actionPlugin.execute(workParams, handlerIO); } pluginResponse.setItemId(handlerName); actionResponse = getActionResponse(handlerName, pluginResponse); } // If not, this is an handler of Vitam } else { final ActionHandler actionHandler = getActionHandler(handlerName); LOGGER.debug("START handler {} in step {}", actionDefinition.getActionKey(), step.getStepName()); if (actionHandler == null) { throw new HandlerNotFoundException(actionDefinition.getActionKey() + HANDLER_NOT_FOUND); } actionResponse = actionHandler.execute(workParams, handlerIO); } responses.setItemsStatus(actionResponse); LOGGER.debug("STOP handler {} in step {}", actionDefinition.getActionKey(), step.getStepName()); // if the action has been defined as Blocking and the action status is KO or FATAL // then break the process if (actionResponse.shallStop(ProcessBehavior.BLOCKING.equals(actionDefinition.getBehavior()))) { break; } } } catch (Exception e) { throw new ProcessingException(e); } LOGGER.debug("step name :" + step.getStepName()); return responses; } private ItemStatus getActionResponse(String handlerName, ItemStatus pluginResponse) { ItemStatus status = new ItemStatus(handlerName); for (final Entry<String, ItemStatus> entry : pluginResponse.getItemsStatus().entrySet()) { ItemStatus subItemStatus = entry.getValue(); subItemStatus.setItemId(handlerName); status.setItemsStatus(handlerName, subItemStatus); } return status; } private LogbookLifeCycleParameters createStartLogbookLfc(Step step, String handlerName, WorkerParameters workParams) throws InvalidGuidOperationException { LogbookLifeCycleParameters lfcParam = null; if (step.getDistribution().getElement() .equals(LogbookType.UNITS.getType())) { lfcParam = LogbookParametersFactory.newLogbookLifeCycleUnitParameters( GUIDFactory.newEventGUID(ParameterHelper.getTenantParameter()), VitamLogbookMessages.getEventTypeLfc(handlerName), GUIDReader.getGUID(workParams.getContainerName()), // TODO Le type de process devrait venir du message recu (paramètre du workflow) LogbookTypeProcess.INGEST, StatusCode.STARTED, VitamLogbookMessages.getOutcomeDetailLfc(handlerName, StatusCode.STARTED), VitamLogbookMessages.getCodeLfc(handlerName, StatusCode.STARTED), GUIDReader.getGUID(LogbookLifecycleWorkerHelper.getObjectID(workParams))); } else if (step.getDistribution().getElement() .equals(LogbookType.OBJECTGROUP.getType())) { lfcParam = LogbookParametersFactory.newLogbookLifeCycleObjectGroupParameters( GUIDFactory.newEventGUID(ParameterHelper.getTenantParameter()), VitamLogbookMessages.getEventTypeLfc(handlerName), GUIDReader.getGUID(workParams.getContainerName()), // TODO Le type de process devrait venir du message recu (paramètre du workflow) LogbookTypeProcess.INGEST, StatusCode.STARTED, VitamLogbookMessages.getOutcomeDetailLfc(handlerName, StatusCode.STARTED), VitamLogbookMessages.getCodeLfc(handlerName, StatusCode.STARTED), GUIDReader.getGUID(LogbookLifecycleWorkerHelper.getObjectID(workParams))); } return lfcParam; } private void writeLogBookLfcFromResponse(String handlerName, LogbookLifeCyclesClient logbookLfcClient, ItemStatus actionResponse, LogbookLifeCycleParameters logbookParam) throws LogbookClientBadRequestException, LogbookClientNotFoundException, LogbookClientServerException { List<LogbookLifeCycleParameters> logbookParamList = new ArrayList<>(); LogbookLifeCycleParameters finalLogbookLfcParam = LogbookLifeCyclesClientHelper.copy(logbookParam); finalLogbookLfcParam.setFinalStatus(handlerName, null, actionResponse.getGlobalStatus(), actionResponse.getMessage()); logbookParamList.add(finalLogbookLfcParam); for (final Entry<String, ItemStatus> entry : actionResponse.getItemsStatus().entrySet()) { for (final Entry<String, ItemStatus> subTaskEntry : entry.getValue().getSubTaskStatus().entrySet()) { LogbookLifeCycleParameters subLogbookLfcParam = LogbookLifeCyclesClientHelper.copy(logbookParam); ItemStatus subItemStatus = subTaskEntry.getValue(); subLogbookLfcParam.setFinalStatus(handlerName, entry.getKey(), subItemStatus.getGlobalStatus(), subItemStatus.getMessage()); if (!subItemStatus.getEvDetailData().isEmpty()) { subLogbookLfcParam.putParameterValue(LogbookParameterName.eventDetailData, subItemStatus.getEvDetailData()); } logbookParamList.add(subLogbookLfcParam); } entry.getValue().getSubTaskStatus().clear(); } for (int i = logbookParamList.size() - 1; i >= 0; i--) { logbookLfcClient.update(logbookParamList.get(i)); } } private ActionHandler getActionHandler(String actionId) throws PluginNotFoundException { return actions.get(actionId); } @Override public String getWorkerId() { return workerId; } @Override public void close() { actions.clear(); } }