1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.common.events;
20
21 import java.util.Arrays;
22 import java.util.Comparator;
23 import java.util.List;
24 import java.util.Objects;
25 import java.util.concurrent.ConcurrentSkipListSet;
26 import java.util.concurrent.TimeUnit;
27 import java.util.concurrent.locks.ReentrantReadWriteLock;
28 import java.util.stream.Stream;
29
30 import org.apache.logging.log4j.LogManager;
31 import org.apache.logging.log4j.Logger;
32 import org.mycore.common.MCRException;
33 import org.mycore.common.MCRSessionMgr;
34 import org.mycore.common.config.MCRConfiguration2;
35 import org.mycore.common.config.MCRConfigurationException;
36
37 import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
38
39
40
41
42
43
44
45
46
47
48
49
50
51 public class MCRShutdownHandler {
52
53 private static final int ADD_CLOSEABLE_TIMEOUT = 10;
54
55 private static final String PROPERTY_SYSTEM_NAME = "MCR.CommandLineInterface.SystemName";
56
57 private static MCRShutdownHandler SINGLETON = new MCRShutdownHandler();
58
59 private final ConcurrentSkipListSet<Closeable> requests = new ConcurrentSkipListSet<>();
60
61 private final ReentrantReadWriteLock shutdownLock = new ReentrantReadWriteLock();
62
63 private volatile boolean shuttingDown = false;
64
65 boolean isWebAppRunning;
66
67 ClassLoaderLeakPreventor leakPreventor;
68
69 private MCRShutdownHandler() {
70 isWebAppRunning = false;
71 }
72
73 private void init() {
74 if (!isWebAppRunning) {
75 MCRShutdownThread.getInstance();
76 }
77 }
78
79 public static MCRShutdownHandler getInstance() {
80 return SINGLETON;
81 }
82
83 public void addCloseable(MCRShutdownHandler.Closeable c) {
84 Objects.requireNonNull(c);
85 init();
86 boolean hasShutDownLock;
87 try {
88 hasShutDownLock = shutdownLock.readLock().tryLock(ADD_CLOSEABLE_TIMEOUT, TimeUnit.SECONDS);
89 } catch (InterruptedException e) {
90 throw new MCRException("Could not aquire shutdown lock in time", e);
91 }
92 try {
93 if (hasShutDownLock && !shuttingDown) {
94 requests.add(c);
95 } else {
96 throw new MCRException("Cannot register Closeable while shutting down application.");
97 }
98 } finally {
99 if (hasShutDownLock) {
100 shutdownLock.readLock().unlock();
101 }
102 }
103 }
104
105 public void removeCloseable(MCRShutdownHandler.Closeable c) {
106 Objects.requireNonNull(c);
107 if (!shuttingDown) {
108 requests.remove(c);
109 }
110 }
111
112 void shutDown() {
113 Logger logger = LogManager.getLogger();
114 String cfgSystemName = "MyCoRe:";
115 try {
116 cfgSystemName = MCRConfiguration2.getStringOrThrow(PROPERTY_SYSTEM_NAME) + ":";
117 } catch (MCRConfigurationException e) {
118
119 logger.warn("Error getting '" + PROPERTY_SYSTEM_NAME + "': {}", e.getMessage());
120 }
121 final String system = cfgSystemName;
122 System.out.println(system + " Shutting down system, please wait...\n");
123 runClosables();
124 System.out.println(system + " closing any remaining MCRSession instances, please wait...\n");
125 MCRSessionMgr.close();
126 System.out.println(system + " Goodbye, and remember: \"Alles wird gut.\"\n");
127 LogManager.shutdown();
128 SINGLETON = null;
129
130 if (leakPreventor != null) {
131 ClassLoaderLeakPreventor myLeakPreventor = leakPreventor;
132 leakPreventor = null;
133 myLeakPreventor.runCleanUps();
134 }
135 }
136
137 void runClosables() {
138 Logger logger = LogManager.getLogger();
139 logger.debug(() -> "requests: " + requests);
140 Closeable[] closeables = requests.stream().toArray(Closeable[]::new);
141 Stream.of(closeables)
142 .peek(c -> logger.debug("Prepare Closing (1): {}", c))
143 .forEach(Closeable::prepareClose);
144 shutdownLock.writeLock().lock();
145 try {
146 shuttingDown = true;
147
148 final List<Closeable> alreadyPrepared = Arrays.asList(closeables);
149 requests.stream()
150 .filter(c -> !alreadyPrepared.contains(c))
151 .peek(c -> logger.debug("Prepare Closing (2): {}", c))
152 .forEach(Closeable::prepareClose);
153
154 requests.stream()
155 .peek(c -> logger.debug("Closing: {}", c))
156 .forEach(Closeable::close);
157 } finally {
158 shutdownLock.writeLock().unlock();
159 }
160 }
161
162
163
164
165
166
167 @FunctionalInterface
168 public interface Closeable extends Comparable<Closeable> {
169
170
171
172 int DEFAULT_PRIORITY = 5;
173
174
175
176
177
178
179 default void prepareClose() {
180
181 }
182
183
184
185
186
187 void close();
188
189
190
191
192
193 default int getPriority() {
194 return DEFAULT_PRIORITY;
195 }
196
197 @Override
198 default int compareTo(Closeable other) {
199
200 return Comparator.comparingInt(Closeable::getPriority)
201 .thenComparingLong(Closeable::hashCode)
202 .compare(other, this);
203 }
204 }
205
206 }