44import org .springframework .core .task .AsyncTaskExecutor ;
55import org .springframework .core .task .SyncTaskExecutor ;
66import org .springframework .core .task .TaskExecutor ;
7+ import org .springframework .scheduling .concurrent .ExecutorConfigurationSupport ;
78import org .springframework .scheduling .concurrent .ThreadPoolTaskExecutor ;
89
910import java .util .ArrayList ;
1314
1415/**
1516 * Support class for BatchWriter implementations that uses Spring's TaskExecutor interface for parallelizing writes to
16- * MarkLogic.
17+ * MarkLogic. Allows for setting a TaskExecutor instance, and if one is not set, a default one will be created based
18+ * on the threadCount attribute. That attribute is ignored if a TaskExecutor is set.
1719 */
1820public abstract class BatchWriterSupport extends LoggingObject implements BatchWriter {
1921
2022 private TaskExecutor taskExecutor ;
2123 private int threadCount = 16 ;
2224
23- /**
24- * Seems necessary to keep track of each Future instance so that we can properly wait for each one to finish.
25- * Spring's TaskExecutor library doesn't seem to provide a better way of doing this.
26- */
27- private List <Future <?>> futures = new ArrayList <>();
28-
2925 @ Override
3026 public void initialize () {
27+ if (taskExecutor == null ) {
28+ initializeDefaultTaskExecutor ();
29+ }
30+ }
31+
32+ @ Override
33+ public void waitForCompletion () {
34+ if (taskExecutor instanceof ExecutorConfigurationSupport ) {
35+ if (logger .isInfoEnabled ()) {
36+ logger .info ("Calling shutdown on thread pool" );
37+ }
38+ ((ExecutorConfigurationSupport ) taskExecutor ).shutdown ();
39+ if (logger .isInfoEnabled ()) {
40+ logger .info ("Thread pool finished shutdown" );
41+ }
42+ }
43+ }
44+
45+ protected void initializeDefaultTaskExecutor () {
3146 if (threadCount > 1 ) {
3247 if (logger .isInfoEnabled ()) {
3348 logger .info ("Initializing thread pool with a count of " + threadCount );
3449 }
3550 ThreadPoolTaskExecutor tpte = new ThreadPoolTaskExecutor ();
3651 tpte .setCorePoolSize (threadCount );
52+ // By default, wait for tasks to finish, and wait up to an hour
53+ tpte .setWaitForTasksToCompleteOnShutdown (true );
54+ tpte .setAwaitTerminationSeconds (60 * 60 );
3755 tpte .afterPropertiesSet ();
3856 this .taskExecutor = tpte ;
3957 } else {
@@ -44,33 +62,8 @@ public void initialize() {
4462 }
4563 }
4664
47- @ Override
48- public void waitForCompletion () {
49- int size = futures .size ();
50- if (logger .isDebugEnabled ()) {
51- logger .debug ("Waiting for threads to finish document processing; futures count: " + size );
52- }
53-
54- for (int i = 0 ; i < size ; i ++) {
55- Future <?> f = futures .get (i );
56- if (f .isDone ()) {
57- continue ;
58- }
59- try {
60- // Wait up to 1 hour for a write to ML to finish (should never happen)
61- f .get (1 , TimeUnit .HOURS );
62- } catch (Exception ex ) {
63- logger .warn ("Unable to wait for last set of documents to be processed: " + ex .getMessage (), ex );
64- }
65- }
66- }
67-
68- protected void execute (Runnable runnable ) {
69- if (taskExecutor instanceof AsyncTaskExecutor ) {
70- futures .add (((AsyncTaskExecutor ) taskExecutor ).submit (runnable ));
71- } else {
72- taskExecutor .execute (runnable );
73- }
65+ protected TaskExecutor getTaskExecutor () {
66+ return taskExecutor ;
7467 }
7568
7669 public void setTaskExecutor (TaskExecutor taskExecutor ) {
0 commit comments