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