0
|
1 package nabble.model;
|
|
2
|
|
3 import java.util.Date;
|
|
4 import java.util.Random;
|
|
5 import java.util.concurrent.TimeUnit;
|
|
6 import org.slf4j.Logger;
|
|
7 import org.slf4j.LoggerFactory;
|
|
8 import fschmidt.db.DbDatabase;
|
|
9 import fschmidt.util.executor.RunnableWrapper;
|
|
10 import fschmidt.util.executor.ThreadPool;
|
|
11 import fschmidt.util.executor.ScheduledThreadPool;
|
|
12 import fschmidt.util.executor.ThreadTimer;
|
|
13 import fschmidt.util.java.DateUtils;
|
|
14
|
|
15
|
|
16 public final class Executors {
|
|
17 private Executors() {} // never
|
|
18
|
|
19 private static final Logger logger = LoggerFactory.getLogger(Executors.class);
|
|
20
|
|
21 public static final ThreadTimer threadTimer = new ThreadTimer();
|
|
22
|
|
23 private static final RunnableWrapper dbCleanup = new RunnableWrapper() {
|
|
24 @Override public Runnable wrap(final Runnable command) {
|
|
25 return new Runnable(){public void run(){
|
|
26 try {
|
|
27 command.run();
|
|
28 } finally {
|
|
29 fschmidt.db.pool.Pool.threadReset();
|
|
30 }
|
|
31 }};
|
|
32 }
|
|
33 };
|
|
34
|
|
35 private static final ScheduledThreadPool backgroundExecutor = new ScheduledThreadPool(1);
|
|
36 static {
|
|
37 backgroundExecutor.addRunnableWrapper(dbCleanup);
|
|
38 backgroundExecutor.addRunnableWrapper(threadTimer);
|
|
39 backgroundExecutor.addRunnableWrapper(new TimedExecuteWrapper());
|
|
40 }
|
|
41 public static final ThreadPool foregroundExecutor = new ThreadPool(Init.get("poolSize",100));
|
|
42 static {
|
|
43 foregroundExecutor.addRunnableWrapper(dbCleanup);
|
|
44 foregroundExecutor.addRunnableWrapper(threadTimer);
|
|
45 }
|
|
46 private static Random rnd = new Random();
|
|
47
|
|
48 private static final long timeLimit = Init.get("execTimeLimitSeconds", 60L);
|
|
49 private static volatile boolean isShuttingDown = false;
|
|
50 private static long start = System.currentTimeMillis();
|
|
51
|
|
52 static void shutdown() {
|
|
53 isShuttingDown = true;
|
|
54 backgroundExecutor.shutdown();
|
|
55 foregroundExecutor.shutdown();
|
|
56 try {
|
|
57 while( !backgroundExecutor.awaitTermination(1,TimeUnit.SECONDS) ) {
|
|
58 logger.error("backgroundExecutor failed to shutdown");
|
|
59 for( Thread thread : backgroundExecutor.getThreads() ) {
|
|
60 if( thread.isAlive() ) {
|
|
61 Throwable t = new Throwable(thread.toString());
|
|
62 t.setStackTrace(thread.getStackTrace());
|
|
63 logger.error("backgroundExecutor thread",t);
|
|
64 }
|
|
65 }
|
|
66 }
|
|
67 /* why does this matter?
|
|
68 while( !foregroundExecutor.awaitTermination(1,TimeUnit.SECONDS) ) {
|
|
69 logger.error("foregroundExecutor failed to shutdown");
|
|
70 for( Thread thread : foregroundExecutor.getThreads() ) {
|
|
71 if( thread.isAlive() ) {
|
|
72 Throwable t = new Throwable(thread.toString());
|
|
73 t.setStackTrace(thread.getStackTrace());
|
|
74 logger.error("foregroundExecutor thread",t);
|
|
75 }
|
|
76 }
|
|
77 }
|
|
78 */
|
|
79 } catch(InterruptedException e) {
|
|
80 logger.error("",e);
|
|
81 }
|
|
82 }
|
|
83
|
|
84 public static boolean isShuttingDown() {
|
|
85 long time = (System.currentTimeMillis() - start)/1000;
|
|
86 if( time > timeLimit )
|
|
87 logger.error("exec took too long, " + time + " seconds",new Exception());
|
|
88 start = System.currentTimeMillis();
|
|
89 return isShuttingDown;
|
|
90 }
|
|
91
|
|
92 private static final class TimedExecuteWrapper implements RunnableWrapper {
|
|
93 private ThreadLocal<Exception> trace = new ThreadLocal<Exception>() {
|
|
94 protected Exception initialValue() {
|
|
95 return new Exception("exec created");
|
|
96 }
|
|
97 };
|
|
98
|
|
99 @Override public Runnable wrap(final Runnable command) {
|
|
100 return new Runnable(){public void run(){
|
|
101 Thread.currentThread().setName("background-thread");
|
|
102 trace.get();
|
|
103 start = System.currentTimeMillis();
|
|
104 try {
|
|
105 command.run();
|
|
106 } finally {
|
|
107 long time = (System.currentTimeMillis() - start)/1000;
|
|
108 if( time > timeLimit )
|
|
109 logger.error("exec took too long, " + time + " seconds",trace);
|
|
110 }
|
|
111 }};
|
|
112 }
|
|
113 }
|
|
114
|
|
115 public static void executeNow(Runnable command) {
|
|
116 foregroundExecutor.execute(command);
|
|
117 }
|
|
118
|
|
119
|
|
120
|
|
121 public static void executeSometime(Runnable command) {
|
|
122 schedule(command,0,TimeUnit.SECONDS);
|
|
123 }
|
|
124
|
|
125 public static void schedule(Runnable command,long delay,TimeUnit unit) {
|
|
126 backgroundExecutor.schedule(command,delay,unit);
|
|
127 }
|
|
128
|
|
129 public static void scheduleWithFixedDelay(final Runnable command,long initialDelay,final long delay,final TimeUnit unit) {
|
|
130 Runnable repeatedCommand = new Runnable(){public void run(){
|
|
131 command.run();
|
|
132 schedule(this,delay,unit);
|
|
133 }};
|
|
134 schedule(repeatedCommand,initialDelay,unit);
|
|
135 }
|
|
136
|
|
137 public static void runDaily(Runnable task) {
|
|
138 scheduleWithFixedDelay(task, rnd.nextInt(60*60*24), 60*60*24, TimeUnit.SECONDS);
|
|
139 }
|
|
140
|
|
141 private static final long MILLIS_PER_DAY = 1000L*60L*60L*24L;
|
|
142
|
|
143 public static void runDaily(final Runnable task, int hour, int minute) {
|
|
144 final long millisAfterMidnight = ((long) hour * 60 + minute) * 60 * 1000;
|
|
145 Date now = new Date();
|
|
146 long time = now.getTime() - DateUtils.roundToDay(now).getTime();
|
|
147 long sleep = time < millisAfterMidnight
|
|
148 ? millisAfterMidnight - time
|
|
149 : MILLIS_PER_DAY - (time - millisAfterMidnight)
|
|
150 ;
|
|
151 scheduleWithFixedDelay(task, sleep/(1000L*60), 60*24, TimeUnit.MINUTES);
|
|
152 }
|
|
153
|
|
154 public static void executeAfterCommit(DbDatabase db,final Runnable command) {
|
|
155 db.runAfterCommit(new Runnable(){public void run(){
|
|
156 executeNow(command);
|
|
157 }});
|
|
158 }
|
|
159
|
|
160 }
|