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.io.Serializable;
21 import java.lang.ref.Reference;
22 import java.lang.ref.SoftReference;
23 import java.lang.ref.WeakReference;
24 import java.util.Collection;
25 import java.util.concurrent.CopyOnWriteArrayList;
26 import java.util.concurrent.Executors;
27 import java.util.concurrent.ThreadFactory;
28 import java.util.concurrent.atomic.AtomicReference;
29
30 import org.apache.logging.log4j.Logger;
31 import org.apache.logging.log4j.core.LifeCycle;
32 import org.apache.logging.log4j.status.StatusLogger;
33
34
35
36
37
38
39
40 public class DefaultShutdownCallbackRegistry implements ShutdownCallbackRegistry, LifeCycle, Runnable, Serializable {
41
42 private static final long serialVersionUID = 1L;
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 @Override
84 public Cancellable addShutdownCallback(final Runnable callback) {
85 if (isStarted()) {
86 final Cancellable receipt = new Cancellable() {
87
88 private final Reference<Runnable> hook = new SoftReference<>(callback);
89
90 @Override
91 public void cancel() {
92 hook.clear();
93 hooks.remove(this);
94 }
95
96 @Override
97 public void run() {
98 final Runnable hook = this.hook.get();
99 if (hook != null) {
100 hook.run();
101 this.hook.clear();
102 }
103 }
104
105 @Override
106 public String toString() {
107 return String.valueOf(hook.get());
108 }
109 };
110 hooks.add(receipt);
111 return receipt;
112 }
113 throw new IllegalStateException("Cannot add new shutdown hook as this is not started. Current state: " +
114 state.get().name());
115 }
116
117 @Override
118 public void initialize() {
119 }
120
121
122
123
124 @Override
125 public void start() {
126 if (state.compareAndSet(State.INITIALIZED, State.STARTING)) {
127 try {
128 addShutdownHook(threadFactory.newThread(this));
129 state.set(State.STARTED);
130 } catch (final Exception e) {
131 LOGGER.catching(e);
132 state.set(State.STOPPED);
133 }
134 }
135 }
136
137 private void addShutdownHook(final Thread thread) {
138 shutdownHookRef = new WeakReference<>(thread);
139 Runtime.getRuntime().addShutdownHook(thread);
140 }
141
142
143
144
145 @Override
146 public void stop() {
147 if (state.compareAndSet(State.STARTED, State.STOPPING)) {
148 try {
149 removeShutdownHook();
150 } finally {
151 state.set(State.STOPPED);
152 }
153 }
154 }
155
156 private void removeShutdownHook() {
157 final Thread shutdownThread = shutdownHookRef.get();
158 if (shutdownThread != null) {
159 Runtime.getRuntime().removeShutdownHook(shutdownThread);
160 shutdownHookRef.enqueue();
161 }
162 }
163
164 @Override
165 public State getState() {
166 return state.get();
167 }
168
169
170
171
172
173
174 @Override
175 public boolean isStarted() {
176 return state.get() == State.STARTED;
177 }
178
179 @Override
180 public boolean isStopped() {
181 return state.get() == State.STOPPED;
182 }
183
184 }