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: 1023471 $)</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
044 public static void version()
045 {
046 System.err.println("java version \"" +
047 System.getProperty("java.version") + "\"");
048 System.err.println(System.getProperty("java.runtime.name") +
049 " (build " +
050 System.getProperty("java.runtime.version") + ")");
051 System.err.println(System.getProperty("java.vm.name") +
052 " (build " +
053 System.getProperty("java.vm.version") +
054 ", " + System.getProperty("java.vm.info") + ")");
055 System.err.println("commons daemon version \"" +
056 System.getProperty("commons.daemon.version") + "\"");
057 System.err.println("commons daemon process (id: " +
058 System.getProperty("commons.daemon.process.id") +
059 ", parent: " +
060 System.getProperty("commons.daemon.process.parent") + ")");
061 }
062
063 public static boolean check(String cn)
064 {
065 try {
066 /* Check the class name */
067 if (cn == null)
068 throw new NullPointerException("Null class name specified");
069
070 /* Get the ClassLoader loading this class */
071 ClassLoader cl = DaemonLoader.class.getClassLoader();
072 if (cl == null) {
073 System.err.println("Cannot retrieve ClassLoader instance");
074 return false;
075 }
076
077 /* Find the required class */
078 Class c = cl.loadClass(cn);
079
080 /* This should _never_ happen, but doublechecking doesn't harm */
081 if (c == null)
082 throw new ClassNotFoundException(cn);
083
084 /* Create a new instance of the daemon */
085 c.newInstance();
086
087 } catch (Throwable t) {
088 /* In case we encounter ANY error, we dump the stack trace and
089 * return false (load, start and stop won't be called).
090 */
091 t.printStackTrace(System.err);
092 return false;
093 }
094 /* The class was loaded and instantiated correctly, we can return
095 */
096 return true;
097 }
098
099 public static boolean load(String className, String args[])
100 {
101 try {
102 /* Make sure any previous instance is garbage collected */
103 System.gc();
104
105 /* Check if the underlying libray supplied a valid list of
106 arguments */
107 if (args == null)
108 args = new String[0];
109
110 /* Check the class name */
111 if (className == null)
112 throw new NullPointerException("Null class name specified");
113
114 /* Get the ClassLoader loading this class */
115 ClassLoader cl = DaemonLoader.class.getClassLoader();
116 if (cl == null) {
117 System.err.println("Cannot retrieve ClassLoader instance");
118 return false;
119 }
120 Class c;
121 if (className.charAt(0) == '@') {
122 /* Wrapp the class with DaemonWrapper
123 * and modify arguments to include the real class name.
124 */
125 c = DaemonWrapper.class;
126 String[] a = new String[args.length + 2];
127 a[0] = "-start";
128 a[1] = className.substring(1);
129 System.arraycopy(args, 0, a, 2, args.length);
130 args = a;
131 }
132 else
133 c = cl.loadClass(className);
134 /* This should _never_ happen, but doublechecking doesn't harm */
135 if (c == null)
136 throw new ClassNotFoundException(className);
137 /* Check interfaces */
138 boolean isdaemon = false;
139
140 try {
141 Class dclass =
142 cl.loadClass("org.apache.commons.daemon.Daemon");
143 isdaemon = dclass.isAssignableFrom(c);
144 }
145 catch (Exception cnfex) {
146 // Swallow if Daemon not found.
147 }
148
149 /* Check methods */
150 Class[] myclass = new Class[1];
151 if (isdaemon) {
152 myclass[0] = DaemonContext.class;
153 }
154 else {
155 myclass[0] = args.getClass();
156 }
157
158 init = c.getMethod("init", myclass);
159
160 myclass = null;
161 start = c.getMethod("start", myclass);
162 stop = c.getMethod("stop", myclass);
163 destroy = c.getMethod("destroy", myclass);
164
165 /* Create a new instance of the daemon */
166 daemon = c.newInstance();
167
168 if (isdaemon) {
169 /* Create a new controller instance */
170 controller = new Controller();
171
172 /* Set the availability flag in the controller */
173 controller.setAvailable(false);
174
175 /* Create context */
176 Context context = new Context();
177 context.setArguments(args);
178 context.setController(controller);
179
180 /* Now we want to call the init method in the class */
181 Object arg[] = new Object[1];
182 arg[0] = context;
183 init.invoke(daemon, arg);
184 }
185 else {
186 Object arg[] = new Object[1];
187 arg[0] = args;
188 init.invoke(daemon, arg);
189 }
190
191 }
192 catch (InvocationTargetException e) {
193 Throwable thrown = e.getTargetException();
194 /* DaemonInitExceptions can fail with a nicer message */
195 if (thrown instanceof DaemonInitException) {
196 failed(((DaemonInitException) thrown).getMessageWithCause());
197 }
198 else {
199 thrown.printStackTrace(System.err);
200 }
201 return false;
202 }
203 catch (Throwable t) {
204 /* In case we encounter ANY error, we dump the stack trace and
205 * return false (load, start and stop won't be called).
206 */
207 t.printStackTrace(System.err);
208 return false;
209 }
210 /* The class was loaded and instantiated correctly, we can return */
211 return true;
212 }
213
214 public static boolean start()
215 {
216 try {
217 /* Attempt to start the daemon */
218 Object arg[] = null;
219 start.invoke(daemon, arg);
220
221 /* Set the availability flag in the controller */
222 if (controller != null)
223 controller.setAvailable(true);
224
225 } catch (Throwable t) {
226 /* In case we encounter ANY error, we dump the stack trace and
227 * return false (load, start and stop won't be called).
228 */
229 t.printStackTrace(System.err);
230 return false;
231 }
232 return true;
233 }
234
235 public static boolean stop()
236 {
237 try {
238 /* Set the availability flag in the controller */
239 if (controller != null)
240 controller.setAvailable(false);
241
242 /* Attempt to stop the daemon */
243 Object arg[] = null;
244 stop.invoke(daemon, arg);
245
246 /* Run garbage collector */
247 System.gc();
248
249 }
250 catch (Throwable t) {
251 /* In case we encounter ANY error, we dump the stack trace and
252 * return false (load, start and stop won't be called).
253 */
254 t.printStackTrace(System.err);
255 return false;
256 }
257 return true;
258 }
259
260 public static boolean destroy()
261 {
262 try {
263 /* Attempt to stop the daemon */
264 Object arg[] = null;
265 destroy.invoke(daemon, arg);
266
267 /* Run garbage collector */
268 daemon = null;
269 controller = null;
270 System.gc();
271
272 } catch (Throwable t) {
273 /* In case we encounter ANY error, we dump the stack trace and
274 * return false (load, start and stop won't be called).
275 */
276 t.printStackTrace(System.err);
277 return false;
278 }
279 return true;
280 }
281
282 private static native void shutdown(boolean reload);
283 private static native void failed(String message);
284
285 public static class Controller
286 implements DaemonController
287 {
288
289 private boolean available = false;
290
291 private Controller()
292 {
293 super();
294 this.setAvailable(false);
295 }
296
297 private boolean isAvailable()
298 {
299 synchronized (this) {
300 return this.available;
301 }
302 }
303
304 private void setAvailable(boolean available)
305 {
306 synchronized (this) {
307 this.available = available;
308 }
309 }
310
311 public void shutdown()
312 throws IllegalStateException
313 {
314 synchronized (this) {
315 if (!this.isAvailable()) {
316 throw new IllegalStateException();
317 }
318 else {
319 this.setAvailable(false);
320 DaemonLoader.shutdown(false);
321 }
322 }
323 }
324
325 public void reload()
326 throws IllegalStateException
327 {
328 synchronized (this) {
329 if (!this.isAvailable()) {
330 throw new IllegalStateException();
331 }
332 else {
333 this.setAvailable(false);
334 DaemonLoader.shutdown(true);
335 }
336 }
337 }
338
339 public void fail()
340 {
341 fail(null, null);
342 }
343
344 public void fail(String message)
345 {
346 fail(message, null);
347 }
348
349 public void fail(Exception exception)
350 {
351 fail(null, exception);
352 }
353
354 public void fail(String message, Exception exception)
355 {
356 synchronized (this) {
357 this.setAvailable(false);
358 String msg = message;
359 if (exception != null) {
360 if (msg != null)
361 msg = msg + ": " + exception.toString();
362 else
363 msg = exception.toString();
364 }
365 DaemonLoader.failed(msg);
366 }
367 }
368
369 }
370
371 public static class Context
372 implements DaemonContext
373 {
374
375 private DaemonController daemonController = null;
376
377 private String[] args = null;
378
379 public DaemonController getController()
380 {
381 return daemonController;
382 }
383
384 public void setController(DaemonController controller)
385 {
386 this.daemonController = controller;
387 }
388
389 public String[] getArguments()
390 {
391 return args;
392 }
393
394 public void setArguments(String[]args)
395 {
396 this.args = args;
397 }
398
399 }
400 }
401