|
3 | 3 | */
|
4 | 4 | package com.tc.lang;
|
5 | 5 |
|
6 |
| -import com.google.common.base.Throwables; |
7 |
| -import com.tc.config.schema.setup.ConfigurationSetupException; |
8 |
| -import com.tc.exception.DatabaseException; |
9 | 6 | import com.tc.exception.ExceptionHelper;
|
10 |
| -import com.tc.exception.ExceptionHelperImpl; |
11 |
| -import com.tc.exception.RuntimeExceptionHelper; |
12 |
| -import com.tc.handler.CallbackStartupExceptionLoggingAdapter; |
13 | 7 | import com.tc.logging.CallbackOnExitHandler;
|
14 |
| -import com.tc.logging.CallbackOnExitState; |
15 |
| -import com.tc.logging.TCLogger; |
16 |
| -import com.tc.properties.TCPropertiesConsts; |
17 |
| -import com.tc.properties.TCPropertiesImpl; |
18 |
| -import com.tc.util.TCDataFileLockingException; |
19 |
| -import com.tc.util.concurrent.ThreadUtil; |
20 |
| -import com.tc.util.startuplock.FileNotCreatedException; |
21 |
| -import com.tc.util.startuplock.LocationNotCreatedException; |
22 | 8 |
|
23 |
| -import java.lang.reflect.Field; |
24 |
| -import java.net.BindException; |
25 |
| -import java.util.HashMap; |
26 |
| -import java.util.List; |
27 |
| -import java.util.Map; |
28 |
| -import java.util.Timer; |
29 |
| -import java.util.TimerTask; |
30 |
| -import java.util.concurrent.CopyOnWriteArrayList; |
| 9 | +public interface ThrowableHandler { |
31 | 10 |
|
32 |
| -/** |
33 |
| - * Handles Throwable appropriately by printing messages to the logger, etc. Deal with nasty problems that can occur as |
34 |
| - * the Terracotta Client is shutting down. |
35 |
| - */ |
36 |
| -public class ThrowableHandler { |
37 |
| - // XXX: The dispatching in this class is retarded, but I wanted to move as much of the exception handling into a |
38 |
| - // single place first, then come up with fancy ways of dealing with them. --Orion 03/20/2006 |
39 |
| - |
40 |
| - // instantiating message here to avoid any allocations on OOME |
41 |
| - private static final String OOME_ERROR_MSG = "Fatal error: out of available memory. Exiting..."; |
42 |
| - protected final TCLogger logger; |
43 |
| - private final ExceptionHelperImpl helper; |
44 |
| - private final List<CallbackOnExitHandler> callbackOnExitDefaultHandlers = new CopyOnWriteArrayList(); |
45 |
| - private final Map<Class<?>, CallbackOnExitHandler> callbackOnExitExceptionHandlers = new HashMap(); |
46 |
| - private final Object dumpLock = new Object(); |
47 |
| - |
48 |
| - private static final long TIME_OUT = TCPropertiesImpl |
49 |
| - .getProperties() |
50 |
| - .getLong(TCPropertiesConsts.L2_DUMP_ON_EXCEPTION_TIMEOUT) |
51 |
| - * 1000; |
52 |
| - private boolean isExitScheduled; |
53 |
| - private boolean isDumpTaken; |
54 |
| - |
55 |
| - /** |
56 |
| - * Construct a new ThrowableHandler with a logger |
57 |
| - * |
58 |
| - * @param logger Logger |
59 |
| - */ |
60 |
| - public ThrowableHandler(TCLogger logger) { |
61 |
| - this.logger = logger; |
62 |
| - helper = new ExceptionHelperImpl(); |
63 |
| - helper.addHelper(new RuntimeExceptionHelper()); |
64 |
| - registerStartupExceptionCallbackHandlers(); |
65 |
| - } |
66 |
| - |
67 |
| - public void addHelper(ExceptionHelper toAdd) { |
68 |
| - helper.addHelper(toAdd); |
69 |
| - } |
70 |
| - |
71 |
| - protected void registerStartupExceptionCallbackHandlers() { |
72 |
| - addCallbackOnExitExceptionHandler(ConfigurationSetupException.class, new CallbackStartupExceptionLoggingAdapter()); |
73 |
| - String bindExceptionExtraMessage = ". Please make sure the server isn't already running or choose a different port."; |
74 |
| - addCallbackOnExitExceptionHandler(BindException.class, |
75 |
| - new CallbackStartupExceptionLoggingAdapter(bindExceptionExtraMessage)); |
76 |
| - addCallbackOnExitExceptionHandler(DatabaseException.class, new CallbackStartupExceptionLoggingAdapter()); |
77 |
| - addCallbackOnExitExceptionHandler(LocationNotCreatedException.class, new CallbackStartupExceptionLoggingAdapter()); |
78 |
| - addCallbackOnExitExceptionHandler(FileNotCreatedException.class, new CallbackStartupExceptionLoggingAdapter()); |
79 |
| - addCallbackOnExitExceptionHandler(TCDataFileLockingException.class, new CallbackStartupExceptionLoggingAdapter()); |
80 |
| - } |
81 |
| - |
82 |
| - public void addCallbackOnExitDefaultHandler(CallbackOnExitHandler callbackOnExitHandler) { |
83 |
| - callbackOnExitDefaultHandlers.add(callbackOnExitHandler); |
84 |
| - } |
85 |
| - |
86 |
| - public void addCallbackOnExitExceptionHandler(Class c, CallbackOnExitHandler exitHandler) { |
87 |
| - callbackOnExitExceptionHandlers.put(c, exitHandler); |
88 |
| - } |
89 |
| - |
90 |
| - /** |
91 |
| - * Handle throwable occurring on thread |
92 |
| - * |
93 |
| - * @param thread Thread receiving Throwable |
94 |
| - * @param t Throwable |
95 |
| - */ |
96 |
| - public void handleThrowable(final Thread thread, final Throwable t) { |
97 |
| - handlePossibleOOME(t); |
98 |
| - |
99 |
| - if (isThreadGroupDestroyed(thread, t)) { |
100 |
| - logger.warn("Ignoring an attempt to start a JMX thread during shutdown.", t); |
101 |
| - return; |
102 |
| - } |
103 |
| - |
104 |
| - if (isJMXTerminatedException(t)) { |
105 |
| - logger.warn("Ignoring a Thread Service termination error from JMX.", t); |
106 |
| - return; |
107 |
| - } |
108 |
| - |
109 |
| - if (isNotificationFetcherThread(thread)) { |
110 |
| - // DEV-5006 -- Do not exit L2. |
111 |
| - logger.warn("Got Exception in JMX Notification forwarding", t); |
112 |
| - return; |
113 |
| - } |
114 |
| - |
115 |
| - final CallbackOnExitState throwableState = new CallbackOnExitState(t); |
116 |
| - scheduleExit(throwableState); |
117 |
| - |
118 |
| - final Throwable proximateCause = helper.getProximateCause(t); |
119 |
| - final Throwable ultimateCause = helper.getUltimateCause(t); |
120 |
| - |
121 |
| - Object registeredExitHandlerObject; |
122 |
| - try { |
123 |
| - if ((registeredExitHandlerObject = callbackOnExitExceptionHandlers.get(proximateCause.getClass())) != null) { |
124 |
| - ((CallbackOnExitHandler) registeredExitHandlerObject).callbackOnExit(throwableState); |
125 |
| - } else if ((registeredExitHandlerObject = callbackOnExitExceptionHandlers.get(ultimateCause.getClass())) != null) { |
126 |
| - ((CallbackOnExitHandler) registeredExitHandlerObject).callbackOnExit(throwableState); |
127 |
| - } else { |
128 |
| - handleDefaultException(thread, throwableState); |
129 |
| - } |
130 |
| - } catch (Throwable throwable) { |
131 |
| - logger.error("Error while handling uncaught expection" + t.getCause(), throwable); |
132 |
| - } |
133 |
| - |
134 |
| - exit(throwableState); |
135 |
| - } |
136 |
| - |
137 |
| - private static boolean isThreadGroupDestroyed(Thread thread, Throwable t) { |
138 |
| - // see EHCTERR-32 |
139 |
| - if (t instanceof IllegalThreadStateException) { |
140 |
| - StackTraceElement[] stack = t.getStackTrace(); |
141 |
| - StackTraceElement bottom = stack[stack.length - 1]; |
142 |
| - if (stack[0].getClassName().equals("java.lang.ThreadGroup") && stack[0].getMethodName().equals("addUnstarted") |
143 |
| - && bottom.getClassName().equals("javax.management.remote.generic.GenericConnectorServer$Receiver") |
144 |
| - && bottom.getMethodName().equals("run")) { return true; } |
145 |
| - } |
146 |
| - |
147 |
| - return false; |
148 |
| - } |
149 |
| - |
150 |
| - /** |
151 |
| - * Makes sure we don't allocate any heap objects on OOME. |
152 |
| - * {@code -XX:+HeapDumpOnOutOfMemoryError} should take care of debug information. |
153 |
| - * Considering {@code -XX:OnOutOfMemoryError=<cmd>} option might be also a good idea. |
154 |
| - */ |
155 |
| - void handlePossibleOOME(final Throwable t) { |
156 |
| - Throwable rootCause = Throwables.getRootCause(t); |
157 |
| - if (rootCause instanceof OutOfMemoryError) { |
158 |
| - try { |
159 |
| - logger.error(OOME_ERROR_MSG); |
160 |
| - String msg = rootCause.getMessage(); |
161 |
| - if (msg != null && msg.length() > 0) { |
162 |
| - logger.error(msg); |
163 |
| - } |
164 |
| - } finally { |
165 |
| - exit(ServerExitStatus.EXITCODE_FATAL_ERROR); |
166 |
| - } |
167 |
| - } |
168 |
| - } |
169 |
| - |
170 |
| - private synchronized void scheduleExit(final CallbackOnExitState throwableState) { |
171 |
| - if (isExitScheduled) { return; } |
172 |
| - isExitScheduled = true; |
173 |
| - |
174 |
| - TimerTask timerTask = new TimerTask() { |
175 |
| - @Override |
176 |
| - public void run() { |
177 |
| - exit(throwableState); |
178 |
| - } |
179 |
| - }; |
180 |
| - Timer timer = new Timer("Dump On Timeout Timer"); |
181 |
| - timer.schedule(timerTask, TIME_OUT); |
182 |
| - } |
183 |
| - |
184 |
| - private void handleDefaultException(Thread thread, CallbackOnExitState throwableState) { |
185 |
| - logException(thread, throwableState); |
186 |
| - |
187 |
| - synchronized (dumpLock) { |
188 |
| - if (!isDumpTaken) { |
189 |
| - isDumpTaken = true; |
190 |
| - for (CallbackOnExitHandler handler : callbackOnExitDefaultHandlers) { |
191 |
| - handler.callbackOnExit(throwableState); |
192 |
| - } |
193 |
| - } |
194 |
| - } |
195 |
| - } |
196 |
| - |
197 |
| - private void logException(Thread thread, CallbackOnExitState throwableState) { |
198 |
| - try { |
199 |
| - // We need to make SURE that our stacktrace gets printed, when using just the logger sometimes the VM exits |
200 |
| - // before the stacktrace prints |
201 |
| - throwableState.getThrowable().printStackTrace(System.err); |
202 |
| - System.err.flush(); |
203 |
| - logger.error("Thread:" + thread + " got an uncaught exception. calling CallbackOnExitDefaultHandlers.", |
204 |
| - throwableState.getThrowable()); |
205 |
| - } catch (Exception e) { |
206 |
| - // IGNORE EXCEPTION HERE |
207 |
| - } |
208 |
| - } |
209 |
| - |
210 |
| - private void exit(CallbackOnExitState throwableState) { |
211 |
| - boolean autoRestart = TCPropertiesImpl.getProperties().getBoolean(TCPropertiesConsts.L2_NHA_AUTORESTART); |
| 11 | + void handleThrowable(Thread thread, Throwable throwable); |
212 | 12 |
|
213 |
| - logger.info("ExitState : " + throwableState + "; AutoRestart: " + autoRestart); |
214 |
| - if (autoRestart && throwableState.isRestartNeeded()) { |
215 |
| - exit(ServerExitStatus.EXITCODE_RESTART_REQUEST); |
216 |
| - } else { |
217 |
| - exit(ServerExitStatus.EXITCODE_STARTUP_ERROR); |
218 |
| - } |
219 |
| - } |
| 13 | + void addHelper(ExceptionHelper helper); |
220 | 14 |
|
221 |
| - protected synchronized void exit(int status) { |
222 |
| - // let all the logging finish |
223 |
| - ThreadUtil.reallySleep(2000); |
224 |
| - System.exit(status); |
225 |
| - } |
| 15 | + void handlePossibleOOME(Throwable t); |
226 | 16 |
|
227 |
| - private static boolean isNotificationFetcherThread(Thread thread) { |
228 |
| - // UGLY Way to Ignore exception in JMX Notification Forwarder Thread. |
229 |
| - try { |
230 |
| - Field runnableField = thread.getClass().getDeclaredField("target"); |
231 |
| - runnableField.setAccessible(true); |
232 |
| - Object runnable = runnableField.get(thread); |
233 |
| - if (runnable != null && runnable.getClass().getSimpleName().equals("NotifFetcher")) { |
234 |
| - return true; |
235 |
| - } else { |
236 |
| - return false; |
237 |
| - } |
238 |
| - } catch (Throwable e) { |
239 |
| - return false; |
240 |
| - } |
| 17 | + void addCallbackOnExitDefaultHandler(CallbackOnExitHandler callbackOnExitHandler); |
241 | 18 |
|
242 |
| - } |
| 19 | + void addCallbackOnExitExceptionHandler(Class c, CallbackOnExitHandler callbackOnExitHandler); |
243 | 20 |
|
244 |
| - private static boolean isJMXTerminatedException(Throwable throwable) { |
245 |
| - return throwable instanceof IllegalStateException && |
246 |
| - throwable.getMessage().contains("The Thread Service has been terminated."); |
247 |
| - } |
248 | 21 | }
|
0 commit comments