En esta entrada abordaremos un tema bastante interesante: cómo correr un programa en Java como servicio en Windows o demonio en UNIX/Linux. Puesto que el API de Java no proporciona nada para estos casos, utiliza remos una librería llamada Java Service Wrapper. 
Dicha librería nos ofrece una serie de scripts y binarios preparados para diferentes sistemas operativos y arquitecturas,  que nos permitirán correr nuestros programas como un servicio; además  ofrece diferentes versiones: Profesional, Estándar y Comunity. En este  caso usaremos la versión Comunity que es libre y gratuita.
La mejor manera de aprender a usar dicha  librería es con un pequeño laboratorio, así que he preparado uno en  donde explico la integración más simple que existe con Java Service  Wrapper (existen 4 métodos; unos más avanzados que otros). La  integración sencilla que he escogido nos permitirá ejecutar como  servicio una aplicación que ya esté creada (probablemente ya empaquetada  en un .jar) y a la que no podemos o es difícil hacerle modificaciones.  Esta es la manera más sencilla de hacerlo, sin embargo tiene una  desventaja: al detener el servicio se envía directamente un  System.exit() a la JVM por lo que la aplicación no se cerrará  limpiamente.
Si quieres integrar tu aplicación de una  manera más segura y estás en la capacidad de prepararla para ello,  podrías intentar la integración avanzada en la que utilizas el API de  Java Service Wrapper para implementar métodos de inicio, pausa y  detención del servicio [Para este caso utilizaremos una aplicación de ejemplo que recibe un archivo como parámetro y escribe en él información sobre la memoria del sistema,  cada 60 segundos. He escogido este ejemplo porque posee varios aspectos  interesantes: requiere de librerías externas (tanto JARs como librerías  nativas [.so, dll, etc.]) y recibe parámetros.
Puedes descargar la aplicación de ejemplo de haciendo clic aquí. Si la ejecutáramos de manera convencional veríamos algo de este tipo:
gentookde@larry bin % java -jar servicio.jar /tmp/log
^C
gentookde@larry bin % cat /tmp/log
======Mon Oct 05 09:42:05 COT 2009=======
Cantidad de memoria RAM: 3952MB
Total: 4042664
Usada: 3509516
Disponible: 533148
Memoria SWAP total: 1052248
Memoria SWAP usada: 0
Memoria SWAP libre: 1052248
 ^C
gentookde@larry bin % cat /tmp/log
======Mon Oct 05 09:42:05 COT 2009=======
Cantidad de memoria RAM: 3952MB
Total: 4042664
Usada: 3509516
Disponible: 533148
Memoria SWAP total: 1052248
Memoria SWAP usada: 0
Memoria SWAP libre: 1052248
Como puedes ver se trata de una aplicación de consola común y corriente. La aplicación consta básicamente de:
- El ejecutable servicio.jar ubicado en el directorio bin/
- La librería sigar.jar en el directorio lib/
- Las librerías específicas de cada arquitectura de sigar en lib/
Pasos para la integración
Lo primero es descargar el paquete de Java Service Wrapper y descomprimirlo.
Supondremos que el directorio donde se encuentra Java Service Wrapper es $RUTA_JSW y que el directorio de la aplicación que vamos a convertir en servicio es $RUTA_APP.
1. Copiamos los siguentes archivos al directorio bin de nuestra aplicación de ejemplo…
UNIX/LINUX
cp $RUTA_JSW/bin/wrapper $RUTA_APP/bin/
cp $RUTA_JSW/src/bin/sh.script.in $RUTA_APP/bin/
cp $RUTA_JSW/lib/* $RUTA_APP/bin/
El archivo $RUTA_JSW/bin/wrapper podría  no existir; por lo tanto lo que debes copiar es el archivo wrapper-xxx  específico para tu plataforma. Por ejemplo, en este caso estoy usando  Gentoo Linux de 64bits por lo tanto copiaré el archivo  $RUTA_JSW/bin/wrapper-linux-x86-64.
WINDOWS
WINDOWS
copy $RUTA_JSW/bin/ wrapper-windows-x86-32.exe $RUTA_APP/bin/
copy $RUTA_JSW/src/bin/ App.bat.in $RUTA_APP/bin/
copy $RUTA_JSW/src/bin/ InstallApp-NT.bat.in $RUTA_APP/bin/
copy $RUTA_JSW/src/bin/ UninstallApp-NT.bat.in $RUTA_APP/bin/
copy $RUTA_JSW/lib/* $RUTA_APP/bin/
copy $RUTA_JSW/src/bin/ App.bat.in $RUTA_APP/bin/
copy $RUTA_JSW/src/bin/ InstallApp-NT.bat.in $RUTA_APP/bin/
copy $RUTA_JSW/src/bin/ UninstallApp-NT.bat.in $RUTA_APP/bin/
copy $RUTA_JSW/lib/* $RUTA_APP/bin/
2. Renombramos algunos archivos…
UNIX/LinuxAhora debes renombrar el script sh.script.in con el nombre de la aplicación con la que haremos la integración y darle privilegios de ejecución. En este casó se llama simplemente “servicio”:
cd $RUTA_APP/bin
mv sh.script.in servicio
chmod a+x servicio
mv sh.script.in servicio
chmod a+x servicio
Windows
Ahora debes renombrar los archivos .bat para que concuerden con el nombre de la aplicación con la que haremos la integración. En este casó se llama simplemente “servicio” (lo puedes hacer usando el explorador si no te gusta usar la consola, pero a mi me daría pena):
cd $RUTA_APP/bin
move App.bat.in servicio.bat
move InstallApp-NT.bat.in InstallServicio-NT.bat
move UninstallApp-NT.bat.in UninstallServicio-NT.bat
move App.bat.in servicio.bat
move InstallApp-NT.bat.in InstallServicio-NT.bat
move UninstallApp-NT.bat.in UninstallServicio-NT.bat
3. editar archivo de configuración
Editamos el archivo wrapper.conf y lo  dejamos en el directorio $RUTA_APP/conf/. El archivo para este ejemplo  tendrá el siguiente contenido (importante! lee la explicación si usas Windows):
| # Comando de Javawrapper.java.command=java# Clase que ejecutara el Wrapperwrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp# Librerias necesarias para ejecutar el programawrapper.java.classpath.1=../lib/wrapper.jarwrapper.java.classpath.2=servicio.jar# Java Library Path (ubicacion de las librerias wrapper.dll o wrapper.so)wrapper.java.library.path.1=../lib# Java Bits.  On applicable platforms, tells the JVM to run in 32 or 64-bit mode.wrapper.java.additional.auto_bits=TRUE# Parametros del programawrapper.app.parameter.1=net.casidiablo.servicio.Serviciowrapper.app.parameter.2=/tmp/log#********************************************************************# Wrapper General Properties#********************************************************************# Allow for the use of non-contiguous numbered propertieswrapper.ignore_sequence_gaps=TRUE# Title to use when running as a consolewrapper.console.title=servicio#********************************************************************# Wrapper Windows NT/2000/XP Service Properties#********************************************************************# Name of the servicewrapper.name=jgossip# Display name of the servicewrapper.displayname=Servicio# Description of the servicewrapper.description=Monitor de memoria# Mode in which the service is installed. AUTO_START or DEMAND_STARTwrapper.ntservice.starttype=AUTO_START# Allow the service to interact with the desktop.wrapper.ntservice.interactive=false | 
Explicación del archivo de configuración:
- wrapper.java.command=javaes el comando para ejecutar java. En este caso es simplemente java porque estoy en Linux y además tengo configurado correctamente el path. En Windows podría ser algo como: c:\archivos de programa\java\jdk1.6\bin\java.exe
- wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleAppindica el archivo a ejecutar es la clase WrapperSimpleApp que se usa en la integración sencilla. Esta clase envoltorio es la que permite ejecutar la aplicación como servicio.
- wrapper.java.classpath.1=../lib/wrapper.jarindican las rutas o archivos del classpath.
 wrapper.java.classpath.2=servicio.jar
- wrapper.java.library.path.1=../libindica el directorio donde se encuentran las librerías a usar.
- wrapper.app.parameter.1=net.casidiablo.servicio.Servicioestos son los parámetros que se pasarán al programa. Importante: como estamos haciendo la integración simple, el primer parámetro debe ser la clase que contiene el método main de nuestra aplicación.
 wrapper.app.parameter.2=/tmp/log
- wrapper.name=jgossipesta opción es para Windows y define el nombre del servicio en el sistema.
- wrapper.ntservice.starttype=AUTO_STARTsi estás en windows, define si el servicio quedará configurado para iniciar automáticamente.
4. Probar/Instalar el servicio…
Ahora veremos cómo podemos probar la aplicación para asegurarnos que ha quedado bien configurada antes de proceder con la instalación.
UNIX/LinuxEjecutamos el script ‘servicio’ que configuramos en el paso 1 y 2 con el parámetro console:
 ./servicio console
Deberías ver algo como:
Running un servicio que escribe cosas…
wrapper | –> Wrapper Started as Console
wrapper | Java Service Wrapper Community Edition 64-bit 3.3.6
wrapper | Copyright (C) 1999-2009 Tanuki Software, Ltd. All Rights Reserved.
wrapper | http://wrapper.tanukisoftware.org
wrapper |
wrapper | Launching a JVM…
jvm 1 | WrapperManager: Initializing…
wrapper | –> Wrapper Started as Console
wrapper | Java Service Wrapper Community Edition 64-bit 3.3.6
wrapper | Copyright (C) 1999-2009 Tanuki Software, Ltd. All Rights Reserved.
wrapper | http://wrapper.tanukisoftware.org
wrapper |
wrapper | Launching a JVM…
jvm 1 | WrapperManager: Initializing…
Lo detienes presionando Ctrl+C. Ahora,  si queremos probarlo como demonio basta con ejecutar los comandos  típicos de un demonio en UNIX/Linux:
$ ./servicio start
Starting un servicio que escribe cosas…
$ ./servicio stop
Stopping un servicio que escribe cosas…
Stopped un servicio que escribe cosas.
Starting un servicio que escribe cosas…
$ ./servicio stop
Stopping un servicio que escribe cosas…
Stopped un servicio que escribe cosas.
Para instalarlo basta con crear un enlace simbólico del script en el directorio /etc/init.d y añadirlo al listado de servicios a iniciar; por ejemplo:
sudo ln -sv $RUTA_APP/bin/servicio /etc/init.d/servicio
sudo rc-update add servicio default
sudo rc-update add servicio default
Windows
Para instalarlo en Windows basta con  ejecutar el archivo InstallServicio-NT.bat. Puedes verificar que fue  instalado correctamente accediendo a la consola de administración de  servicios de NT (ejecutas el comando services.msc):