blob: 1e1df818810eabe150c0895940a6031c0869a4ef [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.daemon.support;
import org.apache.commons.daemon.DaemonContext;
import org.apache.commons.daemon.DaemonController;
import org.apache.commons.daemon.DaemonInitException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Used by jsvc for Daemon management.
*
* @version $Id$
*/
public final class DaemonLoader
{
// N.B. These static mutable variables need to be accessed using synch.
private static Controller controller = null; //@GuardedBy("this")
private static Object daemon = null; //@GuardedBy("this")
/* Methods to call */
private static Method init = null; //@GuardedBy("this")
private static Method start = null; //@GuardedBy("this")
private static Method stop = null; //@GuardedBy("this")
private static Method destroy = null; //@GuardedBy("this")
private static Method signal = null; //@GuardedBy("this")
public static void version()
{
System.err.println("java version \"" +
System.getProperty("java.version") + "\"");
System.err.println(System.getProperty("java.runtime.name") +
" (build " +
System.getProperty("java.runtime.version") + ")");
System.err.println(System.getProperty("java.vm.name") +
" (build " +
System.getProperty("java.vm.version") +
", " + System.getProperty("java.vm.info") + ")");
System.err.println("commons daemon version \"" +
System.getProperty("commons.daemon.version") + "\"");
System.err.println("commons daemon process (id: " +
System.getProperty("commons.daemon.process.id") +
", parent: " +
System.getProperty("commons.daemon.process.parent") + ")");
}
public static boolean check(String cn)
{
try {
/* Check the class name */
if (cn == null) {
throw new NullPointerException("Null class name specified");
}
/* Get the ClassLoader loading this class */
ClassLoader cl = DaemonLoader.class.getClassLoader();
if (cl == null) {
System.err.println("Cannot retrieve ClassLoader instance");
return false;
}
/* Find the required class */
Class c = cl.loadClass(cn);
/* This should _never_ happen, but doublechecking doesn't harm */
if (c == null) {
throw new ClassNotFoundException(cn);
}
/* Create a new instance of the daemon */
c.newInstance();
} catch (Throwable t) {
/* In case we encounter ANY error, we dump the stack trace and
* return false (load, start and stop won't be called).
*/
t.printStackTrace(System.err);
return false;
}
/* The class was loaded and instantiated correctly, we can return
*/
return true;
}
public static boolean signal()
{
try {
if (signal != null) {
signal.invoke(daemon, new Object[0]);
return true;
}
System.out.println("Daemon doesn't support signaling");
} catch (Throwable ex) {
System.err.println("Cannot send signal: " + ex);
ex.printStackTrace(System.err);
}
return false;
}
public static boolean load(String className, String args[])
{
try {
/* Make sure any previous instance is garbage collected */
System.gc();
/* Check if the underlying libray supplied a valid list of
arguments */
if (args == null) {
args = new String[0];
}
/* Check the class name */
if (className == null) {
throw new NullPointerException("Null class name specified");
}
/* Get the ClassLoader loading this class */
ClassLoader cl = DaemonLoader.class.getClassLoader();
if (cl == null) {
System.err.println("Cannot retrieve ClassLoader instance");
return false;
}
Class c;
if (className.charAt(0) == '@') {
/* Wrapp the class with DaemonWrapper
* and modify arguments to include the real class name.
*/
c = DaemonWrapper.class;
String[] a = new String[args.length + 2];
a[0] = "-start";
a[1] = className.substring(1);
System.arraycopy(args, 0, a, 2, args.length);
args = a;
}
else {
c = cl.loadClass(className);
}
/* This should _never_ happen, but doublechecking doesn't harm */
if (c == null) {
throw new ClassNotFoundException(className);
}
/* Check interfaces */
boolean isdaemon = false;
try {
Class dclass =
cl.loadClass("org.apache.commons.daemon.Daemon");
isdaemon = dclass.isAssignableFrom(c);
}
catch (Exception cnfex) {
// Swallow if Daemon not found.
}
/* Check methods */
Class[] myclass = new Class[1];
if (isdaemon) {
myclass[0] = DaemonContext.class;
}
else {
myclass[0] = args.getClass();
}
init = c.getMethod("init", myclass);
myclass = null;
start = c.getMethod("start", myclass);
stop = c.getMethod("stop", myclass);
destroy = c.getMethod("destroy", myclass);
try {
signal = c.getMethod("signal", myclass);
} catch (NoSuchMethodException e) {
// Signaling will be disabled.
}
/* Create a new instance of the daemon */
daemon = c.newInstance();
if (isdaemon) {
/* Create a new controller instance */
controller = new Controller();
/* Set the availability flag in the controller */
controller.setAvailable(false);
/* Create context */
Context context = new Context();
context.setArguments(args);
context.setController(controller);
/* Now we want to call the init method in the class */
Object arg[] = new Object[1];
arg[0] = context;
init.invoke(daemon, arg);
}
else {
Object arg[] = new Object[1];
arg[0] = args;
init.invoke(daemon, arg);
}
}
catch (InvocationTargetException e) {
Throwable thrown = e.getTargetException();
/* DaemonInitExceptions can fail with a nicer message */
if (thrown instanceof DaemonInitException) {
failed(((DaemonInitException) thrown).getMessageWithCause());
}
else {
thrown.printStackTrace(System.err);
}
return false;
}
catch (Throwable t) {
/* In case we encounter ANY error, we dump the stack trace and
* return false (load, start and stop won't be called).
*/
t.printStackTrace(System.err);
return false;
}
/* The class was loaded and instantiated correctly, we can return */
return true;
}
public static boolean start()
{
try {
/* Attempt to start the daemon */
Object arg[] = null;
start.invoke(daemon, arg);
/* Set the availability flag in the controller */
if (controller != null) {
controller.setAvailable(true);
}
} catch (Throwable t) {
/* In case we encounter ANY error, we dump the stack trace and
* return false (load, start and stop won't be called).
*/
t.printStackTrace(System.err);
return false;
}
return true;
}
public static boolean stop()
{
try {
/* Set the availability flag in the controller */
if (controller != null) {
controller.setAvailable(false);
}
/* Attempt to stop the daemon */
Object arg[] = null;
stop.invoke(daemon, arg);
/* Run garbage collector */
System.gc();
}
catch (Throwable t) {
/* In case we encounter ANY error, we dump the stack trace and
* return false (load, start and stop won't be called).
*/
t.printStackTrace(System.err);
return false;
}
return true;
}
public static boolean destroy()
{
try {
/* Attempt to stop the daemon */
Object arg[] = null;
destroy.invoke(daemon, arg);
/* Run garbage collector */
daemon = null;
controller = null;
System.gc();
} catch (Throwable t) {
/* In case we encounter ANY error, we dump the stack trace and
* return false (load, start and stop won't be called).
*/
t.printStackTrace(System.err);
return false;
}
return true;
}
private static native void shutdown(boolean reload);
private static native void failed(String message);
public static class Controller
implements DaemonController
{
private boolean available = false;
private Controller()
{
super();
this.setAvailable(false);
}
private boolean isAvailable()
{
synchronized (this) {
return this.available;
}
}
private void setAvailable(boolean available)
{
synchronized (this) {
this.available = available;
}
}
public void shutdown()
throws IllegalStateException
{
synchronized (this) {
if (!this.isAvailable()) {
throw new IllegalStateException();
}
this.setAvailable(false);
DaemonLoader.shutdown(false);
}
}
public void reload()
throws IllegalStateException
{
synchronized (this) {
if (!this.isAvailable()) {
throw new IllegalStateException();
}
this.setAvailable(false);
DaemonLoader.shutdown(true);
}
}
public void fail()
{
fail(null, null);
}
public void fail(String message)
{
fail(message, null);
}
public void fail(Exception exception)
{
fail(null, exception);
}
public void fail(String message, Exception exception)
{
synchronized (this) {
this.setAvailable(false);
String msg = message;
if (exception != null) {
if (msg != null) {
msg = msg + ": " + exception.toString();
}
else {
msg = exception.toString();
}
}
DaemonLoader.failed(msg);
}
}
}
public static class Context
implements DaemonContext
{
private DaemonController daemonController = null;
private String[] args = null;
public DaemonController getController()
{
return daemonController;
}
public void setController(DaemonController controller)
{
this.daemonController = controller;
}
public String[] getArguments()
{
return args;
}
public void setArguments(String[]args)
{
this.args = args;
}
}
}