I'm trying to get jBoss to run every time Linux starts, and it's been quite
an adventure.
We've worked out a script for use in /etc/rc.d/init.d/jboss, but launching
a Java application this way has a big problem: you can't shut it down
because you can't keep track of the PID.
So I wrote a C program wrapper that uses JNI to call jBoss, with the idea
that killing the C program will kill the JVM it creates and hence kill off
jBoss. It didn't work right away because JNI creates a null ClassLoader,
and jBoss uses the ClassLoader to locate its resources.
So I had the C program use JNI to call another Java application,
jbossd.java, which creates a ClassLoader then uses the new
JbossdClassLoader to launch jBoss. But it doesn't work, it doesn't return
an error message, and I'm stuck in ClassLoader Hell.
Here are the three parts, the startup script, the C program, and the Java
program.
First, the startup script, /etc/rc.d/init.d/jboss:
#!/bin/sh
#
# chkconfig: 4 85 15
# description: jbossd is the Jboss daemon
# processname: jbossd
# pidfile: /var/run/jbossd.pid
# Source function library.
. /etc/rc.d/init.d/functions
# See how we were called.
case "$1" in
start)
# Start daemons.
echo "jbossd"
PATH=$PATH:/usr/java/jdk1.3/bin
export PATH
CLASSPATH=/usr/java/jdk1.3/lib:/usr/local/jboss/bin:/usr/local/jboss/bin/run.jar
export CLASSPATH
/usr/local/jboss/bin/jbossd &
pidof jbossd > /var/run/jbossd.pid
touch /var/lock/subsys/jbossd
;;
stop)
# Stop daemons.
echo -n "Shutting down jbossd: "
kill -USR1 `cat /var/run/jbossd.pid`
rm -f /var/lock/subsys/jbossd
;;
status)
status jbossd
;;
restart|reload)
$0 stop
$0 start
;;
*)
echo "Usage: jbossd {start|stop|restart|reload|status}"
exit 1
esac
Here's the JNI wrapper, /usr/local/jboss/bin/jbossd.c:
#include <jni.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
/* The jbossd daemon launches jBoss, a Java application. When the daemon is
* killed, jBoss is shut down as well.
*
* Based on invoke.c from
* <http://java.sun.com/docs/books/tutorial/native1.1/invoking/invo.html>
*
* Compile with:
* gcc -lpthread \
-I /usr/java/jdk1.3/include \
-I /usr/java/jdk1.3/include/linux \
-o jbossd \
jbossd.c \
/usr/java/jdk1.3/jre/lib/i386/classic/libjvm.so \
/usr/java/jdk1.3/jre/lib/i386/libjava.so \
/usr/java/jdk1.3/jre/lib/i386/libverify.so \
/usr/java/jdk1.3/jre/lib/i386/native_threads/libhpi.so
*
* Then to run the daemon, edit the file "/etc/ld.so.conf" and add the
* following lines:
/usr/java/jdk1.3/jre/lib/i386/classic
/usr/java/jdk1.3/jre/lib/i386
/usr/java/jdk1.3/jre/lib/i386/native_threads
* Then run the command "ldconfig".
*
*/
#ifdef _WIN32
#define PATH_SEPARATOR ';'
#else /* UNIX */
#define PATH_SEPARATOR ':'
#endif
#define USER_CLASSPATH "/usr/local/jboss/bin:/usr/local/jboss/bin/run.jar"
/* wh
ere jbossd.class is */
JavaVM *jvm;
int check_cla(int argc, char* argv[])
{
if (argc != 1) {
return -1;
}
return 0;
}
/*
int daemon_init(void)
{
pid_t pid;
pid = fork();
if (pid < 0) {
return -1;
} else if (pid != 0) {
exit(0); // parent exits
}
setsid();
chdir("/");
umask(0);
return 0;
}
*/
int main(int argc, char* argv[]) {
JNIEnv *env;
JDK1_1InitArgs vm_args;
jint res;
jclass cls;
jmethodID mid;
jstring jstr;
jobjectArray args;
char classpath[4096];
if (check_cla(argc, argv)) {
fprintf(stderr, "%s: Usage error\n", argv[0]);
return -1;
}
/*
if (daemon_init()) {
fprintf(stderr, "%s: Error initializing jBoss daemon\n", argv[0]);
return -1;
}
*/
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Append USER_CLASSPATH to the end of default system class path */
sprintf(classpath, "%s%c%s",
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
vm_args.classpath = classpath;
/* Create the Java VM */
res = JNI_CreateJavaVM(&jvm,(void**)&env,&vm_args);
if (res < 0) {
fprintf(stderr, "%s: Can't create Java VM\n", argv[0]);
exit(1);
}
if (env == 0) {
fprintf(stderr, "%s: Can't find Java environment\n", argv[0]);
exit(3);
}
cls = (*env)->FindClass(env,"jbossd");
if (cls == 0) {
fprintf(stderr, "%s: Can't find class jbossd\n", argv[0]);
exit(4);
}
mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
if (mid == 0) {
fprintf(stderr, "%s: Can't find static method jbossd.main\n", argv[0]);
exit(5);
}
jstr = (*env)->NewStringUTF(env, "");
if (jstr == 0) {
fprintf(stderr, "%s: Can't create jstr\n",argv[0]);
exit(6);
}
args = (*env)->NewObjectArray(env, 1,
(*env)->FindClass(env, "java/lang/String"), jstr);
if (args == 0) {
fprintf(stderr, "%s: Can't create args array\n",argv[0]);
exit(7);
}
// Finally, call the main method of class jbossd
(*env)->CallStaticVoidMethod(env, cls, mid, args);
//(*jvm)->DestroyJavaVM(jvm);
return 0;
}
Here's the Java application that creates a ClassLoader and launches jBoss,
/usr/local/jboss/bin/jbossd.java:
// jbossd.java
//
// Launches jBoss. Called using JNI from jbossd.c.
//
// Compile with:
// javac -classpath run.jar jbossd.java
//
import java.util.Hashtable;
public class jbossd {
jbossd() {
// The args passed in from JNI are screwy. We don't need them
// anyway, so we'll invent our own set of blank args and send
// them along.
String newArgs[] = new String[1];
newArgs[0] = new String("");
try {
System.err.println("jbossd: Starting");
// This didn't work:
// org.jboss.Main m = new org.jboss.Main();
// When I tried it, jBoss started, but it died when org.jboss.Main
// tried to use its ClassLoader. It turns out that JNI has a null
// ClassLoader. So here's my attempt to create a classloader.
JbossdClassLoader loader = new JbossdClassLoader();
org.jboss.Main m =
(org.jboss.Main)loader.findClass("org.jboss.Main").newI
nstance();
m.main(newArgs);
System.err.println("jbossd: Launched.");
} catch (java.lang.Exception e) {
System.err.println("jbossd: Exception: " + e);
e.printStackTrace();
}
}
public static void main(String[] args) {
jbossd j = new jbossd();
}
class JbossdClassLoader extends ClassLoader {
public JbossdClassLoader() {
super();
}
public Class findClass(String name) throws ClassNotFoundException {
return loadClass(name, true );
}
protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Object o = cache.get(name);
if (o != null) {
if (o instanceof Class) {
return (Class) o;
}
}
Class c = findSystemClass(name);
cache.put(name, c);
if (resolve) {
resolveClass(c);
}
return c;
}
private Hashtable cache = new Hashtable();
}
}
Help!
-- Ken Jenks, http://abiblion.com/
Tools for reading.