|
20.5. Things That Make It GoIn order to explain how to integrate an Open Source application server into your system, we have to do a little Linux Sysadmin training. We need to show you how server processes are generally managed on Linux systems. 20.5.1. System V Init SystemVirtually all Linux distributions use some variant of the System V init system to create and customize programs and services that run at the startup of the box. Now, we don't want to write a Linux system administration manual, but we do need to tell you enough to decide how to make JBoss available when needed on your server. The core of the System V init system is the /etc/inittab file. Everything else devolves from this configuration file. In the days before network services, the primary startup tasks were to get getty programs running and then run a single startup shell script. The /etc/inittab file handles these tasks beautifully. Since then, the world of UNIX and Linux has become a complex mixture of client-server programs and protocols, so a complex set of conventions has been developed to turn the primitive /etc/inittab into a much richer set of controls. Let's take a very brief look at /etc/inittab and how it works; then we'll move on to the extended scripts that manage server processes. That is where we will integrate JBoss. A key concept in the System V init system is the runlevel. The idea is that a system can have a number of "standard" configurations, numbered from 0 to 6, where 0 is shutdown, 1 is single-user, 2 to 5 are up to the system administrator, and 6 is reboot. The init[9] command can be used (by the root user) to change the system from its current runlevel to another:
# init 1 What happens when you issue such a command is determined by the /etc/inittab file. Let's take a look at the out-of-the-box /etc/inittab file from a Fedora Core 1[10] system (Example 20.1).
This is a pretty complex file, and we don't want to bog down in it too much, since most of what interests us occurs outside this file. The basic format of a line in /etc/inittab is: id:runlevels:action:process The id is a unique 14 character identifier. The runlevels is a list of the runlevel numbers to which the record applies. The action specifies what action is to be taken. The process is the program to run. The respawn action, for example, tells init that when the process exits, it should be run again. The once action says it should be run once on transition to the runlevel. We won't go into too much more here. See the man inittab page for details. The part that concerns us are the l0 through l6 entries. Note that these cause the /etc/rc.d/rc script to be run once, with the runlevel passed as an argument. This is the key to System V init system. Example 20.1. Fedora Core 1 default /etc/inittab file# # inittab This file describes how the INIT process should set up # the system in a certain runlevel. # # Author: Miquel van Smoorenburg, miquels@drinkel.nl.mugnet.org> # Modified for RHS Linux by Marc Ewing and Donnie Barnes # # Default runlevel. The runlevels used by RHS are: # 0 - halt (Do NOT set initdefault to this) # 1 - Single user mode # 2 - Multiuser, without NFS # (The same as 3, if you do not have networking) # 3 - Full multiuser mode # 4 - unused # 5 - X11 # 6 - reboot (Do NOT set initdefault to this) # id:5:initdefault: # System initialization. si::sysinit:/etc/rc.d/rc.sysinit l0:0:wait:/etc/rc.d/rc 0 l1:1:wait:/etc/rc.d/rc 1 l2:2:wait:/etc/rc.d/rc 2 l3:3:wait:/etc/rc.d/rc 3 l4:4:wait:/etc/rc.d/rc 4 l5:5:wait:/etc/rc.d/rc 5 l6:6:wait:/etc/rc.d/rc 6 # Trap CTRL-ALT-DELETE ca::ctrlaltdel:/sbin/shutdown -t3 -r now # When our UPS tells us power has failed, assume we have a few minutes # of power left. Schedule a shutdown for 2 minutes from now. # This does, of course, assume you have powered installed and your # UPS connected and working correctly. pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down" # If power was restored before the shutdown kicked in, cancel it. pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled" # Run gettys in standard runlevels 1:2345:respawn:/sbin/mingetty tty1 2:2345:respawn:/sbin/mingetty tty2 3:2345:respawn:/sbin/mingetty tty3 4:2345:respawn:/sbin/mingetty tty4 5:2345:respawn:/sbin/mingetty tty5 6:2345:respawn:/sbin/mingetty tty6 # Run xdm in runlevel 5 x:5:respawn:/etc/X11/prefdm -nodaemon Note Some Linux distributions run different scripts for each runlevel instead of passing the runlevel as an argument to a single script. The details are not important. The net effect is that a script is run for each runlevel. Sure, you could put the code to run JBoss directly in that script if you want. But these scripts have been designed to handle arbitrary sets of services without you having to modify those scripts directly. How? By doing what Linux (and its UNIX antecedents) does so well: making complex systems out of simple parts. Each service you might wish to start and stop gets a shell script that controls it. This shell script must take a command argument. The minimum set of commands that must be supported are start and stop. Other options such as restart and status are often supported, but start and stop are the important ones. The script for atd, the one-shot job scheduler, is a fine example. Let's take a look at it (Example 20.2). Example 20.2. The atd init shell script#!/bin/bash # # /etc/rc.d/init.d/atd # # Starts the at daemon # # chkconfig: 345 95 5 # description: Runs commands scheduled by the at command at the \ # time specified when at was run, and runs batch commands when \ # the load average is low enough. # processname: atd # Source function library. . /etc/init.d/functions test -x /usr/sbin/atd || exit 0 RETVAL=0 # # See how we were called. # prog="atd" start() { # Check if atd is already running if [ ! -f /var/lock/subsys/atd ]; then echo -n $"Starting $prog: " daemon /usr/sbin/atd RETVAL=$? [ $RETVAL -eq 0 ] touch /var/lock/subsys/atd echo fi return $RETVAL } stop() { echo -n $"Stopping $prog: " killproc /usr/sbin/atd RETVAL=$? [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/atd echo return $RETVAL } restart() { stop start } reload() { restart } status_at() { status /usr/sbin/atd } case "$1" in start) start ;; stop) stop ;; reload|restart) restart ;; condrestart) if [ -f /var/lock/subsys/atd ]; then restart fi ;; status) status_at ;; *) echo $"Usage: $0 {start|stop|restart|condrestart|status}" exit 1 esac exit $? exit $RETVAL This script is from a RedHat Linux system. Those comments at the top are a magic incantation for the chkconfig program that ships with that distribution[11] (and with Fedora Core). We'll talk more about chkconfig in the next section.
As you can see, the basic premise is that when a daemon is started, the process ID is saved into a file. If the "stop" option is passed, the PID is looked up and the process is killed. That's the basic idea. But wait! There's more! Each runlevel has a directory of scripts. Let's look at the contents of such a directory (Example 20.3). Example 20.3. A directory of scripts[mschwarz@host238 mschwarz]$ cd /etc/rc5.d [mschwarz@host238 rc5.d]$ ls K01yum K73ypbind S18rpcgssd S58ntpd K05saslauthd K74nscd S19rpcidmapd S80sendmail K11jboss K89netplugd S19rpcsvcgssd S80spamassassin K12mysqld S00microcode_ctl S20random S85gpm K15httpd S04readahead_early S24pcmcia S90crond K15postgresql S05kudzu S25netfs S90vmware K20nfs S06cpuspeed S26apmd S90xfs K24irda S08iptables S28autofs S95anacron K35smb S09isdn S40smartd S95atd K35vncserver S10network S44acpid S96readahead K35winbind S12syslog S55cups S97messagebus K36lisa S13irqbalance S55sshd S97rhnsd K50snmpd S13portmap S56rawdevices S99local K50snmptrapd S14nfslock S56xinetd Notice the file S95atd? Let's look at the long form ls output for that file: [mschwarz@host238 rc5.d]$ ls -la S95atd lrwxrwxrwx 1 root root 13 Feb 2 02:08 S95atd -> ../init.d/atd The file is a symbolic link to the file in the init.d directory! If you take a look at the actual script run by the /etc/inittab file on a runlevel change, you will notice that what it does is to pick up all the files in the rcX.d directory (where X is the runlevel being changed to[12]) that begin with the letter K, run through them in numerical order, and execute the linked scripts with stop as the argument. It then picks up all the files that begin with S, runs through them in numerical order, and executes the linked scripts with start as the argument.
This sounds like a mess, but it is actually a very nice way to automate the starting and stopping of services by runlevel. Adding or removing a new service is simply a matter of creating the /etc/init.d script, and then adding the appropriate symlinks to the rcX.d directories.[13] So, first we have to take an init script and modify it to run JBoss.
20.5.2. RedHat/Fedora chkconfigRedHat and its stepchild, Fedora, use a program called chkconfig to automate the setup and integration of init scripts. The chkconfig program has four basic functions. Two involve adding and removing services from management. That's our main interest here, but we'll get to that in a moment. The other two involve querying and setting the runlevels in which services run. That is the more common use, so we'll look at those first. [root@host238 root]# chkconfig --list ntpd ntpd 0:off 1:off 2:off 3:on 4:off 5:on 6:off Tip chkconfig --list without specifying a service name will list all the services managed by chkconfig, including those that are provided by xinetd, which we will not cover here. As you can see, ntpd runs at runlevels 3 and 5, and does not run at any others. The --list argument lets you query the runlevels. [root@host238 root]# chkconfig --levels 2345 ntpd on [root@host238 root]# chkconfig --list ntpd ntpd 0:off 1:off 2:on 3:on 4:on 5:on 6:off The --levels argument lets you specify a list of runlevels that will apply to the named service. The last argument may be on or off to specify which setting to apply to those runlevels. The current value (on or off) for a specified runlevel is overwritten by whatever you specify. There is more to this; see the manpage for chkconfig for details. Now, before we put JBoss under management, we need to make a script for it. Or rather, we need to modify the one provided by JBoss. In the bin subdirectory of JBoss, you will find a script called jboss_init_redhat.sh. You will notice that it has the "chkconfig mojo" in itthat is, the "chkconfig:" comment line. We mentioned this in passing when we looked at the atd init script, but we didn't tell you what those three numbers after the colon actually mean. The first is the list of runlevels in which you want the program to run. The second is the start priority, which is the number that will follow the S in the rcX.d runlevel symlink directory. The third number is the stop priority, which is the number that will follow the K in the rcX.d runlevel symlink directory. These start and stop priority numbers can be very important indeed. Some services (like NFS) depend upon others (like portmap). Your JBoss server might depend on a service like mysqld or postgresql. Don't toy with these orders lightly. You can seriously mess up your services if you don't know what you are doing. Still, you will probably have to tweak things to get them completely right. Just be cautious and think about every change. Example 20.4 is the script as it comes with JBoss 3.2.3. There are three things we have to change here. The first are the runlevels in the "chkconfig:" line (we'll show you the changed lines with a couple of lines of context): # # chkconfig: 345 80 20 # description: JBoss EJB Container # Next, we may need to change the paths to JBoss and to the Java runtime. In our case, if you installed into /usr/local and created the symbolic link as we suggested, you don't need to change the JBOSS_HOME, but you have to change the JAVAPTH variable:[14]
Example 20.4. Out-of-the-box JBoss init script for RedHat#!/bin/sh # # JBoss Control Script # # chkconfig: 3 80 20 # description: JBoss EJB Container # # To use this script, # run it as root - it will switch to the specified user. # It loses all console output - use the log. # # Here is a little (and extremely primitive) # startup/shutdown script for RedHat systems. It assumes # that JBoss lives in /usr/local/jboss, it's run by user # 'jboss' and JDK binaries are in /usr/local/jdk/bin. All # this can be changed in the script itself. # Bojan # # Either amend this script for your requirements # or just ensure that the following variables are set correctly # before calling the script. # [ #420297 ] JBoss startup/shutdown for RedHat # define where jboss is - this is the directory # containing directories log, bin, conf, etc. JBOSS_HOME=${JBOSS_HOME:-"/usr/local/jboss"} # make sure Java is on your path JAVAPTH=${JAVAPTH:-"/usr/local/jdk/bin"} # define the classpath for the shutdown class JBOSSCP=${JBOSSCP:-"$JBOSS_HOME/bin/shutdown.jar:$JBOSS_HOME/client/jnet.jar"} # define the script to use to start jboss JBOSSSH=${JBOSSSH:-"$JBOSS_HOME/bin/run.sh -c all"} if [ -n "$JBOSS_CONSOLE" -a ! -d "$JBOSS_CONSOLE" ]; then # ensure the file exists touch $JBOSS_CONSOLE fi if [ -n "$JBOSS_CONSOLE" -a ! -f "$JBOSS_CONSOLE" ]; then echo "WARNING: location for saving console log invalid: $JBOSS_CONSOLE" echo "WARNING: ignoring it and using /dev/null" JBOSS_CONSOLE="/dev/null" fi # define what will be done with the console log JBOSS_CONSOLE=${JBOSS_CONSOLE:-"/dev/null"} # define the user under which JBoss will run, # or use RUNASIS to run as the current user JBOSSUS=${JBOSSUS:-"jboss"} CMD_START="cd $JBOSS_HOME/bin; $JBOSSSH" CMD_STOP="java -classpath $JBOSSCP org.jboss.Shutdown --shutdown" if [ "$JBOSSUS" = "RUNASIS" ]; then SUBIT="" else SUBIT="su - $JBOSSUS -c " fi if [ -z "`echo $PATH | grep $JAVAPTH`" ]; then export PATH=$PATH:$JAVAPTH fi if [ ! -d "$JBOSS_HOME" ]; then echo JBOSS_HOME does not exist as a valid directory : $JBOSS_HOME exit 1 fi echo CMD_START = $CMD_START case "$1" in start) cd $JBOSS_HOME/bin if [ -z "$SUBIT" ]; then eval $CMD_START ${JBOSS_CONSOLE} 2>1 else $SUBIT "$CMD_START ${JBOSS_CONSOLE} 2>1 " fi ;; stop) if [ -z "$SUBIT" ]; then $CMD_STOP else $SUBIT "$CMD_STOP" fi ;; restart) $0 stop $0 start ;; *) echo "usage: $0 (start|stop|restart|help)" esac # define where JBoss is - this is the directory # containing directories log, bin, conf, etc. JBOSS_HOME=${JBOSS_HOME:-"/usr/local/jboss"} # make sure Java is on your path JAVAPTH=${JAVAPTH:-"/usr/java/jdk/bin"} Finally, we don't need to run the "all" configuration, we only need the default configuration at the moment, so we change the argument to the run.sh invocation: # define the script to use to start JBoss JBOSSSH=${JBOSSSH:-"$JBOSS_HOME/bin/run.sh -c default"}
Now, this script allows you to run JBoss as any user. It defaults to user jboss if none is specified. You have to decide what to do here. Without specifying a user, it will run as root. That is a major security risk. On an out-of-the-box RedHat or Fedora system, there is no user called jboss. We will have to create one. There are a lot of security concerns to creating a special "nonlogin" user. The most important involve changing the user entries in /etc/passwd and /etc/shadow after you create the user. Unfortunately, the JBoss program needs to run a shell script, so you cannot set the shell to /sbin/nologin as is usual. Set the password for the user in /etc/shadow to x, which is completely invalid and will forbid login to the account by password. Example 20.5. Using chkconfig to include JBoss start script[root@cvs root]# cd /usr/local/jboss/bin [root@cvs bin]# cp jboss_init_redhat.sh /etc/init.d/jboss [root@cvs bin]# chkconfig --add jboss [root@cvs bin]# chkconfig --list jboss jboss 0:off 1:off 2:off 3:on 4:on 5:on 6:off [root@cvs bin]# /etc/init.d/jboss start CMD_START = cd /usr/local/jboss/bin; /usr/local/jboss/bin/run.sh -c default Finally, you will need to add the user jboss to any groups you created for JBoss management (such as local in our case). Truth be told, it would be a good idea to use the jboss user to install JBoss. It will avoid having to deal with some file ownership and permission issues. If you do not do this, the simplest way to get this init script working (you will get permission errors) is to run chmod -R g+w /usr/local/jboss That will make the script work with the jboss user, provided jboss belongs to the group owner of the JBoss installation. The final step is to copy your modified script to its final destination and run chkconfig to install it in all the runlevels (Example 20.5). You now have JBoss running. You can start and stop it with the script, and it will come up and shut down automatically depending on the runlevel you switch to. Beauty, eh? 20.5.3. Other DistributionsYou don't need chkconfig to set up equivalent scripts. In fact, the same script provided by JBoss for RedHat will work with most distributions that use System V init system. You will have to copy the init script and then create the appropriate symlinks manually, or locate the automated setup tools for your particular distribution (Debian, for example, has many such tools which you select with their package management system). 20.5.4. IDE IntegrationAnother piece of software you might want to look at is JBoss-IDE,[15] an Eclipse plug-in for JBoss. The software is not downloadable from the footnoted Web site, it is available only from the Eclipse Install/Update manager, so run your copy of Eclipse and install it. We will not cover JBoss-IDE here, but if you use Eclipse as your development platform, JBoss-IDE is very useful for managing and deploying EJB's, servlets, and JSP.
|
|