1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.logging.log4j.core.util;
19
20 import java.lang.ref.Reference;
21 import java.lang.ref.SoftReference;
22 import java.lang.ref.WeakReference;
23 import java.util.Collection;
24 import java.util.concurrent.CopyOnWriteArrayList;
25 import java.util.concurrent.Executors;
26 import java.util.concurrent.ThreadFactory;
27 import java.util.concurrent.TimeUnit;
28 import java.util.concurrent.atomic.AtomicReference;
29
30 import org.apache.logging.log4j.Logger;
31 import org.apache.logging.log4j.core.AbstractLifeCycle;
32 import org.apache.logging.log4j.core.LifeCycle2;
33 import org.apache.logging.log4j.status.StatusLogger;
34
35
36
37
38
39
40
41 public class DefaultShutdownCallbackRegistry implements ShutdownCallbackRegistry, LifeCycle2, Runnable {
42
43 protected static final Logger LOGGER = StatusLogger.getLogger();
44
45 private final AtomicReference<State> state = new AtomicReference<>(State.INITIALIZED);
46 private final ThreadFactory threadFactory;
47 private final Collection<Cancellable> hooks = new CopyOnWriteArrayList<>();
48 private Reference<Thread> shutdownHookRef;
49
50
51
52
53 public DefaultShutdownCallbackRegistry() {
54 this(Executors.defaultThreadFactory());
55 }
56
57
58
59
60
61
62 protected DefaultShutdownCallbackRegistry(final ThreadFactory threadFactory) {
63 this.threadFactory = threadFactory;
64 }
65
66
67
68
69 @Override
70 public void run() {
71 if (state.compareAndSet(State.STARTED, State.STOPPING)) {
72 for (final Runnable hook : hooks) {
73 try {
74 hook.run();
75 } catch (final Throwable t1) {
76 try {
77 LOGGER.error(SHUTDOWN_HOOK_MARKER, "Caught exception executing shutdown hook {}", hook, t1);
78 } catch (final Throwable t2) {
79 System.err.println("Caught exception " + t2.getClass() + " logging exception " + t1.getClass());
80 t1.printStackTrace();
81 }
82 }
83 }
84 state.set(State.STOPPED);
85 }
86 }
87
88 private static class RegisteredCancellable implements Cancellable {
89
90 private final Reference<Runnable> hook;
91 private Collection<Cancellable> registered;
92
93 RegisteredCancellable(final Runnable callback, final Collection<Cancellable> registered) {
94 this.registered = registered;
95 hook = new SoftReference<>(callback);
96 }
97
98 @Override
99 public void cancel() {
100 hook.clear();
101 registered.remove(this);
102 registered = null;
103 }
104
105 @Override
106 public void run() {
107 final Runnable runnableHook = this.hook.get();
108 if (runnableHook != null) {
109 runnableHook.run();
110 this.hook.clear();
111 }
112 }
113
114 @Override
115 public String toString() {
116 return String.valueOf(hook.get());
117 }
118 }
119
120 @Override
121 public Cancellable addShutdownCallback(final Runnable callback) {
122 if (isStarted()) {
123 final Cancellable receipt = new RegisteredCancellable(callback, hooks);
124 hooks.add(receipt);
125 return receipt;
126 }
127 throw new IllegalStateException("Cannot add new shutdown hook as this is not started. Current state: " +
128 state.get().name());
129 }
130
131 @Override
132 public void initialize() {
133 }
134
135
136
137
138 @Override
139 public void start() {
140 if (state.compareAndSet(State.INITIALIZED, State.STARTING)) {
141 try {
142 addShutdownHook(threadFactory.newThread(this));
143 state.set(State.STARTED);
144 } catch (final IllegalStateException ex) {
145 state.set(State.STOPPED);
146 throw ex;
147 } catch (final Exception e) {
148 LOGGER.catching(e);
149 state.set(State.STOPPED);
150 }
151 }
152 }
153
154 private void addShutdownHook(final Thread thread) {
155 shutdownHookRef = new WeakReference<>(thread);
156 Runtime.getRuntime().addShutdownHook(thread);
157 }
158
159 @Override
160 public void stop() {
161 stop(AbstractLifeCycle.DEFAULT_STOP_TIMEOUT, AbstractLifeCycle.DEFAULT_STOP_TIMEUNIT);
162 }
163
164
165
166
167 @Override
168 public boolean stop(final long timeout, final TimeUnit timeUnit) {
169 if (state.compareAndSet(State.STARTED, State.STOPPING)) {
170 try {
171 removeShutdownHook();
172 } finally {
173 state.set(State.STOPPED);
174 }
175 }
176 return true;
177 }
178
179 private void removeShutdownHook() {
180 final Thread shutdownThread = shutdownHookRef.get();
181 if (shutdownThread != null) {
182 Runtime.getRuntime().removeShutdownHook(shutdownThread);
183 shutdownHookRef.enqueue();
184 }
185 }
186
187 @Override
188 public State getState() {
189 return state.get();
190 }
191
192
193
194
195
196
197 @Override
198 public boolean isStarted() {
199 return state.get() == State.STARTED;
200 }
201
202 @Override
203 public boolean isStopped() {
204 return state.get() == State.STOPPED;
205 }
206
207 }