View Javadoc
1   /*******************************************************************************
2    * Copyhacked (H) 2012-2025.
3    * This program and the accompanying materials
4    * are made available under no term at all, use it like
5    * you want, but share and discuss it
6    * every time possible with every body.
7    * 
8    * Contributors:
9    *      ron190 at ymail dot com - initial implementation
10   ******************************************************************************/
11  package com.jsql.util;
12  
13  import com.jsql.model.InjectionModel;
14  import com.jsql.model.bean.database.AbstractElementDatabase;
15  import com.jsql.model.suspendable.AbstractSuspendable;
16  import com.jsql.model.suspendable.callable.ThreadFactoryCallable;
17  import org.apache.logging.log4j.LogManager;
18  import org.apache.logging.log4j.Logger;
19  
20  import java.util.HashMap;
21  import java.util.Map;
22  import java.util.concurrent.ConcurrentHashMap;
23  import java.util.concurrent.ExecutorService;
24  import java.util.concurrent.Executors;
25  import java.util.concurrent.TimeUnit;
26  
27  /**
28   * Utility class managing running threads on which the user can act.
29   * It must be noted that as SwingWorker class are used then only 10 jobs can be run
30   * at the same time, the 11th will be waiting in the thread pool until one of the 10
31   * actives one is freed.
32   */
33  public final class ThreadUtil {
34      
35      private static final Logger LOGGER = LogManager.getRootLogger();
36      
37      /**
38       * List of running jobs associated to a database injection task.
39       * We can interact with those tasks in order to pause/resume and stop the process.
40       */
41      // Fix #8258: ConcurrentModificationException on java.util.HashMap$ValueIterator.next()
42      private final Map<AbstractElementDatabase, AbstractSuspendable> suspendables = new ConcurrentHashMap<>(new HashMap<>());
43  
44      private final InjectionModel injectionModel;
45      
46      public ThreadUtil(InjectionModel injectionModel) {
47          this.injectionModel = injectionModel;
48      }
49  
50      /**
51       * Add a job to the list of ongoing tasks. It is used to allow the user to act
52       * on the job and stop/pause a running process.
53       * @param elementDatabase component associated to the active job
54       * @param suspendable active job to act on
55       */
56      public void put(AbstractElementDatabase elementDatabase, AbstractSuspendable suspendable) {
57          this.suspendables.put(elementDatabase, suspendable);
58      }
59      
60      /**
61       * Get the task associated to the database component.
62       * It's usually done to act on the task like stop/pause the corresponding process, or
63       * to check the status of the job.
64       * @param elementDatabase component associated to the active job
65       * @return job currently running
66       */
67      public AbstractSuspendable get(AbstractElementDatabase elementDatabase) {
68          return this.suspendables.get(elementDatabase);
69      }
70      
71      /**
72       * Remove the thread corresponding to the component in order to be
73       * garbage collected. The thread should be stopped prior the deletion.
74       * @param elementDatabase component associated to thread
75       */
76      public void remove(AbstractElementDatabase elementDatabase) {
77          this.suspendables.remove(elementDatabase);
78      }
79      
80      /**
81       * Force to stop every threads still running and empty the list where
82       * they were instantiated in order to be garbage collected.
83       */
84      public void reset() {
85          this.suspendables.values().forEach(AbstractSuspendable::stop);
86          this.suspendables.clear();
87      }
88      
89      public ExecutorService getExecutor(String nameThread) {
90          ExecutorService taskExecutor;
91          
92          if (this.injectionModel.getMediatorUtils().getPreferencesUtil().isLimitingThreads()) {
93              int countThreads = this.injectionModel.getMediatorUtils().getPreferencesUtil().countLimitingThreads();
94              taskExecutor = Executors.newFixedThreadPool(countThreads, new ThreadFactoryCallable(nameThread));
95          } else {
96              taskExecutor = Executors.newCachedThreadPool(new ThreadFactoryCallable(nameThread));
97          }
98          return taskExecutor;
99      }
100 
101     public void shutdown(ExecutorService taskExecutor) {
102         int timeout = 15;
103         if (this.injectionModel.getMediatorUtils().getPreferencesUtil().isConnectionTimeout()) {
104             timeout = this.injectionModel.getMediatorUtils().getPreferencesUtil().countConnectionTimeout();
105         }
106 
107         try {
108             taskExecutor.shutdown();
109             if (!taskExecutor.awaitTermination(timeout, TimeUnit.SECONDS)) {
110                 taskExecutor.shutdownNow();
111             }
112         } catch (InterruptedException e) {
113             LOGGER.log(LogLevelUtil.IGNORE, e, e);
114             Thread.currentThread().interrupt();
115         }
116     }
117 }