ArchiveService.java

/*
 * Copyright (c) 2002-2022, City of Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.plugins.workflow.modules.archive.service;

import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Locale;

import javax.inject.Inject;
import javax.inject.Named;

import fr.paris.lutece.plugins.workflow.modules.archive.IResourceArchiver;
import fr.paris.lutece.plugins.workflow.modules.archive.WorkflowResourceArchiver;
import fr.paris.lutece.plugins.workflow.modules.archive.business.ArchiveConfig;
import fr.paris.lutece.plugins.workflow.modules.archive.business.ArchiveResource;
import fr.paris.lutece.plugins.workflow.modules.archive.business.IArchiveResourceDao;
import fr.paris.lutece.plugins.workflow.utils.WorkflowUtils;
import fr.paris.lutece.plugins.workflowcore.business.action.Action;
import fr.paris.lutece.plugins.workflowcore.business.resource.IResourceHistoryDAO;
import fr.paris.lutece.plugins.workflowcore.business.resource.IResourceWorkflowDAO;
import fr.paris.lutece.plugins.workflowcore.business.resource.ResourceHistory;
import fr.paris.lutece.plugins.workflowcore.business.resource.ResourceWorkflow;
import fr.paris.lutece.plugins.workflowcore.business.state.State;
import fr.paris.lutece.plugins.workflowcore.service.action.IActionService;
import fr.paris.lutece.plugins.workflowcore.service.config.ITaskConfigService;
import fr.paris.lutece.plugins.workflowcore.service.resource.IResourceHistoryService;
import fr.paris.lutece.plugins.workflowcore.service.resource.IResourceWorkflowService;
import fr.paris.lutece.plugins.workflowcore.service.state.IStateService;
import fr.paris.lutece.plugins.workflowcore.service.task.ITask;
import fr.paris.lutece.portal.service.i18n.I18nService;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.service.workflow.WorkflowService;

/**
 * Implements {@link IArchiveService}
 */
public class ArchiveService implements IArchiveService
{
    public static final String BEAN_SERVICE = "workflow.archiveService";

    @Inject
    @Named( "workflow.taskArchiveConfigService" )
    private ITaskConfigService _taskArchiveConfigService;

    @Inject
    private IResourceHistoryDAO _resourceHistoryDAO;

    @Inject
    private IResourceWorkflowDAO _resourceWorkflowDAO;

    @Inject
    private IArchiveResourceDao _archiveResourceDao;

    @Inject
    private IActionService _actionService;

    @Inject
    private IStateService _stateService;

    @Inject
    private IResourceHistoryService _resourceHistoryService;

    @Inject
    private IResourceWorkflowService _resourceWorkflowService;

    private static final String USER_AUTO = "auto";

    @Override
    public ArchiveConfig loadConfig( ITask task )
    {
        return _taskArchiveConfigService.findByPrimaryKey( task.getId( ) );
    }

    @Override
    public ArchiveResource getArchiveResource( int nIdHistory, int nIdTask )
    {
        ResourceHistory history = _resourceHistoryDAO.load( nIdHistory );
        if ( history == null )
        {
            return null;
        }

        ResourceWorkflow resource = _resourceWorkflowDAO.load( history.getIdResource( ), history.getResourceType( ), history.getWorkflow( ).getId( ) );
        if ( resource == null )
        {
            return null;
        }
        return _archiveResourceDao.load( resource.getIdResource( ), nIdTask );
    }

    @Override
    public void createArchiveResource( ResourceWorkflow resourceWorkflow, ArchiveConfig config )
    {
        ArchiveResource archiveResource = _archiveResourceDao.load( resourceWorkflow.getIdResource( ), config.getIdTask( ) );
        if ( archiveResource == null )
        {
            archiveResource = new ArchiveResource( );
            archiveResource.setIdResource( resourceWorkflow.getIdResource( ) );
            archiveResource.setIdTask( config.getIdTask( ) );
            archiveResource.setIsArchived( false );
            archiveResource.setInitialDate( Timestamp.valueOf( LocalDateTime.now( ) ) );
            _archiveResourceDao.insert( archiveResource );
        }
    }

    @Override
    public boolean isResourceUpForArchival( ResourceWorkflow resourceWorkflow, ArchiveConfig config )
    {
        if ( config.getDelayArchival( ) <= 0 )
        {
            return true;
        }
        ArchiveResource archiveResource = _archiveResourceDao.load( resourceWorkflow.getIdResource( ), config.getIdTask( ) );

        if ( archiveResource == null || archiveResource.isArchived( ) )
        {
            return false;
        }
        return LocalDateTime.now( ).isAfter( calculateArchivalDate( archiveResource, config.getDelayArchival( ) ) );
    }

    @Override
    public ResourceWorkflow getResourceWorkflowByHistory( int nIdHistory )
    {
        ResourceHistory history = _resourceHistoryDAO.load( nIdHistory );
        if ( history == null )
        {
            return null;
        }

        return _resourceWorkflowDAO.load( history.getIdResource( ), history.getResourceType( ), history.getWorkflow( ).getId( ) );
    }

    private LocalDateTime calculateArchivalDate( ArchiveResource archiveResource, int daysBeforeArchival )
    {
        if ( daysBeforeArchival > 0 )
        {
            return archiveResource.getInitialDate( ).toLocalDateTime( ).plusDays( daysBeforeArchival );
        }
        return LocalDate.now( ).atStartOfDay( );
    }

    @Override
    public void archiveResource( ResourceWorkflow resourceWorkflow, ITask task, ArchiveConfig config )
    {
        if ( config.getNextState( ) != resourceWorkflow.getState( ).getId( ) )
        {
            doChangeState( task, resourceWorkflow.getIdResource( ), resourceWorkflow.getResourceType( ), resourceWorkflow.getWorkflow( ).getId( ),
                    config.getNextState( ) );
        }
        List<IResourceArchiver> archiverList = SpringContextService.getBeansOfType( IResourceArchiver.class );
        IResourceArchiver lastArchiver = null;

        for ( IResourceArchiver archiver : archiverList )
        {
            if ( WorkflowResourceArchiver.BEAN_NAME.equals( archiver.getBeanName( ) ) )
            {
                lastArchiver = archiver;
            }
            else
            {
                archiver.archiveResource( config.getTypeArchival( ), resourceWorkflow );
            }
        }
        if ( lastArchiver != null )
        {
            lastArchiver.archiveResource( config.getTypeArchival( ), resourceWorkflow );
        }

        ArchiveResource archiveResource = _archiveResourceDao.load( resourceWorkflow.getIdResource( ), config.getIdTask( ) );
        // If the archival process is not a full deletion and keeps the archiveResource,
        // it must be updated
        if ( archiveResource != null )
        {
            archiveResource.setArchivalDate( Timestamp.valueOf( LocalDateTime.now( ) ) );
            archiveResource.setIsArchived( true );
            _archiveResourceDao.store( archiveResource );
        }
    }

    private void doChangeState( ITask task, int nIdResource, String strResourceType, int nIdWorkflow, int newState )
    {
        Locale locale = I18nService.getDefaultLocale( );
        State state = _stateService.findByPrimaryKey( newState );
        Action action = _actionService.findByPrimaryKey( task.getAction( ).getId( ) );

        if ( state != null && action != null )
        {

            // Create Resource History
            ResourceHistory resourceHistory = new ResourceHistory( );
            resourceHistory.setIdResource( nIdResource );
            resourceHistory.setResourceType( strResourceType );
            resourceHistory.setAction( action );
            resourceHistory.setWorkFlow( action.getWorkflow( ) );
            resourceHistory.setCreationDate( WorkflowUtils.getCurrentTimestamp( ) );
            resourceHistory.setUserAccessCode( USER_AUTO );
            _resourceHistoryService.create( resourceHistory );

            // Update Resource
            ResourceWorkflow resourceWorkflow = _resourceWorkflowService.findByPrimaryKey( nIdResource, strResourceType, nIdWorkflow );
            resourceWorkflow.setState( state );
            _resourceWorkflowService.update( resourceWorkflow );

            // Execute the relative tasks of the state in the workflow
            // We use AutomaticReflexiveActions because we don't want to change the state of the resource by executing actions.
            WorkflowService.getInstance( ).doProcessAutomaticReflexiveActions( nIdResource, strResourceType, state.getId( ), null, locale, null );
        }
    }

    @Override
    public void removeArchiveResource( int idResource, int idTask )
    {
        _archiveResourceDao.delete( idResource, idTask );
    }

    @Override
    public void removeConfig( ITask task )
    {
        _taskArchiveConfigService.remove( task.getId( ) );
    }
}