From 35f9c325f52888860a148c64734745f8e184da1e Mon Sep 17 00:00:00 2001
From: srouthiau <stephane.routhiau@capsi-informatique.fr>
Date: Thu, 21 Nov 2024 17:38:19 +0100
Subject: [PATCH] cda patch singletx sur creer dossier + delayed users sur le
 workerservice

---
 .../repo/impl/CorbeillesServiceImpl.java      |   4 +-
 .../repo/impl/ParapheurServiceImpl.java       |   4 +-
 .../signature/CMSSignatureHandlerV3.java      |   4 +-
 .../libriciel/model/CreerDossieModel.java     |  10 +-
 .../service/CreerDossierService.java          | 393 +++++++++++-------
 .../libriciel/utils/poppler/PopplerUtils.java |  32 +-
 .../repo/amq/impl/MessagesReceiver.java       |  38 +-
 .../repo/jscript/StreamBinaryData.java        |  20 +-
 .../iparapheur/repo/office/RuntimeExec.java   |  57 +--
 .../repo/worker/SchedulerService.java         |  85 +++-
 .../repo/worker/impl/WorkerServiceImpl.java   |   8 +-
 .../util/signature/XadesTimeStamper.java      |  49 ++-
 .../module/parapheur/module-context.xml       |   6 +
 13 files changed, 454 insertions(+), 256 deletions(-)

diff --git a/iparapheur-core/src/main/java/com/atolcd/parapheur/repo/impl/CorbeillesServiceImpl.java b/iparapheur-core/src/main/java/com/atolcd/parapheur/repo/impl/CorbeillesServiceImpl.java
index 32748a1..fcbee26 100644
--- a/iparapheur-core/src/main/java/com/atolcd/parapheur/repo/impl/CorbeillesServiceImpl.java
+++ b/iparapheur-core/src/main/java/com/atolcd/parapheur/repo/impl/CorbeillesServiceImpl.java
@@ -668,11 +668,11 @@ public class CorbeillesServiceImpl extends AbstractLifecycleBean implements Corb
      * PATH:"//*" +TYPE:"{http://www.atolcd.com/alfresco/model/parapheur/1.0}dossier" +@\{http\://www.atolcd.com/alfresco/model/parapheur/1.0\}dateLimite:["1950-01-01T00:00:00.000Z" TO "yyyy-MM-dd'T'HH:mm:ss.000'Z'"]
      */
     private static final String QUERY = "PATH:\"//\\*\"+TYPE:\"{http://www.atolcd.com/alfresco/model/parapheur/1.0}dossier\"+@\\{http\\://www.atolcd.com/alfresco/model/parapheur/1.0\\}dateLimite:[\"1950-01-01T00:00:00.000Z\" TO \"{0}\"]+@\\{http\\://www.atolcd.com/alfresco/model/parapheur/1.0\\}termine:false";
-    private static final DateFormat QUERY_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.000'Z'");
 
     @Override
     public List<NodeRef> findLate() {
-        String query = QUERY.replace("{0}", QUERY_DATE_FORMAT.format(new Date()));
+    	DateFormat QUERY_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.000'Z'");
+    	String query = QUERY.replace("{0}", QUERY_DATE_FORMAT.format(new Date()));
         ResultSet results = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_LUCENE, query);
 
         return results.getNodeRefs();
diff --git a/iparapheur-core/src/main/java/com/atolcd/parapheur/repo/impl/ParapheurServiceImpl.java b/iparapheur-core/src/main/java/com/atolcd/parapheur/repo/impl/ParapheurServiceImpl.java
index e1d602f..4daa15d 100755
--- a/iparapheur-core/src/main/java/com/atolcd/parapheur/repo/impl/ParapheurServiceImpl.java
+++ b/iparapheur-core/src/main/java/com/atolcd/parapheur/repo/impl/ParapheurServiceImpl.java
@@ -6095,7 +6095,7 @@ public final class ParapheurServiceImpl implements ParapheurService, Initializin
                 strSig = new String(signedBytes, docEncoding);
                 String strXPath = (String) this.nodeService.getProperty(dossier, ParapheurModel.PROP_XPATH_SIGNATURE);
 
-                rt.gc();
+                //rt.gc();
                 if (logger.isDebugEnabled()) {
                     logger.debug("Injection '" + docEncoding + "' dans '" + strXPath + "' de Signature:" + strSig.substring(0, (strSig.length() > 40) ? 40 : strSig.length()));
                 }
@@ -6124,7 +6124,7 @@ public final class ParapheurServiceImpl implements ParapheurService, Initializin
                     logger.debug("  Tentative d'injection finie");
                 }
                 // logger.debug("MON PES Signé:\n" + PES_Signe);
-                rt.gc();
+                //rt.gc();
             } catch (UnsupportedEncodingException e) {
                 logger.error("Encoding not supported", e);
             }
diff --git a/iparapheur-core/src/main/java/coop/libriciel/crypto/service/signature/CMSSignatureHandlerV3.java b/iparapheur-core/src/main/java/coop/libriciel/crypto/service/signature/CMSSignatureHandlerV3.java
index 3bdc8a4..fc5fd66 100644
--- a/iparapheur-core/src/main/java/coop/libriciel/crypto/service/signature/CMSSignatureHandlerV3.java
+++ b/iparapheur-core/src/main/java/coop/libriciel/crypto/service/signature/CMSSignatureHandlerV3.java
@@ -19,6 +19,7 @@ import org.alfresco.model.ContentModel;
 import org.alfresco.repo.content.filestore.FileContentReader;
 import org.alfresco.repo.security.authentication.AuthenticationUtil;
 import org.alfresco.service.cmr.repository.NodeRef;
+import org.apache.commons.io.IOUtils;
 import org.apache.http.HttpEntity;
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -185,8 +186,7 @@ public class CMSSignatureHandlerV3 extends CommonSignatureHandlerv3 {
 
     protected void handleDocumentSignature(NodeRef document, NodeRef dossier, EtapeCircuit etape, InputStream signed) {
         try {
-            byte[] targetArray = new byte[signed.available()];
-            signed.read(targetArray);
+            byte[] targetArray = IOUtils.toByteArray(signed);
 
             signatureService.setDetachedSignature(document.getId(), new ByteArrayInputStream(targetArray), this.getSigMimeType());
             getServiceRegistry().getNodeService().setProperty(etape.getNodeRef(), ParapheurModel.PROP_SIGNATURE_ETAPE, new String(Base64.getEncoder().encode(targetArray)));
diff --git a/iparapheur-core/src/main/java/coop/libriciel/model/CreerDossieModel.java b/iparapheur-core/src/main/java/coop/libriciel/model/CreerDossieModel.java
index 42a7970..d068513 100644
--- a/iparapheur-core/src/main/java/coop/libriciel/model/CreerDossieModel.java
+++ b/iparapheur-core/src/main/java/coop/libriciel/model/CreerDossieModel.java
@@ -1,6 +1,9 @@
 package coop.libriciel.model;
 
+import lombok.AccessLevel;
 import lombok.Data;
+import lombok.Getter;
+
 import org.adullact.iparapheur.rules.bean.CustomProperty;
 import org.adullact.spring_ws.iparapheur._1.TypeDocAnnexes;
 import org.adullact.spring_ws.iparapheur._1.Visibilite;
@@ -17,7 +20,8 @@ import java.util.Map;
  */
 @Data
 public class CreerDossieModel {
-    private String loggerPrefix = "creerDossier (%s) : ";
+	@Getter(value = AccessLevel.NONE)
+    private String loggerPrefix = "creerDossier";
     private String username;
     private String wsUsername;
     private DOCTYPE mainDocType;
@@ -47,4 +51,8 @@ public class CreerDossieModel {
         BUREAUTIQUE,
         OTHER
     }
+    
+    public String getLoggerPrefix() {
+		return "creerDossier ("+ (dossier != null ? dossier.toString() : ""+id) + ") - Thread(" +Thread.currentThread().getName() +") : " ;
+	}
 }
diff --git a/iparapheur-core/src/main/java/coop/libriciel/service/CreerDossierService.java b/iparapheur-core/src/main/java/coop/libriciel/service/CreerDossierService.java
index c021933..ec66907 100644
--- a/iparapheur-core/src/main/java/coop/libriciel/service/CreerDossierService.java
+++ b/iparapheur-core/src/main/java/coop/libriciel/service/CreerDossierService.java
@@ -62,6 +62,14 @@ import java.util.regex.Pattern;
 @Log4j
 public class CreerDossierService {
 
+    public static class SyncExecutor implements Executor {
+    	private static SyncExecutor INSTANCE = new SyncExecutor() ;
+		@Override
+		public void execute(Runnable command) {
+			command.run();
+		}
+	}
+	
     @Autowired
     private NodeService nodeService;
 
@@ -132,6 +140,10 @@ public class CreerDossierService {
                     Thread nt = new Thread(runnable);
                     nt.setPriority(Thread.MIN_PRIORITY);
                     nt.setName(PH_JOB_PREFIX + "-" + jobId);
+                    if (jobId > 1) {
+                      	log.warn("CreerDossierService taskExecutor created more than 1 thread", new Exception("CreerDossierService taskExecutor created more than 1 thread"));
+                    }
+                    
                     jobId++;
                     nt.setDaemon(true);
                     return nt;
@@ -183,152 +195,198 @@ public class CreerDossierService {
 
             tx.commit();
         } catch (Exception e) {
+        	log.warn("Requete doCreateFile incorrecte :" +  e.getMessage());
             try {
                 tx.rollback();
             } catch (SystemException e1) {
                 log.error("Erreur lors du rollback de la vérification de la requête", e1);
             }
-            log.error(e.getMessage(), e);
             if(idAddedToList) {
                 idsCreatingList.remove(request.getDossierID());
             }
             return this.handleException(e);
         }
 
-        log.debug(model.getLoggerPrefix() + " - Verification de requête OK");
+        log.info(model.getLoggerPrefix() + " - Verification de requête OK");
 
         RetryingTransactionHelper rtx = transactionService.getRetryingTransactionHelper();
         rtx.setMaxRetries(10);
         boolean finalIdAddedToList = idAddedToList;
-        rtx.doInTransaction(() -> {
-            try {
-                Map<QName, Serializable> properties = this.defineProperties(model);
-
-                log.debug(model.getLoggerPrefix() + " - Creation de dossier...");
-                // ------------------------------------- Création dossier
-                model.setDossier(dossierService.createDossier(model.getParapheur(), properties));
-                dossierService.setDossierPropertiesWithAuditTrail(model.getDossier(), model.getParapheur(), properties, true);
-                model.setId((String) nodeService.getProperty(model.getDossier(), ContentModel.PROP_NAME));
-                nodeService.addAspect(model.getDossier(), ParapheurModel.ASPECT_DOSSIER_ORIGINE_WS, null);
-
-                log.debug(model.getLoggerPrefix() + " - Creation de dossier OK");
-
-                msg.setCodeRetour("OK");
-                msg.setMessage("Dossier " + model.getTitle() + " soumis dans le circuit");
-                msg.setSeverite("INFO");
-
-                res.setMessageRetour(msg);
-                // Retro-compatibilité - Pour les connecteurs n'utilisant pas les dernières versions du WSDL, comprenant le champ "DossierID"
-                // Pour information - Ce champ a été ajouté en 2013
-                if(!finalIdAddedToList) {
-                    res.setDossierID(model.getId());
-                }
-            }catch (Throwable are) {
-                are.printStackTrace();
-                throw new ConcurrencyFailureException(are.getMessage());
-            }
-            return null;
-        });
-
-        if(idAddedToList) {
-            idsCreatingList.remove(request.getDossierID());
+        try {
+	        rtx.doInTransaction(() -> {
+	            try {
+	        	    log.info(model.getLoggerPrefix() + " - Phase 1 ("+ getExecutionPolicy() +") - Debut phase 1 creation de dossier");
+
+	            	Map<QName, Serializable> properties = this.defineProperties(model);
+	
+	                log.debug(model.getLoggerPrefix() + " - Creation de dossier...");
+	                // ------------------------------------- Création dossier
+	                model.setDossier(dossierService.createDossier(model.getParapheur(), properties));
+	                dossierService.setDossierPropertiesWithAuditTrail(model.getDossier(), model.getParapheur(), properties, true);
+	                model.setId((String) nodeService.getProperty(model.getDossier(), ContentModel.PROP_NAME));
+	                nodeService.addAspect(model.getDossier(), ParapheurModel.ASPECT_DOSSIER_ORIGINE_WS, null);
+	
+	                log.info(model.getLoggerPrefix() + " - Phase 1 ("+ getExecutionPolicy() +") - Creation de dossier OK");
+	
+	                msg.setCodeRetour("OK");
+	                msg.setMessage("Dossier " + model.getTitle() + " soumis dans le circuit");
+	                msg.setSeverite("INFO");
+	
+	                res.setMessageRetour(msg);
+	                // Retro-compatibilité - Pour les connecteurs n'utilisant pas les dernières versions du WSDL, comprenant le champ "DossierID"
+	                // Pour information - Ce champ a été ajouté en 2013
+	                if(!finalIdAddedToList) {
+	                    res.setDossierID(model.getId());
+	                }
+	                
+	                if (isSingleTx()) {
+	                	doCreateFilePhase2(request, model);
+	                	doCreateFilePhase3(request, msg, model) ;
+	                }
+	                
+	            }catch (Throwable are) {
+	                log.warn("Erreur l'execution de la creation du dossier, may be retried, waiting 1s before next retry : " + are.getMessage(), are);
+	                Thread.sleep(1000);
+	                throw new ConcurrencyFailureException(are.getMessage());
+	            }
+	            return null;
+	        });
+        }
+        finally {
+        	if(idAddedToList || idsCreatingList.contains(request.getDossierID())) {
+        		idsCreatingList.remove(request.getDossierID());
+        	}
         }
 
-        if(model.getDossier() != null && nodeService.exists(model.getDossier())) {
-            log.info(model.getLoggerPrefix() + " - Lancement du async...");
-            taskExecutor.execute(() -> {
-                AuthenticationUtil.setRunAsUser(model.getUsername());
-                authenticationComponent.setCurrentUser(model.getUsername());
-
-                RetryingTransactionHelper rtxx = transactionService.getRetryingTransactionHelper();
-                rtxx.setMaxRetries(10);
-                rtxx.doInTransaction(() -> {
-                    try {
-                        log.debug(model.getLoggerPrefix() + " - Préparation du circuit...");
-                        // This define the final workflow to the folder
-                        typesService.getWorkflow(model.getDossier(), model.getType(), model.getSousType(), model.getScriptCustomProperties());
-
-                        log.debug(model.getLoggerPrefix() + " - Préparation multidoc...");
-                        this.prepareMultidoc(request, model);
-
-                        log.debug(model.getLoggerPrefix() + " - Définition du circuit...");
-                        this.setWorkflow(request, model);
-
-                        log.debug(model.getLoggerPrefix() + " - Gestion des aspects de typologie...");
-                        if (model.getProtocole() != null && model.getProtocole().equalsIgnoreCase("HELIOS")) {
-                            if (typesService.isTdtAuto(model.getType(), model.getSousType())) {
-                                nodeService.addAspect(model.getDossier(), ParapheurModel.ASPECT_ETAPE_TDT_AUTO, null);
-                            }
-                        }
-                        if (request.getXPathPourSignatureXML() != null) {
-                            model.getTypageProps().put(ParapheurModel.PROP_XPATH_SIGNATURE, request.getXPathPourSignatureXML());
-                        }
-                        nodeService.addAspect(model.getDossier(), ParapheurModel.ASPECT_TYPAGE_METIER, model.getTypageProps());
-
-                        log.debug(model.getLoggerPrefix() + " - Métadonnées personalisées...");
-                        if (!model.getCustomProperties().isEmpty()) {
-                            nodeService.addAspect(model.getDossier(), QName.createQName("cu:customMetadata", namespaceService), model.getCustomProperties());
-                        }
-
-                        log.debug(model.getLoggerPrefix() + " - Définition des annotations...");
-                        // ------------------- Annotations (publique + privee)
-                        if (request.getAnnotationPublique() != null) {
-                            parapheurService.setAnnotationPublique(model.getDossier(), request.getAnnotationPublique());
-                        }
-                        if (request.getAnnotationPrivee() != null) {
-                            parapheurService.setAnnotationPrivee(model.getDossier(), request.getAnnotationPrivee());
-                        }
-
-                        log.debug(model.getLoggerPrefix() + " - Définition du contenu...");
-                        this.setContent(request, model);
-                        this.setVisuel(request, model);
-
-                        log.debug(model.getLoggerPrefix() + " - Définition de signature détachée...");
-                        this.setSignatureDetachee(request, model);
-
-                        log.debug(model.getLoggerPrefix() + " - Finalisation de la création...");
-                        //redéfinition de la propriété, obligatoire sinon non pris en compte
-                        nodeService.setProperty(model.getDossier(), ParapheurModel.PROP_RECUPERABLE, Boolean.TRUE);
-
-                        // Cachet serveur automatique ?
-                        if (typesService.isCachetAuto(model.getType(), model.getSousType())) {
-                            nodeService.addAspect(model.getDossier(), ParapheurModel.ASPECT_ETAPE_CACHET_AUTO, null);
-                        }
-                    } catch (Throwable are) {
-                        //are.printStackTrace();
-                        throw new ConcurrencyFailureException(are.getMessage());
-                    }
-                    return null;
-                });
-
-                log.debug(model.getLoggerPrefix() + " - Création de dossier terminée");
-
-                // Passer à la suite uniquement si l'étape de création de dossier s'est bien passée, càd code retour OK et noeud existant
-                if (msg.getCodeRetour().equals("OK") && model.getDossier() != null && nodeService.exists(model.getDossier())) {
-                    // Gestion des documents supplémentaires
-                    if (request.getDocumentsSupplementaires() != null) {
-                        log.debug(model.getLoggerPrefix() + " - Gestion des documents supplémentaires...");
-                        this.handleDocsSupp(request, model);
-                    }
-                    // Gestion des documents annexes
-                    if (request.getDocumentsAnnexes() != null) {
-                        log.debug(model.getLoggerPrefix() + " - Gestion des annexes...");
-                        this.handleDocsAnnexes(request, model);
-                    }
-                    // Envoi du dossier dans le circuit
-                    log.debug(model.getLoggerPrefix() + " - Envoi du document dans le circuit de validation...");
-                    
-                    // Gestion de l'ordre des documents
-                    this.handleDocumentOrder(model);
-                    
-                    this.sendFileInWorkflow(request, model);
-                }
-            });
+	    log.info(model.getLoggerPrefix() + " ("+ getExecutionPolicy() +") - creation de dossier / tx principale committee");
+        
+        if (!isSingleTx()) {
+	       	if(model.getDossier() != null && nodeService.exists(model.getDossier())) {
+	            log.info(model.getLoggerPrefix() + " - Lancement du async...");
+	            Executor executor = isDoAsync() ? taskExecutor : SyncExecutor.INSTANCE ;
+	            executor.execute(() -> {
+	                AuthenticationUtil.setRunAsUser(model.getUsername());
+	                authenticationComponent.setCurrentUser(model.getUsername());
+	
+	                RetryingTransactionHelper rtxx = transactionService.getRetryingTransactionHelper();
+	                rtxx.setMaxRetries(10);
+	                rtxx.doInTransaction(() -> {
+	                    try {
+	                        doCreateFilePhase2(request, model);
+	                    } catch (Throwable are) {
+	                        log.warn("Erreur l'execution des taches async sur la creation du dossier, may be retried, waiting 10 s before next retry : " + are.getMessage(), are);
+	                        Thread.sleep(1000);
+	                        throw new ConcurrencyFailureException(are.getMessage());
+	                    }
+	                    
+	                    return null;
+	                });
+	
+	                log.debug(model.getLoggerPrefix() + " - Création de dossier terminée");
+	
+	                doCreateFilePhase3(request, msg, model);
+	            });
+	        }
         }
 
         return res;
     }
 
+	private void doCreateFilePhase3(CreerDossierRequest request, MessageRetour msg, CreerDossieModel model) {
+	    log.info(model.getLoggerPrefix() + " - Phase 3 ("+ getExecutionPolicy() +") - Debut phase 3 creation de dossier / gestion docs additionnels terminee");
+
+		// Passer à la suite uniquement si l'étape de création de dossier s'est bien passée, càd code retour OK et noeud existant
+		if (msg.getCodeRetour().equals("OK") && model.getDossier() != null && nodeService.exists(model.getDossier())) {
+		    // Gestion des documents supplémentaires
+		    if (request.getDocumentsSupplementaires() != null) {
+		        log.debug(model.getLoggerPrefix() + " - Gestion des documents supplémentaires...");
+		        this.handleDocsSupp(request, model);
+		    }
+		    // Gestion des documents annexes
+		    if (request.getDocumentsAnnexes() != null) {
+		        log.debug(model.getLoggerPrefix() + " - Gestion des annexes...");
+		        this.handleDocsAnnexes(request, model);
+		    }
+		    // Envoi du dossier dans le circuit
+		    log.debug(model.getLoggerPrefix() + " - Envoi du document dans le circuit de validation...");
+		    
+		    // Gestion de l'ordre des documents
+		    this.handleDocumentOrder(model);
+		    
+		    this.sendFileInWorkflow(request, model);
+
+		    log.info(model.getLoggerPrefix() + " - Phase 3 ("+ getExecutionPolicy() +") - Creation de dossier / gestion docs additionnels terminée");
+		}
+	}
+
+	private void doCreateFilePhase2(CreerDossierRequest request, CreerDossieModel model) throws IOException {
+        log.info(model.getLoggerPrefix() + " - Phase 2 ("+ getExecutionPolicy() +") - Debut phase 2 creation de dossier");
+
+		log.debug(model.getLoggerPrefix() + " - Préparation du circuit...");
+		// This define the final workflow to the folder
+		typesService.getWorkflow(model.getDossier(), model.getType(), model.getSousType(), model.getScriptCustomProperties());
+
+		log.debug(model.getLoggerPrefix() + " - Préparation multidoc...");
+		this.prepareMultidoc(request, model);
+
+		log.debug(model.getLoggerPrefix() + " - Définition du circuit...");
+		this.setWorkflow(request, model);
+
+		log.debug(model.getLoggerPrefix() + " - Gestion des aspects de typologie...");
+		if (model.getProtocole() != null && model.getProtocole().equalsIgnoreCase("HELIOS")) {
+		    if (typesService.isTdtAuto(model.getType(), model.getSousType())) {
+		        nodeService.addAspect(model.getDossier(), ParapheurModel.ASPECT_ETAPE_TDT_AUTO, null);
+		    }
+		}
+		if (request.getXPathPourSignatureXML() != null) {
+		    model.getTypageProps().put(ParapheurModel.PROP_XPATH_SIGNATURE, request.getXPathPourSignatureXML());
+		}
+		nodeService.addAspect(model.getDossier(), ParapheurModel.ASPECT_TYPAGE_METIER, model.getTypageProps());
+
+		log.debug(model.getLoggerPrefix() + " - Métadonnées personalisées...");
+		if (!model.getCustomProperties().isEmpty()) {
+		    nodeService.addAspect(model.getDossier(), QName.createQName("cu:customMetadata", namespaceService), model.getCustomProperties());
+		}
+
+		log.debug(model.getLoggerPrefix() + " - Définition des annotations...");
+		// ------------------- Annotations (publique + privee)
+		if (request.getAnnotationPublique() != null) {
+		    parapheurService.setAnnotationPublique(model.getDossier(), request.getAnnotationPublique());
+		}
+		if (request.getAnnotationPrivee() != null) {
+		    parapheurService.setAnnotationPrivee(model.getDossier(), request.getAnnotationPrivee());
+		}
+
+		log.debug(model.getLoggerPrefix() + " - Définition du contenu...");
+		this.setContent(request, model);
+		this.setVisuel(request, model);
+
+		log.debug(model.getLoggerPrefix() + " - Définition de signature détachée...");
+		this.setSignatureDetachee(request, model);
+
+		log.debug(model.getLoggerPrefix() + " - Finalisation de la création...");
+		//redéfinition de la propriété, obligatoire sinon non pris en compte
+		nodeService.setProperty(model.getDossier(), ParapheurModel.PROP_RECUPERABLE, Boolean.TRUE);
+
+		// Cachet serveur automatique ?
+		if (typesService.isCachetAuto(model.getType(), model.getSousType())) {
+		    nodeService.addAspect(model.getDossier(), ParapheurModel.ASPECT_ETAPE_CACHET_AUTO, null);
+		}
+        log.info(model.getLoggerPrefix() + " - Phase 2 ("+ getExecutionPolicy() +") - Creation de dossier terminee");
+	}
+
+	private boolean isDoAsync() {
+		return "async".equals(getExecutionPolicy());
+	}
+
+	private String getExecutionPolicy() {
+		return System.getProperty("iparapheur.create-dossier.task", "async");
+	}
+
+	private boolean isSingleTx() {
+		return "single-transaction".equals(getExecutionPolicy());
+	}
+	
     private void checkAndBuildRequest(CreerDossierRequest request, CreerDossieModel model) throws Exception {
 
         model.setUsername(this.getValidUser());
@@ -485,19 +543,24 @@ public class CreerDossierService {
                 leDoc.setNom(Normalizer.normalize(nom, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", ""));
                 model.getDocNames().put(nom, nom);
 
-                UserTransaction tx = transactionService.getNonPropagatingUserTransaction(false);
-
-                try {
-                    tx.begin();
+                if (!isSingleTx()) {
+	                UserTransaction tx = transactionService.getNonPropagatingUserTransaction(false);
+	
+	                try {
+	                    tx.begin();
+	                    this.handleDocAnnexes(leDoc, model);
+	                    tx.commit();
+	                } catch(Throwable e) {
+	                    e.printStackTrace();
+	                    try {
+	                        tx.rollback();
+	                    } catch(Throwable e1) {
+	                        e1.printStackTrace();
+	                    }
+	                }
+                }
+                else {
                     this.handleDocAnnexes(leDoc, model);
-                    tx.commit();
-                } catch(Throwable e) {
-                    e.printStackTrace();
-                    try {
-                        tx.rollback();
-                    } catch(Throwable e1) {
-                        e1.printStackTrace();
-                    }
                 }
 
             } else {
@@ -511,19 +574,24 @@ public class CreerDossierService {
     private void handleDocumentOrder(CreerDossieModel model) {
     	if(model.getOrdreDocuments()!=null && !model.getOrdreDocuments().isEmpty()) {
     		log.debug("Gestion de l'ordre des documents");
-    		UserTransaction tx = transactionService.getNonPropagatingUserTransaction(false);
-    		try {
-    			tx.begin();
+    		if (!isSingleTx()) {
+	    		UserTransaction tx = transactionService.getNonPropagatingUserTransaction(false);
+	    		try {
+	    			tx.begin();
+	                dossierService.setOrderDocuments(model.getDossier(), model.getOrdreDocuments());
+	                tx.commit();
+	    		} catch(Throwable e) {
+	    			log.error("Erreur lors de la gestion des documents", e);
+	                try {
+	                    tx.rollback();
+	                } catch(Throwable e1) {
+	                	log.error("erreur lors du rollback dans [handleDocumentOrder]", e1);
+	                }
+	            }
+    		}
+    		else {
                 dossierService.setOrderDocuments(model.getDossier(), model.getOrdreDocuments());
-                tx.commit();
-    		} catch(Throwable e) {
-    			log.error("Erreur lors de la gestion des documents", e);
-                try {
-                    tx.rollback();
-                } catch(Throwable e1) {
-                	log.error("erreur lors du rollback dans [handleDocumentOrder]", e1);
-                }
-            }
+    		}
     	}
     }
     
@@ -602,23 +670,28 @@ public class CreerDossierService {
             }
             model.getDocNames().put(nom, nom);
 
-            UserTransaction tx = transactionService.getNonPropagatingUserTransaction(false);
-
-            try {
-                tx.begin();
+            if (!isSingleTx()) {
+	            UserTransaction tx = transactionService.getNonPropagatingUserTransaction(false);
+	            try {
+	                tx.begin();
+	                this.handleDocSupp(leDoc, model);
+	                tx.commit();
+	            } catch(Throwable e) {
+	                e.printStackTrace();
+	                try {
+	                    tx.rollback();
+	                } catch(Throwable e1) {
+	                    e1.printStackTrace();
+	                }
+	            }
+            }
+            else {
                 this.handleDocSupp(leDoc, model);
-                tx.commit();
-            } catch(Throwable e) {
-                e.printStackTrace();
-                try {
-                    tx.rollback();
-                } catch(Throwable e1) {
-                    e1.printStackTrace();
-                }
             }
 
 
 
+
             if (leDoc.getSignature() != null) {   // signature, optionnel
                 String leSigType = leDoc.getSignature().getContentType();
                 if (leSigType != null && (leSigType.equalsIgnoreCase("application/x-pkcs7-signature") || leSigType.equalsIgnoreCase(MimetypeMap.MIMETYPE_ZIP))) {
diff --git a/iparapheur-core/src/main/java/coop/libriciel/utils/poppler/PopplerUtils.java b/iparapheur-core/src/main/java/coop/libriciel/utils/poppler/PopplerUtils.java
index 06bae2c..ebd1685 100644
--- a/iparapheur-core/src/main/java/coop/libriciel/utils/poppler/PopplerUtils.java
+++ b/iparapheur-core/src/main/java/coop/libriciel/utils/poppler/PopplerUtils.java
@@ -10,9 +10,16 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.StringJoiner;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
+import javax.annotation.PreDestroy;
+
 @Log4j
 public class PopplerUtils {
 
@@ -194,6 +201,13 @@ public class PopplerUtils {
         }
     }
 
+    public static ExecutorService popplerExecutor = new ThreadPoolExecutor(20, 100, 30, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(128)) ;
+    
+    @PreDestroy
+    public void dispose() {
+    	popplerExecutor.shutdown();
+    }
+    
     private PopplerCallResult doCall(String... params) {
         List<String> paramsList = new ArrayList<>(Arrays.asList(params));
         paramsList.add(0, EXECPATH);
@@ -212,11 +226,13 @@ public class PopplerUtils {
             StreamGobbler outputStreamGobbler = new StreamGobbler(process.getInputStream(), output::add);
             StreamGobbler errorStreamGobbler = new StreamGobbler(process.getErrorStream(), outputErr::add);
 
-            Thread outThread = new Thread(outputStreamGobbler);
-            outThread.start();
+            Future<?> outputStreamGobblerFuture = popplerExecutor.submit(outputStreamGobbler) ;
+            //Thread outThread = new Thread(outputStreamGobbler);
+            //outThread.start();
 
-            Thread errThread = new Thread(errorStreamGobbler);
-            errThread.start();
+            Future<?> errorStreamGobblerFuture = popplerExecutor.submit(errorStreamGobbler) ;
+            //Thread errThread = new Thread(errorStreamGobbler);
+            //errThread.start();
 
             boolean ended = process.waitFor(5L, TimeUnit.MINUTES);
             if(ended) {
@@ -227,8 +243,12 @@ public class PopplerUtils {
                 process.destroyForcibly();
             }
 
-            outThread.join();
-            errThread.join();
+            try {
+				outputStreamGobblerFuture.get(10, TimeUnit.SECONDS);
+				errorStreamGobblerFuture.get(10, TimeUnit.SECONDS);
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
 
             result.error = outputErr.toString();
             result.output = output.toString();
diff --git a/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/amq/impl/MessagesReceiver.java b/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/amq/impl/MessagesReceiver.java
index 19870cd..e2e71b5 100644
--- a/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/amq/impl/MessagesReceiver.java
+++ b/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/amq/impl/MessagesReceiver.java
@@ -1,18 +1,27 @@
 package org.adullact.iparapheur.repo.amq.impl;
 
-import lombok.extern.log4j.Log4j;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.PreDestroy;
+import javax.jms.ExceptionListener;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.TextMessage;
+
 import org.adullact.iparapheur.repo.worker.SchedulerService;
 import org.adullact.iparapheur.repo.worker.WorkerService;
-import org.codehaus.jackson.JsonParseException;
-import org.codehaus.jackson.map.JsonMappingException;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.springframework.beans.factory.annotation.Autowired;
 
-import javax.jms.*;
-import java.io.IOException;
-import java.util.HashMap;
+import lombok.extern.log4j.Log4j;
 
 /**
  * Created by lhameury on 14/01/14.
@@ -20,9 +29,19 @@ import java.util.HashMap;
 @Log4j
 public class MessagesReceiver implements MessageListener, ExceptionListener {
 
-    @Autowired
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+	@Autowired
     private SchedulerService schedulerService;
 
+    public ExecutorService jmsExecutor = new ThreadPoolExecutor(20, 20, 30, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(128)) ;
+    
+    @PreDestroy
+    public void dispose() {
+    	jmsExecutor.shutdown();
+    }
+    
+    
     /**
      This method is called asynchronously by JMS when a message arrives
      at the queue. Client applications must not throw any exceptions in
@@ -36,10 +55,11 @@ public class MessagesReceiver implements MessageListener, ExceptionListener {
         try {
             if(m != null) {
                 JSONObject obj = new JSONObject(m.getText());
-                HashMap request = new ObjectMapper().readValue(obj.toString(), HashMap.class);
+                HashMap request = OBJECT_MAPPER.readValue(obj.toString(), HashMap.class);
                 log.info("Consumer : message received; ACTION : " + WorkerService.ACTION + "; ID : " + WorkerService.ID);
                 SelectWorkerRunnable selectWorkerRunnable = new SelectWorkerRunnable(schedulerService, request, m);
-                (new Thread(selectWorkerRunnable)).start();
+                jmsExecutor.submit(selectWorkerRunnable) ;
+                //(new Thread(selectWorkerRunnable)).start();
             }
         } catch (JMSException | JSONException | IOException ex) {
             log.error("Error on onMessage : ", ex);
diff --git a/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/jscript/StreamBinaryData.java b/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/jscript/StreamBinaryData.java
index 15e0af1..54328de 100644
--- a/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/jscript/StreamBinaryData.java
+++ b/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/jscript/StreamBinaryData.java
@@ -109,11 +109,6 @@ public class StreamBinaryData extends AbstractWebScript implements ResourceLoade
     // Logger
     private static final Log logger = LogFactory.getLog(StreamBinaryData.class);
 
-    /**
-     * format definied by RFC 822, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 
-     */
-    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US);
-    
     /** Services */
     protected PermissionService permissionService;
     protected NodeService nodeService;
@@ -490,6 +485,11 @@ public class StreamBinaryData extends AbstractWebScript implements ResourceLoade
             String modifiedSinceStr = req.getHeader("If-Modified-Since");
             if (modifiedSinceStr != null) {
                 try {
+                    /**
+                     * format definied by RFC 822, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 
+                     */
+                    SimpleDateFormat dateFormat = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US);    
+
                     modifiedSince = dateFormat.parse(modifiedSinceStr).getTime();
                 }
                 catch (Throwable e) {
@@ -869,6 +869,11 @@ public class StreamBinaryData extends AbstractWebScript implements ResourceLoade
             String modifiedSinceStr = req.getHeader("If-Modified-Since");
             if (modifiedSinceStr != null) {
                 try {
+                    /**
+                     * format definied by RFC 822, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 
+                     */
+                    SimpleDateFormat dateFormat = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US);    
+
                     modifiedSince = dateFormat.parse(modifiedSinceStr).getTime();
                 } catch (Throwable e) {
                     if (logger.isInfoEnabled()) {
@@ -991,6 +996,11 @@ public class StreamBinaryData extends AbstractWebScript implements ResourceLoade
             String modifiedSinceStr = req.getHeader("If-Modified-Since");
             if (modifiedSinceStr != null) {
                 try {
+                    /**
+                     * format definied by RFC 822, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 
+                     */
+                    SimpleDateFormat dateFormat = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US);    
+
                     modifiedSince = dateFormat.parse(modifiedSinceStr).getTime();
                 } catch (Throwable e) {
                     if (logger.isInfoEnabled()) {
diff --git a/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/office/RuntimeExec.java b/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/office/RuntimeExec.java
index 5861831..501447b 100644
--- a/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/office/RuntimeExec.java
+++ b/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/office/RuntimeExec.java
@@ -33,6 +33,15 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.StringTokenizer;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.PreDestroy;
 
 import org.alfresco.error.AlfrescoRuntimeException;
 import org.apache.commons.logging.Log;
@@ -403,6 +412,14 @@ public class RuntimeExec
         return execute(defaultProperties);
     }
 
+    public static ExecutorService officeExecutor = new ThreadPoolExecutor(20, 100, 30, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(128)) ;
+    
+    @PreDestroy
+    public void dispose() {
+    	officeExecutor.shutdown();
+    }
+
+    
     /**
      * Executes the statement that this instance was constructed with.
      * <p>
@@ -449,9 +466,12 @@ public class RuntimeExec
         InputStreamReaderThread stdOutGobbler = new InputStreamReaderThread(process.getInputStream(), charset);
         InputStreamReaderThread stdErrGobbler = new InputStreamReaderThread(process.getErrorStream(), charset);
 
+        Future<?> stdOutGobblerFuture = officeExecutor.submit(stdOutGobbler) ;
+        Future<?> stdErrGobblerFuture = officeExecutor.submit(stdErrGobbler) ;
+        
         // start gobbling
-        stdOutGobbler.start();
-        stdErrGobbler.start();
+        //stdOutGobbler.start();
+        //stdErrGobbler.start();
 
         // wait for the process to finish
         int exitValue = 0;
@@ -472,8 +492,12 @@ public class RuntimeExec
         if (waitForCompletion)
         {
             // ensure that the stream gobblers get to finish
-            stdOutGobbler.waitForCompletion();
-            stdErrGobbler.waitForCompletion();
+        	try {
+				stdOutGobblerFuture.get(10, TimeUnit.SECONDS) ;
+				stdErrGobblerFuture.get(10, TimeUnit.SECONDS) ;
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
         }
 
         // get the stream values
@@ -694,7 +718,7 @@ public class RuntimeExec
      * <p>
      * The reading of the input stream is buffered.
      */
-    public static class InputStreamReaderThread extends Thread
+    public static class InputStreamReaderThread implements Runnable
     {
         private final InputStream is;
         private final Charset charset;
@@ -709,7 +733,6 @@ public class RuntimeExec
         public InputStreamReaderThread(InputStream is, Charset charset)
         {
             super();
-            setDaemon(true); // must not hold up the VM if it is terminating
             this.is = is;
             this.charset = charset;
             this.buffer = new StringBuffer(BUFFER_SIZE);
@@ -717,7 +740,7 @@ public class RuntimeExec
             this.completed = false;
         }
 
-        public synchronized void run()
+        public void run()
         {
             // mark this thread as running
             isRunning = true;
@@ -764,26 +787,6 @@ public class RuntimeExec
             }
         }
 
-        /**
-         * Waits for the run to complete.
-         * <p>
-         * <b>Remember to <code>start</code> the thread first
-         */
-        public synchronized void waitForCompletion()
-        {
-            while (!completed && !isRunning)
-            {
-                try
-                {
-                    // release our lock and wait a bit
-                    this.wait(200L); // 200 ms
-                }
-                catch (InterruptedException e)
-                {
-                }
-            }
-        }
-
         /**
          * @param msg the message to add to the buffer
          */
diff --git a/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/worker/SchedulerService.java b/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/worker/SchedulerService.java
index 2793a4c..e98ca31 100644
--- a/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/worker/SchedulerService.java
+++ b/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/worker/SchedulerService.java
@@ -1,7 +1,23 @@
 package org.adullact.iparapheur.repo.worker;
 
-import com.atolcd.parapheur.repo.DossierService;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.ThreadFactory;
+
 import org.adullact.iparapheur.repo.worker.impl.WorkerServiceImpl;
+import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
 import org.jetbrains.annotations.NotNull;
@@ -9,12 +25,9 @@ import org.json.JSONException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEvent;
 import org.springframework.extensions.surf.util.AbstractLifecycleBean;
-import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
+import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
 
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.*;
+import com.atolcd.parapheur.repo.DossierService;
 
 public class SchedulerService extends AbstractLifecycleBean {
 
@@ -22,9 +35,12 @@ public class SchedulerService extends AbstractLifecycleBean {
 
     @Autowired
     private WorkerServiceImpl workerService;
+    
+    @Autowired
+    private AuthenticationComponent authenticationComponent;
 
     //Executeur de tâches, géré par spring
-    private ConcurrentTaskExecutor taskExecutor;
+    private ConcurrentTaskScheduler taskExecutor;
 
     //Id de noeuds en transaction
     private List<String> idInTransaction = new CopyOnWriteArrayList<String>();
@@ -53,6 +69,24 @@ public class SchedulerService extends AbstractLifecycleBean {
 
     private static final String ACTION = "action";
 
+	private long threadPoolDelay;
+
+	private Set<String> delayedUsers = new HashSet<String>() ; 
+	
+	public void setDelayedUsers(String delayedUsers) {
+		this.delayedUsers = new HashSet<String>(Arrays.asList(delayedUsers.split(",")));
+		logger.info("Delayed user are : " +  delayedUsers) ;
+	}
+	
+	public SchedulerService() {
+		try {
+			threadPoolDelay = Long.parseLong(System.getProperty("iparapheur.worker.execution-delay-ms", "0"));
+		} catch (NumberFormatException e) {
+			logger.warn("Incorrect value for 'iparapheur.worker.execution-delay-ms' : " + System.getProperty("iparapheur.worker.execution-delay-ms"));
+		}
+	}
+	
+	
     public ConcurrentMap getCurrentActions() {
         return currentActions;
     }
@@ -99,11 +133,21 @@ public class SchedulerService extends AbstractLifecycleBean {
         return !currentFuture.get(id).isDone() && currentFuture.get(id).cancel(true);
     }
 
+	private Date getNextExecutionDate(DossierService.ACTION_DOSSIER action, String username) {
+		long delay = delayedUsers.contains(username) ? threadPoolDelay : 0 ;
+		delay = DossierService.ACTION_DOSSIER.CORBEILLE_EVENT.equals(action) ? 12 * threadPoolDelay : delay ;
+		
+		logger.info("Delaying execution of " + action + " of " + delay + "ms for user " + username) ;
+		return new Date(System.currentTimeMillis() + delay);
+	}
+    
     public void selectWorker(Map request) throws JSONException {
         String type = (String) request.get(WorkerService.TYPE);
+        String username = (String) request.get(WorkerService.USERNAME);
         DossierService.ACTION_DOSSIER action = DossierService.ACTION_DOSSIER.valueOf((String) request.get(ACTION));
+        
         if(WorkerService.TYPE_EVENT.equals(type)) {
-            taskExecutor.submit(workerService.getHandler(request));
+            taskExecutor.schedule(workerService.getHandler(request), getNextExecutionDate(action, username));
         } else if (WorkerService.TYPE_DOSSIER.equals(type) || WorkerService.TYPE_DOCUMENT.equals(type)) {
             String id = (String) request.get(WorkerService.ID);
 
@@ -139,9 +183,9 @@ public class SchedulerService extends AbstractLifecycleBean {
                     idInTransaction.add(id);
                     request.put("date", new Date().getTime());
                     currentActions.putIfAbsent(id, request);
-                    currentFuture.putIfAbsent(id, taskExecutor.submit(workerService.getHandler(request)));
+                    currentFuture.putIfAbsent(id, taskExecutor.schedule(workerService.getHandler(request), getNextExecutionDate(action, username)));
                 } else {
-                    taskExecutor.submit(workerService.getHandler(request));
+                    taskExecutor.schedule(workerService.getHandler(request), getNextExecutionDate(action, username));
                 }
             } else {
                 logger.error("----Consumer : " + "id " + id + " déjà en cours de traitement. CECI NE DEVRAIT PAS ARRIVER !!!");
@@ -155,6 +199,19 @@ public class SchedulerService extends AbstractLifecycleBean {
             this.threadPool = Integer.parseInt(threadPool);
         }
     }
+    
+    public void setThreadPoolDelayString(String threadPoolDelayString) {
+        if (threadPoolDelayString != null && StringUtils.isNumeric(threadPoolDelayString)) {
+        	try {
+        		this.threadPoolDelay =  Long.parseLong(threadPoolDelayString);
+			} catch (NumberFormatException e) {
+				logger.warn("Incorrect value for 'threadPoolDelayString' : " + threadPoolDelayString);
+			}
+        }
+        else if (StringUtils.isNotBlank(threadPoolDelayString)) {
+			logger.warn("Incorrect value for 'threadPoolDelayString' : " + threadPoolDelayString);
+        }
+    }
 
     /**
      * Initialisation du Pool de Threads (géré par Spring)
@@ -181,13 +238,9 @@ public class SchedulerService extends AbstractLifecycleBean {
                 }
             };
 
-            ThreadPoolExecutor executor = new ThreadPoolExecutor(threadPool,
-                                                                 threadPool,
-                                                                 0L,
-                                                                 TimeUnit.MILLISECONDS,
-                                                                 new LinkedBlockingQueue<Runnable>(),
+            ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(threadPool,
                                                                  threadFactory);
-            taskExecutor = new ConcurrentTaskExecutor(executor);
+            taskExecutor = new ConcurrentTaskScheduler(executor);
             sem = new Semaphore(threadPool);
             signSem = new Semaphore(1);
             tdtSem = new Semaphore(1);
diff --git a/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/worker/impl/WorkerServiceImpl.java b/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/worker/impl/WorkerServiceImpl.java
index 756f0f8..ff21e85 100644
--- a/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/worker/impl/WorkerServiceImpl.java
+++ b/iparapheur-core/src/main/java/org/adullact/iparapheur/repo/worker/impl/WorkerServiceImpl.java
@@ -1427,13 +1427,13 @@ public class WorkerServiceImpl implements WorkerService {
                             function.errorCode = ((ParapheurException) e.getCause()).getInternalCode();
                         }
 
-                        e.printStackTrace();
+                        logger.warn("Exception lors du traitement du dossier  : " + getCause(e).getMessage(), e);
                     }
                     function.error();
                     try {
                         utx.rollback();
                     } catch (Exception e1) {
-                        e1.printStackTrace();
+                        logger.warn("Exception lors du rollback sur erreur de traitement du dossier  : " + e1.getMessage(), e1);
                     }
                 } finally {
                     // Si il y a eu une exception, le dossier est toujours "PENDING", il faut donc enlever l'aspect pour
@@ -1447,7 +1447,9 @@ public class WorkerServiceImpl implements WorkerService {
                         } catch (Exception e) {
                             try {
                                 transaction.rollback();
-                            } catch (Exception e1) {e1.printStackTrace();}
+                            } catch (Exception e1) {
+                                logger.warn("Exception lors du rollback sur erreur de traitement du dossier  : " + e1.getMessage(), e1);
+                            }
                         }
                     }
                     schedulerService.stopExecution(id);
diff --git a/iparapheur-core/src/main/java/org/adullact/libersign/util/signature/XadesTimeStamper.java b/iparapheur-core/src/main/java/org/adullact/libersign/util/signature/XadesTimeStamper.java
index 96c90c9..b893b6e 100644
--- a/iparapheur-core/src/main/java/org/adullact/libersign/util/signature/XadesTimeStamper.java
+++ b/iparapheur-core/src/main/java/org/adullact/libersign/util/signature/XadesTimeStamper.java
@@ -78,6 +78,8 @@ import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.Transformer;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
+
+import org.apache.commons.io.IOUtils;
 import org.apache.log4j.Logger;
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
@@ -478,29 +480,30 @@ public class XadesTimeStamper {
             outStream.flush();
             outStream.close();
 
-            DataInputStream inStream = new DataInputStream(connection.getInputStream());
-            while (inStream.available() == 0)
-            {
-                try
-                {
-                    Thread.sleep(300);
-                } catch (InterruptedException ex)
-                {
-                    logger.error(null, ex);
-                    return null;
-                }
-            }
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            byte[] buffer = new byte[2048];
-            int readen = 0;
-            do
-            {
-                readen = inStream.read(buffer);
-                if (readen > 0)
-                    baos.write(buffer, 0, readen);
-            }
-            while (readen != -1);
-            return baos.toByteArray();
+            return IOUtils.toByteArray(connection.getInputStream()) ;
+//            DataInputStream inStream = new DataInputStream(connection.getInputStream());
+//            while (inStream.available() == 0)
+//            {
+//                try
+//                {
+//                    Thread.sleep(300);
+//                } catch (InterruptedException ex)
+//                {
+//                    logger.error(null, ex);
+//                    return null;
+//                }
+//            }
+//            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+//            byte[] buffer = new byte[2048];
+//            int readen = 0;
+//            do
+//            {
+//                readen = inStream.read(buffer);
+//                if (readen > 0)
+//                    baos.write(buffer, 0, readen);
+//            }
+//            while (readen != -1);
+//            return baos.toByteArray();
 
         } catch (IOException ex)
         {
diff --git a/iparapheur-core/src/main/resources/alfresco/module/parapheur/module-context.xml b/iparapheur-core/src/main/resources/alfresco/module/parapheur/module-context.xml
index d66d870..af52f71 100755
--- a/iparapheur-core/src/main/resources/alfresco/module/parapheur/module-context.xml
+++ b/iparapheur-core/src/main/resources/alfresco/module/parapheur/module-context.xml
@@ -1466,6 +1466,12 @@
         <property name="threadPoolString">
             <value>${parapheur.jobs.thread.pool}</value>
         </property>
+        <property name="threadPoolDelayString">
+            <value>${parapheur.jobs.thread.pool.delay}</value>
+        </property>
+        <property name="delayedUsers">
+            <value>${parapheur.jobs.thread.pool.delayedUsers}</value>
+        </property>
     </bean>
 
     <bean id="activeMQConnectionService" class="org.adullact.iparapheur.repo.amq.impl.ActiveMQConnectionServiceImpl">
-- 
GitLab