作者:whuige (whuige at gmail.com) 日期:2008-1-11
前言
很多人都希望将自己的Java应用程序制作成为Windows的服务程序,成为windows服务的一个好处是,系统或者其他的程序可以通过Windows的服务管理,start或者stop我们的程序,也能探测到我们程序的运行状态,以便采取必要的策略。在系统重新启动的时候自动启动我们的服务可以避免手动启动的麻烦; 或者在正规的工程中,作为Windows(虽然大家觉得Windows的操作系统不是很稳定,但是目前还有部分非关键业务是运行在Windows版本的服务器上面的,包括电信行业机房) HA的应用程序,需要支持cluster的切换,这些,都需要将我们的程序制作成为Windows的服务。
在本篇中,我们将利用Apache的Procrun开源程序将我们的应用程序制作成为Windows的应用程序,主要是参考Tomcat,具有通用性,可以用在您以后的程序发布过程中。以后我会介绍*nix的成为系统启动的方法。
准备工作
一. 规划好你的程序目录结构
在常用的Java Application中,通常有如下性质的文件。
l 我们程序需要用到的jar文件,包括我们自己的java库或者一些第三方jar文件。
l 一些本地动态链接库,供我们java程序jni调用,比如加解密,或者第三方提供的动态链接库等等。
l 日志文件. 系统运行的错误信息,控制台捕获的消息。
l 配置文件. 我们系统需要的配置信息文件。
l 其他特性文件。 一些Java Application用到的非常规性文件。
因此,我们建立目录如下:
Bin: 存放我们的程序启动脚本,本地动态链接库
Lib: 存放所有的jar文件,一些配置文件也需要放在该目录下
Logs:服务启动日志,控制台打印输出。
Temp: 临时文件夹
还有一些其他的文件夹, 根据需要自行增加。
二. 下载相应相应的程序并编译
a) Procrun, 下载地址:
http://commons.apache.org/downloads/download_daemon.cgi
b) 编译, 用cygwin或者微软的开发工具编译成动态连接库,我将他编译为servicer.exe, 放在前面准备好的目录结构的bin目录下,当然, 你可以编译成你自己的名字。
三. 准备Bootstrap
Bootstrap是用来定制我们的ClassLoader和调用我们的程序。我们的bootstrap比较简单,简单的只有二个源文件
J, 根据此原理,你可以制作更复杂的Bootstrap,实现更多的功能。
由于程序比较长,完整的程序将附加在后面, 主要描述主要的函数。
YankeeClassLoader.java
/**
*
常用的
Classloader
,根据制定的目录,装载该目录下面的所有的
jar
和
zip
文件到
classloader
中
*
@param
parent
上一级
Classloader
*
@param
libDir
lib
目录
*
@throws
MalformedURLException
*/
YankeeClassLoader(ClassLoader parent, File libDir)
throws
MalformedURLException;
|
Bootstrap.java
/**
*
Bootstrap
的入口方法,
也是本地动态链接库调用的入口方法
*
*
@param
args
*
动态链接库传入的启动参数。
*/
public
static
void
main(String args[]);
/**
*
开始服务,
该方法有
procrun
调用
*
@throws
Exception
*/
public
void
start()
throws
Exception ;
/*
*
主线程一直循环,
否则整个服务启动以后将会退出。
*/
public
void
await() ;
/*
*
停止我们的服务
*/
public
void
stop()
throws
Exception ;
|
将上述程序编译,打包成为bootstrap.jar,放在前面建立好的bin目录下。
四.准备批处理
批处理的作用是设置我们的程序参数,调用我们的编译的servicer.exe, 将我们的Java
Application 部署到Windows的服务中去。我的保存文件名称保存为service.bat,你可以从附件里面直接拷贝过去,放在上面准备的目录bin中,是不是放了很多个文件到bin中去了?对的, 到现在为止,一共放了3个文件
测试程序
对所有使用该程序的入口程序,必须自己提供start和stop方法,start方法开始启动程序,stop方式停止程序,并且释放所有的资源。
我们的测试程序用来检验是否能正常的系统的服务启动。我们的测试程序开一个新的线程,不停的打印数据, 为了增加复杂程度,在测试程序里面还会打开一个RMI的服务端口。
public
void
start() {
if
(
started
) {
System.
out
.println(
"already started, abort"
);
return
;
}
System.
out
.println(
"i am start"
);
try
{
impl
=
new
TestInterfaceImpl();
TestInterface interface1 = (TestInterface) UnicastRemoteObject
.exportObject(
impl
, 0);
LocateRegistry.createRegistry(6666);
LocateRegistry.getRegistry(6666).rebind(
"test"
, interface1);
System.
out
.println(
"bind successfully"
);
started
=
true
;
testThread
=
new
TestThread();
new
Thread(
testThread
).start();
System.
out
.println(
"i am main..."
);
}
catch
(IOException e) {
//
TODO
Auto-generated catch block
e.printStackTrace();
}
}
|
public
void
stop() {
if
(!
started
) {
System.
out
.println(
"not started, abort"
);
return
;
}
try
{
UnicastRemoteObject.unexportObject(
impl
,
true
);
started
=
false
;
}
catch
(NoSuchObjectException e) {
//
TODO
Auto-generated catch block
e.printStackTrace();
}
System.
out
.println(
"stop rmi successfully..."
);
testThread
.setRunning(
false
);
System.
out
.println(
"stop test thread successfully..."
);
System.
out
.println(
"I am stoop"
);
}
|
Ok, 开始测试了, 将测试程序打包成test.jar,并放置在我们的lib目录下,进入bin目录,运行service.bat, 按照提示输入,安装该服务程序:
“开始”--》”运行”, 输入”services.msc”, 打开服务管理器,看看我们安装的服务,启动它,过一段时间关闭,然后进入我们的logs目录,看看我们的日志文件,是否有程序运行的日志?如果一切正常,那么恭喜你,我们的第一个服务程序安装完毕,并且以后的application不需要重新制作该包,只需要将以后的程序包拷贝到相应的目录,就可以复用了。
当然,你也可以直接下载我的安装包,将您的程序放置在相应的为止,直接安装服务。
整个工程的源程序
下载地址