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 t) {
76 LOGGER.error(SHUTDOWN_HOOK_MARKER, "Caught exception executing shutdown hook {}", hook, t);
77 }
78 }
79 state.set(State.STOPPED);
80 }
81 }
82
83 private static class RegisteredCancellable implements Cancellable {
84
85 private final Reference<Runnable> hook;
86 private Collection<Cancellable> registered;
87
88 RegisteredCancellable(final Runnable callback, final Collection<Cancellable> registered) {
89 this.registered = registered;
90 hook = new SoftReference<>(callback);
91 }
92
93 @Override
94 public void cancel() {
95 hook.clear();
96 registered.remove(this);
97 registered = null;
98 }
99
100 @Override
101 public void run() {
102 final Runnable runnableHook = this.hook.get();
103 if (runnableHook != null) {
104 runnableHook.run();
105 this.hook.clear();
106 }
107 }
108
109 @Override
110 public String toString() {
111 return String.valueOf(hook.get());
112 }
113 }
114
115 @Override
116 public Cancellable addShutdownCallback(final Runnable callback) {
117 if (isStarted()) {
118 final Cancellable receipt = new RegisteredCancellable(callback, hooks);
119 hooks.add(receipt);
120 return receipt;
121 }
122 throw new IllegalStateException("Cannot add new shutdown hook as this is not started. Current state: " +
123 state.get().name());
124 }
125
126 @Override
127 public void initialize() {
128 }
129
130
131
132
133 @Override
134 public void start() {
135 if (state.compareAndSet(State.INITIALIZED, State.STARTING)) {
136 try {
137 addShutdownHook(threadFactory.newThread(this));
138 state.set(State.STARTED);
139 } catch (final IllegalStateException ex) {
140 state.set(State.STOPPED);
141 throw ex;
142 } catch (final Exception e) {
143 LOGGER.catching(e);
144 state.set(State.STOPPED);
145 }
146 }
147 }
148
149 private void addShutdownHook(final Thread thread) {
150 shutdownHookRef = new WeakReference<>(thread);
151 Runtime.getRuntime().addShutdownHook(thread);
152 }
153
154 @Override
155 public void stop() {
156 stop(AbstractLifeCycle.DEFAULT_STOP_TIMEOUT, AbstractLifeCycle.DEFAULT_STOP_TIMEUNIT);
157 }
158
159
160
161
162 @Override
163 public boolean stop(final long timeout, final TimeUnit timeUnit) {
164 if (state.compareAndSet(State.STARTED, State.STOPPING)) {
165 try {
166 removeShutdownHook();
167 } finally {
168 state.set(State.STOPPED);
169 }
170 }
171 return true;
172 }
173
174 private void removeShutdownHook() {
175 final Thread shutdownThread = shutdownHookRef.get();
176 if (shutdownThread != null) {
177 Runtime.getRuntime().removeShutdownHook(shutdownThread);
178 shutdownHookRef.enqueue();
179 }
180 }
181
182 @Override
183 public State getState() {
184 return state.get();
185 }
186
187
188
189
190
191
192 @Override
193 public boolean isStarted() {
194 return state.get() == State.STARTED;
195 }
196
197 @Override
198 public boolean isStopped() {
199 return state.get() == State.STOPPED;
200 }
201
202 }