View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.workflow.service.json;
35  
36  import java.sql.Timestamp;
37  import java.util.ArrayList;
38  import java.util.Collection;
39  import java.util.HashMap;
40  import java.util.List;
41  import java.util.Locale;
42  import java.util.Map;
43  import java.util.UUID;
44  import java.util.stream.Collectors;
45  
46  import org.apache.commons.collections.CollectionUtils;
47  import org.springframework.transaction.annotation.Transactional;
48  
49  import com.fasterxml.jackson.core.JsonProcessingException;
50  import com.fasterxml.jackson.databind.DeserializationFeature;
51  import com.fasterxml.jackson.databind.ObjectMapper;
52  import com.fasterxml.jackson.databind.module.SimpleModule;
53  
54  import fr.paris.lutece.plugins.workflow.service.prerequisite.PrerequisiteManagementService;
55  import fr.paris.lutece.plugins.workflow.service.task.TaskFactory;
56  import fr.paris.lutece.plugins.workflow.utils.WorkflowUtils;
57  import fr.paris.lutece.plugins.workflowcore.business.action.Action;
58  import fr.paris.lutece.plugins.workflowcore.business.action.ActionFilter;
59  import fr.paris.lutece.plugins.workflowcore.business.prerequisite.Prerequisite;
60  import fr.paris.lutece.plugins.workflowcore.business.state.State;
61  import fr.paris.lutece.plugins.workflowcore.business.workflow.Workflow;
62  import fr.paris.lutece.plugins.workflowcore.service.action.ActionService;
63  import fr.paris.lutece.plugins.workflowcore.service.action.IActionService;
64  import fr.paris.lutece.plugins.workflowcore.service.prerequisite.IAutomaticActionPrerequisiteService;
65  import fr.paris.lutece.plugins.workflowcore.service.prerequisite.IPrerequisiteManagementService;
66  import fr.paris.lutece.plugins.workflowcore.service.state.IStateService;
67  import fr.paris.lutece.plugins.workflowcore.service.state.StateService;
68  import fr.paris.lutece.plugins.workflowcore.service.task.ITask;
69  import fr.paris.lutece.plugins.workflowcore.service.task.ITaskFactory;
70  import fr.paris.lutece.plugins.workflowcore.service.task.ITaskService;
71  import fr.paris.lutece.plugins.workflowcore.service.task.TaskService;
72  import fr.paris.lutece.plugins.workflowcore.service.workflow.IWorkflowService;
73  import fr.paris.lutece.plugins.workflowcore.service.workflow.WorkflowService;
74  import fr.paris.lutece.portal.service.i18n.I18nService;
75  import fr.paris.lutece.portal.service.spring.SpringContextService;
76  
77  public class WorkflowJsonService
78  {
79      private static final String PROPERTY_COPY_WF_TITLE = "workflow.manage_workflow.copy_of_workflow";
80  
81      public static final WorkflowJsonServiceervice/json/WorkflowJsonService.html#WorkflowJsonService">WorkflowJsonService INSTANCE = new WorkflowJsonService( );
82  
83      private ObjectMapper _objectMapper;
84  
85      private IWorkflowService _workflowService = SpringContextService.getBean( WorkflowService.BEAN_SERVICE );
86      private IStateService _stateService = SpringContextService.getBean( StateService.BEAN_SERVICE );
87      private IActionService _actionService = SpringContextService.getBean( ActionService.BEAN_SERVICE );
88      private ITaskService _taskService = SpringContextService.getBean( TaskService.BEAN_SERVICE );
89      private ITaskFactory _taskFactory = SpringContextService.getBean( TaskFactory.BEAN_SERVICE );
90      private IPrerequisiteManagementService _prerequisiteManagementService = SpringContextService.getBean( PrerequisiteManagementService.BEAN_NAME );
91  
92      private WorkflowJsonService( )
93      {
94          SimpleModule timestampModule = new SimpleModule( "TimestampModule" );
95          timestampModule.addSerializer( Timestamp.class, new TimestampSerializer( ) );
96          timestampModule.addDeserializer( Timestamp.class, new TimestampDeserializer( ) );
97  
98          SimpleModule taskModule = new SimpleModule( "TaskModule" );
99          taskModule.addSerializer( ITask.class, new TaskSerialiser( ) );
100         taskModule.addDeserializer( ITask.class, new TaskDeserializer( ) );
101 
102         _objectMapper = new ObjectMapper( ).configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false ).registerModule( timestampModule )
103                 .registerModule( taskModule );
104     }
105 
106     public static WorkflowJsonService getInstance( )
107     {
108         return INSTANCE;
109     }
110 
111     /**
112      * Export the workflow as a Json Object.
113      * 
114      * @return
115      * @throws JsonProcessingException
116      */
117     public String jsonExportWorkflow( int idWorkflow ) throws JsonProcessingException
118     {
119         WorkflowJsonDataw/service/json/WorkflowJsonData.html#WorkflowJsonData">WorkflowJsonData jsonData = new WorkflowJsonData( );
120 
121         Workflow workflow = _workflowService.findByPrimaryKey( idWorkflow );
122         if( workflow.getUid( ) == null ){
123             workflow.setUid( UUID.randomUUID( ).toString( ) );
124         }
125         jsonData.setWorkflow( workflow );
126 
127         List<State> stateList = (List<State>) _workflowService.getAllStateByWorkflow( workflow.getId( ) );
128         stateList.forEach(state -> {
129             if( state.getUid( ) == null ){
130                 state.setUid( UUID.randomUUID( ).toString( ) );
131             }
132         });
133         jsonData.setStateList( stateList );
134 
135         List<Action> actionList = new ArrayList<>( );
136 
137         ActionFilter filter = new ActionFilter( );
138         filter.setIdWorkflow( workflow.getId( ) );
139         actionList.addAll( _actionService.getListActionByFilter( filter ) );
140         actionList.forEach(action -> {
141             if( action.getUid( ) == null ){
142                 action.setUid( UUID.randomUUID( ).toString( ) );
143             }
144         });
145        
146         for( Action action : actionList ) 
147         {       	
148         	State actionStateAfter = _stateService.findByPrimaryKey( action.getStateAfter( ).getId( ) );
149     		action.setStrUidStateAfter( actionStateAfter.getUid( ) );
150     		
151     		if(action.getAlternativeStateAfter().getId() != -1) {
152             	State actionAlternativeStateAfter = _stateService.findByPrimaryKey( action.getAlternativeStateAfter( ).getId( ) );
153         		action.setStrUidAlternativeStateAfter( actionAlternativeStateAfter.getUid( ) );
154     		}
155     		
156     		List<String> listUidStateBefore = new ArrayList<>( );
157     		action.getListIdStateBefore( ).forEach( stateId -> 
158     		{
159     			listUidStateBefore.add( _stateService.findByPrimaryKey( stateId ).getUid( ) );
160     		});
161     		action.setListUidStateBefore( listUidStateBefore );
162         }
163         
164         jsonData.setActionList( actionList );
165 
166         List<ITask> taskList = new ArrayList<>( );
167         List<Prerequisite> prerequisiteList = new ArrayList<>( );
168         for ( Action action : actionList )
169         {
170             taskList.addAll( _taskService.getListTaskByIdAction( action.getId( ), Locale.getDefault( ) ) );
171             prerequisiteList.addAll( _prerequisiteManagementService.getListPrerequisite( action.getId( ) ) );
172         }
173 
174         taskList.forEach(task -> {
175             if( task.getUid( ) == null ){
176                 task.setUid( UUID.randomUUID( ).toString( ) );
177             }
178         });
179         
180         for ( ITask task : taskList ) 
181         {
182         	task.setActionUid( _actionService.findByPrimaryKey( task.getAction( ).getId( ) ).getUid( ) );
183         }
184 
185         prerequisiteList.forEach(prerequisite -> {
186             if( prerequisite.getUidPrerequisite( ) == null ){
187                 prerequisite.setUidPrerequisite( UUID.randomUUID( ).toString( ) );
188             }
189         });
190         
191         for( Prerequisite prerequisite : prerequisiteList ) 
192         {
193         	prerequisite.setUidAction( _actionService.findByPrimaryKey( prerequisite.getIdAction() ).getUid( ) );
194         }
195 
196         jsonData.setTaskList( taskList );
197         jsonData.setPrerequisiteList( prerequisiteList );
198         return _objectMapper.writeValueAsString( jsonData );
199     }
200 
201     @Transactional
202     public void jsonImportWorkflow( String json, Locale locale ) throws JsonProcessingException
203     {
204         WorkflowJsonData jsonData = _objectMapper.readValue( json, WorkflowJsonData.class );
205 
206         int newIdWf = importWorkflow( jsonData.getWorkflow( ), locale );
207 
208         List<State> stateList = jsonData.getStateList( );
209         List<Action> actionList = jsonData.getActionList( );
210         List<ITask> taskList = jsonData.getTaskList( );
211         List<Prerequisite> prerequisiteList = jsonData.getPrerequisiteList( );
212 
213         importStates( newIdWf, stateList, actionList );
214         importActions( actionList, taskList, prerequisiteList );
215         importTasks( taskList );
216         importPrerequisites( prerequisiteList );
217     }
218 
219     public int importWorkflow( Workflow workflow, Locale locale )
220     {
221         String strNameCopyWf = I18nService.getLocalizedString( PROPERTY_COPY_WF_TITLE, locale );
222         if ( strNameCopyWf != null )
223         {
224             workflow.setName( strNameCopyWf + workflow.getName( ) );
225         }
226         workflow.setEnabled( false );
227         workflow.setCreationDate( WorkflowUtils.getCurrentTimestamp( ) );
228 
229         _workflowService.create( workflow );
230 
231         return workflow.getId( );
232     }
233 
234     private void importStates( int newIdWf, List<State> stateList, List<Action> actionList )
235     {
236         Map<String, Integer> mapIdStates = new HashMap<>( );
237         Workflow workflow = _workflowService.findByPrimaryKey( newIdWf );
238 
239         for ( State state : stateList )
240         {
241             String uid = state.getUid( );
242             state.setWorkflow( workflow );
243             _stateService.create( state );
244 
245             int newId = state.getId( );
246             mapIdStates.put( uid, newId );
247         }
248         updateActionWithStateAndWf( newIdWf, actionList, mapIdStates );
249     }
250 
251     private void updateActionWithStateAndWf( int newIdWf, List<Action> actionList, Map<String, Integer> mapIdStates )
252     {
253     	Workflow workflow = _workflowService.findByPrimaryKey( newIdWf );
254         for ( Action action : actionList )
255         {
256             action.setWorkflow( workflow );
257             action.setStateAfter( _stateService.findByPrimaryKey( mapIdStates.get( action.getStrUidStateAfter( ) ) ) );
258             
259             if(action.getStrUidAlternativeStateAfter( ) != null) {
260             	action.setAlternativeStateAfter( _stateService.findByPrimaryKey( mapIdStates.get( action.getStrUidAlternativeStateAfter( ) ) ) );
261             }
262             updateStateBefore(action, mapIdStates);
263         }
264     }
265     
266     private void updateStateBefore(Action action, Map<String, Integer> mapIdStates) {
267     	if(action.getListUidStateBefore( ) != null) {
268         	List<Integer> StateBefore = new ArrayList<Integer>();
269             for (String strStateBefore : action.getListUidStateBefore( ) )
270             {
271             	StateBefore.add(mapIdStates.get( strStateBefore ));
272             }
273             action.setListIdStateBefore( StateBefore );
274     	}
275     }
276 
277     private void importActions( List<Action> actionList, List<ITask> taskList, List<Prerequisite> prerequisiteList )
278     {
279         Map<String, Integer> mapIdActions = new HashMap<>( );
280 
281         for ( Action action : actionList )
282         {
283             String uid = action.getUid( );
284             if(uid == null || uid.isEmpty()) 
285             {
286                 action.setListIdStateBefore( new ArrayList<Integer>( ) );
287                 action.setListIdsLinkedAction( new ArrayList<Integer>( ) );
288             }
289             _actionService.create( action );
290 
291 
292             int newId = action.getId( );
293             mapIdActions.put( uid, newId );
294         }
295 
296         // Update Linked Actions
297         for ( Action action : actionList )
298         {
299             Collection<String> listUid = action.getListUidsLinkedAction( );
300 
301             if ( CollectionUtils.isNotEmpty( listUid ) )
302             {
303                 Collection<Integer> listNewId = listUid.stream( ).map( mapIdActions::get ).collect( Collectors.toList( ) );
304                 action.setListIdsLinkedAction( listNewId );
305                 _actionService.update( action );
306             }
307         }
308 
309         updateTaskWithActions( taskList, mapIdActions );
310         updatePrerequisiteWithActions( prerequisiteList, mapIdActions );
311     }
312 
313     private void updateTaskWithActions( List<ITask> taskList, Map<String, Integer> mapIdActions )
314     {
315         for ( ITask task : taskList )
316         {
317             Action action = _actionService.findByPrimaryKey( mapIdActions.get( task.getAction( ).getUid( ) ) );
318             if(action != null) {
319                 task.setAction( action );
320             }
321         }
322     }
323 
324     private void updatePrerequisiteWithActions( List<Prerequisite> prerequisiteList, Map<String, Integer> mapIdActions )
325     {
326         for ( Prerequisite prerequisite : prerequisiteList )
327         {
328             Action action = _actionService.findByPrimaryKey( mapIdActions.get( prerequisite.getUidAction( ) ) );
329             if ( action != null )
330             {  
331                 prerequisite.setIdAction( mapIdActions.get( action.getUid( ) ) );
332             }
333         }
334     }
335 
336     private void importTasks( List<ITask> taskList )
337     {
338         for ( ITask task : taskList )
339         {
340         	if( _taskFactory.getAllTaskTypes( ).stream( ).anyMatch( type -> type.getTitle( ) == task.getTaskType( ).getTitle( ) ) ) {
341             	task.setTaskType( _taskFactory.getAllTaskTypes( ).stream( )
342             			.filter( type -> type.getTitle( ) == task.getTaskType( ).getTitle( ) )
343             			.findFirst( ).orElse(null) );
344                 _taskService.create( task );
345         	}
346         }
347     }
348 
349     private void importPrerequisites( List<Prerequisite> prerequisiteList )
350     {
351     	List<String> PrerequisiteTypeList = new ArrayList<String>();
352     	
353         for ( IAutomaticActionPrerequisiteService service : _prerequisiteManagementService.getPrerequisiteServiceList( ) )
354         {
355         	PrerequisiteTypeList.add( service.getPrerequisiteType( ) );
356         }
357     	
358         for ( Prerequisite prerequisite : prerequisiteList )
359         {
360         	if( PrerequisiteTypeList.contains( prerequisite.getPrerequisiteType( ) ) )
361         	{
362                 _prerequisiteManagementService.createPrerequisite( prerequisite );
363         	}
364         }
365     }
366 }