001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.daemon.support;
019
020 import org.apache.commons.daemon.DaemonContext;
021 import org.apache.commons.daemon.DaemonController;
022 import org.apache.commons.daemon.DaemonInitException;
023
024 import java.lang.reflect.InvocationTargetException;
025 import java.lang.reflect.Method;
026
027 /*
028 * Used by jsvc for Daemon management.
029 *
030 * @version 1.0 <i>(SVN $Revision: 1130635 $)</i>
031 */
032 public final class DaemonLoader
033 {
034
035 // N.B. These static mutable variables need to be accessed using synch.
036 private static Controller controller = null; //@GuardedBy("this")
037 private static Object daemon = null; //@GuardedBy("this")
038 /* Methods to call */
039 private static Method init = null; //@GuardedBy("this")
040 private static Method start = null; //@GuardedBy("this")
041 private static Method stop = null; //@GuardedBy("this")
042 private static Method destroy = null; //@GuardedBy("this")
043 private static Method signal = null; //@GuardedBy("this")
044
045 public static void version()
046 {
047 System.err.println("java version \"" +
048 System.getProperty("java.version") + "\"");
049 System.err.println(System.getProperty("java.runtime.name") +
050 " (build " +
051 System.getProperty("java.runtime.version") + ")");
052 System.err.println(System.getProperty("java.vm.name") +
053 " (build " +
054 System.getProperty("java.vm.version") +
055 ", " + System.getProperty("java.vm.info") + ")");
056 System.err.println("commons daemon version \"" +
057 System.getProperty("commons.daemon.version") + "\"");
058 System.err.println("commons daemon process (id: " +
059 System.getProperty("commons.daemon.process.id") +
060 ", parent: " +
061 System.getProperty("commons.daemon.process.parent") + ")");
062 }
063
064 public static boolean check(String cn)
065 {
066 try {
067 /* Check the class name */
068 if (cn == null)
069 throw new NullPointerException("Null class name specified");
070
071 /* Get the ClassLoader loading this class */
072 ClassLoader cl = DaemonLoader.class.getClassLoader();
073 if (cl == null) {
074 System.err.println("Cannot retrieve ClassLoader instance");
075 return false;
076 }
077
078 /* Find the required class */
079 Class c = cl.loadClass(cn);
080
081 /* This should _never_ happen, but doublechecking doesn't harm */
082 if (c == null)
083 throw new ClassNotFoundException(cn);
084
085 /* Create a new instance of the daemon */
086 c.newInstance();
087
088 } catch (Throwable t) {
089 /* In case we encounter ANY error, we dump the stack trace and
090 * return false (load, start and stop won't be called).
091 */
092 t.printStackTrace(System.err);
093 return false;
094 }
095 /* The class was loaded and instantiated correctly, we can return
096 */
097 return true;
098 }
099
100 public static boolean signal()
101 {
102 try {
103 if (signal != null) {
104 signal.invoke(daemon, new Object[0]);
105 return true;
106 }
107 else {
108 System.out.println("Daemon doesn't support signaling");
109 }
110 } catch (Throwable ex) {
111 System.err.println("Cannot send signal: " + ex);
112 ex.printStackTrace(System.err);
113 }
114 return false;
115 }
116
117 public static boolean load(String className, String args[])
118 {
119 try {
120 /* Make sure any previous instance is garbage collected */
121 System.gc();
122
123 /* Check if the underlying libray supplied a valid list of
124 arguments */
125 if (args == null)
126 args = new String[0];
127
128 /* Check the class name */
129 if (className == null)
130 throw new NullPointerException("Null class name specified");
131
132 /* Get the ClassLoader loading this class */
133 ClassLoader cl = DaemonLoader.class.getClassLoader();
134 if (cl == null) {
135 System.err.println("Cannot retrieve ClassLoader instance");
136 return false;
137 }
138 Class c;
139 if (className.charAt(0) == '@') {
140 /* Wrapp the class with DaemonWrapper
141 * and modify arguments to include the real class name.
142 */
143 c = DaemonWrapper.class;
144 String[] a = new String[args.length + 2];
145 a[0] = "-start";
146 a[1] = className.substring(1);
147 System.arraycopy(args, 0, a, 2, args.length);
148 args = a;
149 }
150 else
151 c = cl.loadClass(className);
152 /* This should _never_ happen, but doublechecking doesn't harm */
153 if (c == null)
154 throw new ClassNotFoundException(className);
155 /* Check interfaces */
156 boolean isdaemon = false;
157
158 try {
159 Class dclass =
160 cl.loadClass("org.apache.commons.daemon.Daemon");
161 isdaemon = dclass.isAssignableFrom(c);
162 }
163 catch (Exception cnfex) {
164 // Swallow if Daemon not found.
165 }
166
167 /* Check methods */
168 Class[] myclass = new Class[1];
169 if (isdaemon) {
170 myclass[0] = DaemonContext.class;
171 }
172 else {
173 myclass[0] = args.getClass();
174 }
175
176 init = c.getMethod("init", myclass);
177
178 myclass = null;
179 start = c.getMethod("start", myclass);
180 stop = c.getMethod("stop", myclass);
181 destroy = c.getMethod("destroy", myclass);
182
183 try {
184 signal = c.getMethod("signal", myclass);
185 } catch (NoSuchMethodException e) {
186 // Signaling will be disabled.
187 }
188
189 /* Create a new instance of the daemon */
190 daemon = c.newInstance();
191
192 if (isdaemon) {
193 /* Create a new controller instance */
194 controller = new Controller();
195
196 /* Set the availability flag in the controller */
197 controller.setAvailable(false);
198
199 /* Create context */
200 Context context = new Context();
201 context.setArguments(args);
202 context.setController(controller);
203
204 /* Now we want to call the init method in the class */
205 Object arg[] = new Object[1];
206 arg[0] = context;
207 init.invoke(daemon, arg);
208 }
209 else {
210 Object arg[] = new Object[1];
211 arg[0] = args;
212 init.invoke(daemon, arg);
213 }
214
215 }
216 catch (InvocationTargetException e) {
217 Throwable thrown = e.getTargetException();
218 /* DaemonInitExceptions can fail with a nicer message */
219 if (thrown instanceof DaemonInitException) {
220 failed(((DaemonInitException) thrown).getMessageWithCause());
221 }
222 else {
223 thrown.printStackTrace(System.err);
224 }
225 return false;
226 }
227 catch (Throwable t) {
228 /* In case we encounter ANY error, we dump the stack trace and
229 * return false (load, start and stop won't be called).
230 */
231 t.printStackTrace(System.err);
232 return false;
233 }
234 /* The class was loaded and instantiated correctly, we can return */
235 return true;
236 }
237
238 public static boolean start()
239 {
240 try {
241 /* Attempt to start the daemon */
242 Object arg[] = null;
243 start.invoke(daemon, arg);
244
245 /* Set the availability flag in the controller */
246 if (controller != null)
247 controller.setAvailable(true);
248
249 } catch (Throwable t) {
250 /* In case we encounter ANY error, we dump the stack trace and
251 * return false (load, start and stop won't be called).
252 */
253 t.printStackTrace(System.err);
254 return false;
255 }
256 return true;
257 }
258
259 public static boolean stop()
260 {
261 try {
262 /* Set the availability flag in the controller */
263 if (controller != null)
264 controller.setAvailable(false);
265
266 /* Attempt to stop the daemon */
267 Object arg[] = null;
268 stop.invoke(daemon, arg);
269
270 /* Run garbage collector */
271 System.gc();
272
273 }
274 catch (Throwable t) {
275 /* In case we encounter ANY error, we dump the stack trace and
276 * return false (load, start and stop won't be called).
277 */
278 t.printStackTrace(System.err);
279 return false;
280 }
281 return true;
282 }
283
284 public static boolean destroy()
285 {
286 try {
287 /* Attempt to stop the daemon */
288 Object arg[] = null;
289 destroy.invoke(daemon, arg);
290
291 /* Run garbage collector */
292 daemon = null;
293 controller = null;
294 System.gc();
295
296 } catch (Throwable t) {
297 /* In case we encounter ANY error, we dump the stack trace and
298 * return false (load, start and stop won't be called).
299 */
300 t.printStackTrace(System.err);
301 return false;
302 }
303 return true;
304 }
305
306 private static native void shutdown(boolean reload);
307 private static native void failed(String message);
308
309 public static class Controller
310 implements DaemonController
311 {
312
313 private boolean available = false;
314
315 private Controller()
316 {
317 super();
318 this.setAvailable(false);
319 }
320
321 private boolean isAvailable()
322 {
323 synchronized (this) {
324 return this.available;
325 }
326 }
327
328 private void setAvailable(boolean available)
329 {
330 synchronized (this) {
331 this.available = available;
332 }
333 }
334
335 public void shutdown()
336 throws IllegalStateException
337 {
338 synchronized (this) {
339 if (!this.isAvailable()) {
340 throw new IllegalStateException();
341 }
342 else {
343 this.setAvailable(false);
344 DaemonLoader.shutdown(false);
345 }
346 }
347 }
348
349 public void reload()
350 throws IllegalStateException
351 {
352 synchronized (this) {
353 if (!this.isAvailable()) {
354 throw new IllegalStateException();
355 }
356 else {
357 this.setAvailable(false);
358 DaemonLoader.shutdown(true);
359 }
360 }
361 }
362
363 public void fail()
364 {
365 fail(null, null);
366 }
367
368 public void fail(String message)
369 {
370 fail(message, null);
371 }
372
373 public void fail(Exception exception)
374 {
375 fail(null, exception);
376 }
377
378 public void fail(String message, Exception exception)
379 {
380 synchronized (this) {
381 this.setAvailable(false);
382 String msg = message;
383 if (exception != null) {
384 if (msg != null)
385 msg = msg + ": " + exception.toString();
386 else
387 msg = exception.toString();
388 }
389 DaemonLoader.failed(msg);
390 }
391 }
392
393 }
394
395 public static class Context
396 implements DaemonContext
397 {
398
399 private DaemonController daemonController = null;
400
401 private String[] args = null;
402
403 public DaemonController getController()
404 {
405 return daemonController;
406 }
407
408 public void setController(DaemonController controller)
409 {
410 this.daemonController = controller;
411 }
412
413 public String[] getArguments()
414 {
415 return args;
416 }
417
418 public void setArguments(String[]args)
419 {
420 this.args = args;
421 }
422
423 }
424 }
425