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