AbstractTaskUnitAssignment.java
/*
* Copyright (c) 2002-2021, 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.unittree.service.task;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.collections.CollectionUtils;
import fr.paris.lutece.plugins.unittree.business.assignment.UnitAssignment;
import fr.paris.lutece.plugins.unittree.business.assignment.UnitAssignmentHome;
import fr.paris.lutece.plugins.unittree.business.assignment.UnitAssignmentType;
import fr.paris.lutece.plugins.unittree.business.unit.Unit;
import fr.paris.lutece.plugins.unittree.exception.AssignmentNotPossibleException;
import fr.paris.lutece.plugins.unittree.service.selection.IUnitSelection;
import fr.paris.lutece.plugins.unittree.service.unit.IUnitService;
import fr.paris.lutece.plugins.workflow.modules.unittree.business.assignment.task.config.TaskUnitAssignmentConfig;
import fr.paris.lutece.plugins.workflow.modules.unittree.business.task.information.TaskInformation;
import fr.paris.lutece.plugins.workflow.modules.unittree.business.task.information.TaskInformationHome;
import fr.paris.lutece.plugins.workflow.modules.unittree.service.task.selection.UnitSelectionService;
import fr.paris.lutece.plugins.workflow.modules.unittree.util.ChangeUnitEventPublisher;
import fr.paris.lutece.plugins.workflowcore.business.resource.ResourceHistory;
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.task.SimpleTask;
import fr.paris.lutece.portal.service.util.AppException;
/**
* This class is an abstract task to assign a resource to a unit
*
*/
public abstract class AbstractTaskUnitAssignment extends SimpleTask
{
public static final int UNSET_ASSIGNED_UNIT_ID = -1;
// Informations
private static final String TASK_INFORMATION_ASSIGNED_UNIT = "ASSIGNED_UNIT";
private static final String TASK_INFORMATION_ASSIGNOR_UNIT = "ASSIGNOR_UNIT";
private static final int UNFOUND_INDEX = UNSET_ASSIGNED_UNIT_ID;
// Services
@Inject
private IUnitService _unitService;
@Inject
@Named( "workflow-unittree.taskUnitAssignmentConfigService" )
private ITaskConfigService _taskConfigService;
@Inject
private IResourceHistoryService _resourceHistoryService;
@Inject
private ChangeUnitEventPublisher _publisher;
/**
* {@inheritDoc}
*/
@Override
public void processTask( int nIdResourceHistory, HttpServletRequest request, Locale locale )
{
ResourceHistory resourceHistory = _resourceHistoryService.findByPrimaryKey( nIdResourceHistory );
if ( resourceHistory != null )
{
try
{
IUnitSelection unitSelection = fetchUnitSelection( request );
if ( unitSelection == null )
{
throw new AppException( "There is no unit selection" );
}
int nIdUnit = unitSelection.select( resourceHistory.getIdResource( ), resourceHistory.getResourceType( ), request, this );
Unit unitAssigned = _unitService.getUnit( nIdUnit, false );
if ( unitAssigned == null )
{
throw new AppException( "The target unit does not exist" );
}
List<UnitAssignment> listUnitAssignment = UnitAssignmentHome.findByResource( resourceHistory.getIdResource( ),
resourceHistory.getResourceType( ) );
Unit unitAssignor = findAssignorUnit( listUnitAssignment );
TaskUnitAssignmentConfig config = getConfig( );
UnitAssignmentType unitAssignmentType = UnitAssignmentType.findByCode( config.getAssignmentType( ) );
UnitAssignment unitAssignment = createUnitAssignment( resourceHistory.getIdResource( ), resourceHistory.getResourceType( ), unitAssignmentType,
unitAssignor, unitAssigned );
manageUnitAssignments( listUnitAssignment, unitAssignment );
saveTaskInformation( nIdResourceHistory, unitAssigned, unitAssignor );
}
catch( AssignmentNotPossibleException e )
{
throw new AppException( e.getMessage( ), e );
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void doRemoveConfig( )
{
TaskUnitAssignmentConfig config = getConfig( );
if ( config != null )
{
for ( String strUnitSelectionId : config.getUnitSelections( ) )
{
IUnitSelection unitSelection = UnitSelectionService.getInstance( ).find( strUnitSelectionId );
if ( unitSelection != null )
{
unitSelection.getConfigurationHandler( ).removeConfiguration( this );
}
}
}
_taskConfigService.remove( this.getId( ) );
}
/**
* Gives the configuration associated to this task
*
* @return the configuration
*/
protected TaskUnitAssignmentConfig getConfig( )
{
return _taskConfigService.findByPrimaryKey( getId( ) );
}
/**
* <p>
* Finds the assignor unit among the specified unit assignment list.
* </p>
* <p>
* The assignor unit is the assigned unit of the last activated unit assignment.
* </p>
*
* @param listUnitAssignment
* the unit assignment list
* @return the assignor unit
*/
private Unit findAssignorUnit( List<UnitAssignment> listUnitAssignment )
{
Unit unit = null;
for ( int i = listUnitAssignment.size( ) - 1; i >= 0; i-- )
{
UnitAssignment unitAssignment = listUnitAssignment.get( i );
if ( unitAssignment.isActive( ) )
{
unit = unitAssignment.getAssignedUnit( );
break;
}
}
return unit;
}
/**
* Creates a unit assignment
*
* @param nIdResource
* the resource id
* @param strResourceType
* the resource type
* @param unitAssignmentType
* the assignment type
* @param unitAssignor
* the assignor unit
* @param unitAssigned
* the assigned unit
* @return the created unit assignment
*/
private UnitAssignment createUnitAssignment( int nIdResource, String strResourceType, UnitAssignmentType unitAssignmentType, Unit unitAssignor,
Unit unitAssigned )
{
UnitAssignment unitAssignment = new UnitAssignment( );
unitAssignment.setIdResource( nIdResource );
unitAssignment.setResourceType( strResourceType );
unitAssignment.setIdAssignedUnit( unitAssigned.getIdUnit( ) );
if ( unitAssignor == null )
{
unitAssignment.setIdAssignorUnit( UNSET_ASSIGNED_UNIT_ID );
}
else
{
unitAssignment.setIdAssignorUnit( unitAssignor.getIdUnit( ) );
}
unitAssignment.setAssignmentType( unitAssignmentType );
unitAssignment.setActive( true );
return unitAssignment;
}
/**
* Manages the unit assignments (creation, activation / deactivation)
*
* @param listUnitAssignment
* the unit assignment already present
* @param unitAssignmentNew
* the new unit assignment
*/
private void manageUnitAssignments( List<UnitAssignment> listUnitAssignment, UnitAssignment unitAssignmentNew )
{
List<UnitAssignment> listUnitAssignmentToDeactivate = buildUnitAssignmentListToDeactivate( listUnitAssignment, unitAssignmentNew );
if ( UnitAssignmentType.ASSIGN_DOWN == unitAssignmentNew.getAssignmentType( ) && listUnitAssignmentToDeactivate.isEmpty( ) )
{
throw new AppException( "Cannot assign down to a unit which has not previously been assigned by assign up" );
}
deactivateUnitAssignments( listUnitAssignmentToDeactivate );
if ( CollectionUtils.isNotEmpty( listUnitAssignmentToDeactivate ) )
{
_publisher.publish( listUnitAssignmentToDeactivate );
}
// ASSIGN DOWN are not recorded
if ( UnitAssignmentType.ASSIGN_DOWN != unitAssignmentNew.getAssignmentType( ) )
{
UnitAssignmentHome.create( unitAssignmentNew );
}
}
/**
* Builds the list of unit assignments to deactivate
*
* @param listUnitAssignment
* the unit assignment already present
* @param unitAssignmentNew
* the new unit assignment
* @return the list of unit assignments to deactivate
*/
private List<UnitAssignment> buildUnitAssignmentListToDeactivate( List<UnitAssignment> listUnitAssignment, UnitAssignment unitAssignmentNew )
{
List<UnitAssignment> result = new ArrayList<>( );
UnitAssignmentType assignmentType = unitAssignmentNew.getAssignmentType( );
if ( UnitAssignmentType.CREATION == assignmentType || UnitAssignmentType.TRANSFER == assignmentType )
{
result.addAll( listUnitAssignment.stream( ).filter( UnitAssignment::isActive ).collect( Collectors.toList( ) ) );
}
if ( UnitAssignmentType.ASSIGN_DOWN == assignmentType || UnitAssignmentType.ASSIGN_UP == assignmentType )
{
List<UnitAssignment> listTmp = new ArrayList<>( );
// First, finds assignments since the last CREATION or TRANSFER
for ( UnitAssignment unitAssignment : listUnitAssignment )
{
if ( UnitAssignmentType.CREATION == unitAssignment.getAssignmentType( ) || UnitAssignmentType.TRANSFER == unitAssignment.getAssignmentType( ) )
{
listTmp.clear( );
}
listTmp.add( unitAssignment );
}
// Second, finds the index of the original assignment
int nIndex = UNFOUND_INDEX;
for ( int i = 0; i < listTmp.size( ); i++ )
{
UnitAssignment unitAssignment = listTmp.get( i );
if ( unitAssignment.isActive( ) && unitAssignment.getAssignedUnit( ).getIdUnit( ) == unitAssignmentNew.getAssignedUnit( ).getIdUnit( ) )
{
nIndex = i;
break;
}
}
// Third, keeps only assignments before the current assignment
if ( UNFOUND_INDEX != nIndex )
{
// The assignment is already in the list, the new unit assignment must be deactivated to use the original assignment
unitAssignmentNew.setActive( false );
// Do not include the original assignment
nIndex++;
for ( int i = nIndex; i < listTmp.size( ); i++ )
{
result.add( listTmp.get( i ) );
}
}
}
return result;
}
/**
* Deactivates the specified unit assignments
*
* @param listUnitAssignment
* the list of unit assignments to deactivate
*/
private void deactivateUnitAssignments( List<UnitAssignment> listUnitAssignment )
{
for ( UnitAssignment unitAssignment : listUnitAssignment )
{
UnitAssignmentHome.deactivate( unitAssignment );
}
}
/**
* Saves the task information
*
* @param nIdResourceHistory
* the resource history id
* @param unitAssigned
* the assigned unit
* @param unitAssignor
* the assignor unit
*/
private void saveTaskInformation( int nIdResourceHistory, Unit unitAssigned, Unit unitAssignor )
{
TaskInformation taskInformation = new TaskInformation( nIdResourceHistory, getId( ) );
taskInformation.add( TASK_INFORMATION_ASSIGNED_UNIT, unitAssigned.getLabel( ) );
if ( unitAssignor != null )
{
taskInformation.add( TASK_INFORMATION_ASSIGNOR_UNIT, unitAssignor.getLabel( ) );
}
TaskInformationHome.create( taskInformation );
}
@Override
public void doRemoveTaskInformation( int nIdHistory )
{
ResourceHistory history = _resourceHistoryService.findByPrimaryKey( nIdHistory );
TaskInformationHome.delete( history.getIdResource( ), getId( ) );
}
/**
* Gives the unit selection to use
*
* @param request
* the request which can contain information to fetch the unit selection
* @return the unit selection
*/
protected abstract IUnitSelection fetchUnitSelection( HttpServletRequest request );
}