Passed
Pull Request — master (#226)
by Vincent
10:57
created

isShutdown()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
1
/*
2
 * This file is part of Araknemu.
3
 *
4
 * Araknemu is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * Araknemu is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with Araknemu.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2022 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.util;
21
22
import java.util.ArrayList;
23
import java.util.List;
24
import java.util.concurrent.AbstractExecutorService;
25
import java.util.concurrent.Callable;
26
import java.util.concurrent.Executors;
27
import java.util.concurrent.ScheduledExecutorService;
28
import java.util.concurrent.ScheduledFuture;
29
import java.util.concurrent.TimeUnit;
30
31
/**
32
 * Replacement of {@link Executors} class for create {@link ScheduledExecutorService} instances
33
 *
34
 * By default, this factory will only forward calls to {@link Executors} for create executors.
35
 * A testing mode can be enabled using {@link ExecutorFactory#enableTestingMode()} which allows direct execution,
36
 * and keep tracking of all created instances for perform a shutdown when calling {@link ExecutorFactory#resetTestingExecutor()}
37
 */
38
public final class ExecutorFactory {
39 1
    private static boolean testing = false;
40 1
    private static boolean directExecution = true;
41 1
    private static final List<TestingExecutor> testingExecutors = new ArrayList<>();
42
43
    /**
44
     * Disable constructor
45
     */
46
    private ExecutorFactory() { }
47
48
    /**
49
     * Create a single thread executor service
50
     * If testing mode is enabled, a TestingExecutor is returned
51
     *
52
     * @see Executors#newSingleThreadScheduledExecutor()
53
     */
54
    public static ScheduledExecutorService createSingleThread() {
55 1
        if (testing) {
56 1
            return createTestingExecutor();
57
        }
58
59 1
        return Executors.newSingleThreadScheduledExecutor();
60
    }
61
62
    /**
63
     * Create an executor service with the requested amount of threads in the pool
64
     * If testing mode is enabled, a TestingExecutor is returned
65
     *
66
     * @see Executors#newScheduledThreadPool(int)
67
     */
68
    public static ScheduledExecutorService create(int corePoolSize) {
69 1
        if (testing) {
70 1
            return createTestingExecutor();
71
        }
72
73 1
        return Executors.newScheduledThreadPool(corePoolSize);
74
    }
75
76
    /**
77
     * Enable testing mode
78
     *
79
     * In testing mode all created executors instance are kept to perform shutdown on reset
80
     * Also all created executor will support direct execution (i.e. submitted tasks will be executed by the current thread)
81
     *
82
     * If testing mode is enabled, {@link ExecutorFactory#resetTestingExecutor()} should be called after tests
83
     * for free created threads
84
     *
85
     * /!\ This method must not be called on production environment
86
     *
87
     * @see ExecutorFactory#resetTestingExecutor() For reset all created executors
88
     * @see ExecutorFactory#enableDirectExecution() For enable direct execution (default mode)
89
     * @see ExecutorFactory#disableDirectExecution() For disable direct execution
90
     */
91
    public static void enableTestingMode() {
92 1
        testing = true;
93 1
        directExecution = true;
94 1
    }
95
96
    /**
97
     * Disable testing mode
98
     *
99
     * @see ExecutorFactory#enableTestingMode() For enable it
100
     */
101
    public static void disableTestingMode() {
102 1
        resetTestingExecutor();
103 1
        testing = false;
104 1
    }
105
106
    /**
107
     * Shutdown all created executors
108
     *
109
     * This method must be called after tests execution to free threads and memory
110
     * This method is also enabled the direct execution mode
111
     *
112
     * Note: this method as no effects if testing mode is disabled
113
     */
114
    public static void resetTestingExecutor() {
115 1
        for (TestingExecutor executor : testingExecutors) {
116
            try {
117 1
                executor.shutdownNow();
118
            } catch (RuntimeException e) {
119
                // Ignore
120 1
            }
121 1
        }
122
123 1
        testingExecutors.clear();
124 1
        directExecution = true;
125 1
    }
126
127
    /**
128
     * Enable direct execution of task
129
     * This is the default mode on testing mode.
130
     *
131
     * When enabled, all actions submitted using {@link java.util.concurrent.ExecutorService#execute(Runnable)}
132
     * or {@link java.util.concurrent.ExecutorService#submit(Runnable)} will be executed on the current thread
133
     * instead of the internal executor one.
134
     *
135
     * This mode has no impact on scheduled tasks, which will be executed asynchronously.
136
     *
137
     * Note: this method as no effects if testing mode is disabled
138
     *
139
     * @see ExecutorFactory#disableDirectExecution() For disable this mode
140
     */
141
    public static void enableDirectExecution() {
142 1
        directExecution = true;
143 1
    }
144
145
    /**
146
     * Disable direct execution of task
147
     *
148
     * After this method call, all calls to created executors will be executed asynchronously.
149
     *
150
     * Note: this method as no effects if testing mode is disabled
151
     *
152
     * @see ExecutorFactory#enableDirectExecution() For re-enabled this mode
153
     */
154
    public static void disableDirectExecution() {
155 1
        directExecution = false;
156 1
    }
157
158
    private static ScheduledExecutorService createTestingExecutor() {
159 1
        final TestingExecutor executor = new TestingExecutor();
160
161 1
        testingExecutors.add(executor);
162
163 1
        return executor;
164
    }
165
166
    /**
167
     * Implementation of ScheduledExecutorService allowing direct execution
168
     * All calls are forwarded to a real executor service
169
     */
170 1
    private static class TestingExecutor extends AbstractExecutorService implements ScheduledExecutorService {
171 1
        private final ScheduledExecutorService inner = Executors.newSingleThreadScheduledExecutor((r) -> {
172 1
            final Thread thread = new Thread(r);
173 1
            thread.setName("Testing");
174
175 1
            return thread;
176
        });
177
178
        @Override
179
        public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
180 1
            return inner.schedule(command, delay, unit);
181
        }
182
183
        @Override
184
        public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
185
            return inner.schedule(callable, delay, unit);
186
        }
187
188
        @Override
189
        public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
190 1
            return inner.scheduleAtFixedRate(command, initialDelay, period, unit);
191
        }
192
193
        @Override
194
        public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
195
            return inner.scheduleWithFixedDelay(command, initialDelay, delay, unit);
196
        }
197
198
        @Override
199
        public void shutdown() {
200 1
            inner.shutdown();
201 1
        }
202
203
        @Override
204
        public List<Runnable> shutdownNow() {
205 1
            return inner.shutdownNow();
206
        }
207
208
        @Override
209
        public boolean isShutdown() {
210 1
            return inner.isShutdown();
211
        }
212
213
        @Override
214
        public boolean isTerminated() {
215
            return inner.isTerminated();
216
        }
217
218
        @Override
219
        public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
220
            return inner.awaitTermination(timeout, unit);
221
        }
222
223
        @Override
224
        public void execute(Runnable runnable) {
225 1
            if (directExecution) {
226 1
                runnable.run();
227
            } else {
228 1
                inner.execute(runnable);
229
            }
230 1
        }
231
    }
232
}
233