Java Service Wrapper将java程序设置为服务

时间:2023-03-09 19:32:55
Java Service Wrapper将java程序设置为服务

  有时候我们希望我们java写的程序作为服务注册到系统中,Java Service Wrapper(下面简称wrapper)是目前较为流行的将Java程序部署成Windows服务的解决方案, 本文将讨论如何使用wrapper把我们的程序打包成WIN服务! 

主要作用有:

  1.打包服务

  2.设置JVM参数

  3.所有的日志可以输出到指定文件

0.准备需要注册为服务的程序

public class MapTest {
private static int i; public static void main(String[] args) {
while (true) {
try {
System.out.println("访问次数:" + i++);
HttpUtil.doGet("http://www.cnblogs.com/qlqwjy/");
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} }

上面程序依赖的jar包:

Java Service Wrapper将java程序设置为服务

将上面程序也打成包:(使用eclipse打包或者直接Jdk自带的jar打包)

  Java Service Wrapper将java程序设置为服务

1.下载serviceWrapper包

  下载地址:http://qiaoliqiang.cn/fileDown/wrapper-windows-x86-32-3.5.25.zip

下载后是一个压缩包,解压目录如下:

Java Service Wrapper将java程序设置为服务

2.开始注册一个简单的服务:

1. 准备一个目录,例如我在桌面建了一个SW目录,并在里面新建如下结构的目录:   接下来全文的%EXAMPLE_HOME%  就是我新建的SW目录名称

%EXAMPLE_HOME%\
%EXAMPLE_HOME%\bin\
%EXAMPLE_HOME%\conf\
%EXAMPLE_HOME%\lang\
%EXAMPLE_HOME%\lib\
%EXAMPLE_HOME%\mylib\
%EXAMPLE_HOME%\logs\

如下:lang目录是存放支持其他语言的语言包,一般用不到

Java Service Wrapper将java程序设置为服务

2.   然后将我们下载的wrapper目录下的文件拷贝到我们上面建的目录:

%WRAPPER_HOME%\bin\wrapper.exe -> %EXAMPLE_HOME%\bin\wrapper.exe
%WRAPPER_HOME%\lib\wrapper.jar -> %EXAMPLE_HOME%\lib\wrapper.jar
%WRAPPER_HOME%\lib\wrapper.dll -> %EXAMPLE_HOME%\lib\wrapper.dll
%WRAPPER_HOME%\conf\wrapper.conf -> %EXAMPLE_HOME%\conf\wrapper.conf

将自己程序打成的包以及自己程序依赖的包放到mylib:

Java Service Wrapper将java程序设置为服务

3.修改配置文件    %EXAMPLE_HOME%\conf\wrapper.conf

#java.exe所在位置
wrapper.java.command=C:\Program Files\Java\jdk1.7.0_80\bin\java.exe
#日志级别
wrapper.java.command.loglevel=INFO

#主类入口,第一个mainClass是固定写法,是wrapper自带的,不可以写成自己的,如果写成自己的入口程序自己的程序需要实现wrapper的WrapperListener接口
#parameter.1是自己的主程序入口所在类(从包名开始)
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
wrapper.app.parameter.1=MapTest

#依赖的包,第一个是wrapper包,第二个是自己打的包以及程序依赖包
wrapper.java.classpath.1=../lib/wrapper.jar
wrapper.java.classpath.2=../mylib/*.jar

#固定写法,依赖的wrapper的包
wrapper.java.library.path.1=../lib

#日志文件位置
wrapper.logfile=../logs/wrapper.log

#服务名称以及描述信息
wrapper.console.title=Hello World Server
wrapper.name=helloworldserver
wrapper.displayname=Hello World Server
wrapper.description=Hello World Server

注意:

  (1)上面的主类实际是:org.tanukisoftware.wrapper.WrapperSimpleApp,此类实现了WrapperListener接口。所以如果我们用此参数定义自己的主函数需要实现此接口。实现此接口就不用wrapper.app.parameter.1作为函数参数了

  (2)wrapper.app.parameter.1=MapTest  是将Maptest作为参数传入到主函数中,也就是依次作为类WrapperSimpleApp的main(String[] args)函数的参数。源码如下:

  (3)不能一次传多个参数,生效的始终是第一个参数,传第二个参数也不会生效。传第二个参数或者多个参数是作为MapTest的主函数的参数。
package org.tanukisoftware.wrapper;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.tanukisoftware.wrapper.WrapperListener;
import org.tanukisoftware.wrapper.WrapperManager;
import org.tanukisoftware.wrapper.WrapperPrintStream;
import org.tanukisoftware.wrapper.WrapperSystemPropertyUtil; public class WrapperSimpleApp implements WrapperListener, Runnable {
private static WrapperPrintStream m_outInfo;
private static WrapperPrintStream m_outError;
private static WrapperPrintStream m_outDebug;
private Method m_mainMethod;
private String[] m_appArgs;
private boolean m_mainStarted;
private boolean m_mainComplete;
private Integer m_mainExitCode;
private boolean m_ignoreMainExceptions;
private boolean m_startComplete; protected WrapperSimpleApp(String[] args) {
if (class$org$tanukisoftware$wrapper$WrapperManager == null) {
class$org$tanukisoftware$wrapper$WrapperManager = class$("org.tanukisoftware.wrapper.WrapperManager");
} else {
Class arg9999 = class$org$tanukisoftware$wrapper$WrapperManager;
} this.m_mainMethod = null;
m_outInfo = new WrapperPrintStream(System.out, "WrapperSimpleApp: ");
m_outError = new WrapperPrintStream(System.out, "WrapperSimpleApp Error: ");
m_outDebug = new WrapperPrintStream(System.out, "WrapperSimpleApp Debug: ");
if (args.length < 1) {
this.showUsage();
WrapperManager.stop(1);
} else {
String mainClassString = args[0];
String mainMethodString = "main";
String[] ar = args[0].split("/");
if (ar.length > 1) {
mainClassString = ar[0];
mainMethodString = ar[1];
} Class mainClass;
try {
mainClass = Class.forName(mainClassString);
} catch (ClassNotFoundException arg11) {
m_outError.println(WrapperManager.getRes().getString("Unable to locate the class {0} : {1}",
mainClassString, arg11));
this.showUsage();
WrapperManager.stop(1);
return;
} catch (ExceptionInInitializerError arg12) {
m_outError.println(WrapperManager.getRes()
.getString("Class {0} found but could not be initialized due to:", mainClassString));
arg12.printStackTrace(m_outError);
WrapperManager.stop(1);
return;
} catch (LinkageError arg13) {
m_outError.println(WrapperManager.getRes()
.getString("Class {0} found but could not be initialized: {1}", mainClassString, arg13));
WrapperManager.stop(1);
return;
} try {
this.m_mainMethod = mainClass.getMethod(mainMethodString, new Class[]{String[].class});
} catch (NoSuchMethodException arg9) {
try {
this.m_mainMethod = mainClass.getMethod(mainMethodString, new Class[0]);
} catch (NoSuchMethodException arg8) {
;
} if (this.m_mainMethod == null) {
m_outError.println(WrapperManager.getRes().getString(
"Unable to locate a public static {2} method in class {0} : {1}", mainClassString, arg9,
mainMethodString));
this.showUsage();
WrapperManager.stop(1);
return;
}
} catch (SecurityException arg10) {
m_outError.println(WrapperManager.getRes().getString(
"Unable to locate a public static {2} method in class {0} : {1}", mainClassString, arg10,
mainMethodString));
this.showUsage();
WrapperManager.stop(1);
return;
} int modifiers = this.m_mainMethod.getModifiers();
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) {
String[] appArgs = new String[args.length - 1];
System.arraycopy(args, 1, appArgs, 0, appArgs.length);
WrapperManager.start(this, appArgs);
} else {
m_outError.println(WrapperManager.getRes().getString(
"The {1} method in class {0} must be declared public and static.", mainClassString,
mainMethodString));
this.showUsage();
WrapperManager.stop(1);
}
}
} public void run() {
synchronized (this) {
this.m_mainStarted = true;
this.notifyAll();
} Object t = null; try {
if (WrapperManager.isDebugEnabled()) {
m_outDebug.println(WrapperManager.getRes().getString("invoking main method"));
} try {
this.m_mainMethod.invoke((Object) null, new Object[]{this.m_appArgs});
} catch (IllegalArgumentException arg15) {
this.m_mainMethod.invoke((Object) null, new Object[0]);
} finally {
Thread.currentThread().setPriority(10);
} if (WrapperManager.isDebugEnabled()) {
m_outDebug.println(WrapperManager.getRes().getString("main method completed"));
} synchronized (this) {
this.m_mainComplete = true;
this.notifyAll();
return;
}
} catch (IllegalAccessException arg18) {
t = arg18;
} catch (IllegalArgumentException arg19) {
t = arg19;
} catch (InvocationTargetException arg20) {
t = arg20.getTargetException();
if (t == null) {
t = arg20;
}
} m_outInfo.println();
m_outError.println(WrapperManager.getRes().getString("Encountered an error running main:"));
((Throwable) t).printStackTrace(m_outError);
synchronized (this) {
if (this.m_ignoreMainExceptions) {
if (!this.m_startComplete) {
this.m_mainComplete = true;
this.notifyAll();
} } else if (this.m_startComplete) {
WrapperManager.stop(1);
} else {
this.m_mainComplete = true;
this.m_mainExitCode = new Integer(1);
this.notifyAll();
}
}
} public Integer start(String[] args) {
boolean waitForStartMain = WrapperSystemPropertyUtil
.getBooleanProperty(WrapperSimpleApp.class.getName() + ".waitForStartMain", false);
this.m_ignoreMainExceptions = WrapperSystemPropertyUtil
.getBooleanProperty(WrapperSimpleApp.class.getName() + ".ignoreMainExceptions", false);
int maxStartMainWait = WrapperSystemPropertyUtil
.getIntProperty(WrapperSimpleApp.class.getName() + ".maxStartMainWait", 2);
maxStartMainWait = Math.max(1, maxStartMainWait);
int maxLoops;
if (waitForStartMain) {
maxLoops = Integer.MAX_VALUE;
if (WrapperManager.isDebugEnabled()) {
m_outDebug.println(WrapperManager.getRes()
.getString("start(args) Will wait indefinitely for the main method to complete."));
}
} else {
maxLoops = maxStartMainWait;
if (WrapperManager.isDebugEnabled()) {
m_outDebug.println(WrapperManager.getRes().getString(
"start(args) Will wait up to {0} seconds for the main method to complete.",
new Integer(maxStartMainWait)));
}
} Thread mainThread = new Thread(this, "WrapperSimpleAppMain");
synchronized (this) {
this.m_appArgs = args;
mainThread.start();
Thread.currentThread().setPriority(10); while (!this.m_mainStarted) {
try {
this.wait(1000L);
} catch (InterruptedException arg10) {
;
}
} for (int loops = 0; loops < maxLoops && !this.m_mainComplete; ++loops) {
try {
this.wait(1000L);
} catch (InterruptedException arg9) {
;
} if (!this.m_mainComplete) {
WrapperManager.signalStarting(5000);
}
} this.m_startComplete = true;
if (WrapperManager.isDebugEnabled()) {
m_outDebug
.println(WrapperManager.getRes().getString("start(args) end. Main Completed={0}, exitCode={1}",
new Boolean(this.m_mainComplete), this.m_mainExitCode));
} return this.m_mainExitCode;
}
} public int stop(int exitCode) {
if (WrapperManager.isDebugEnabled()) {
m_outDebug.println(WrapperManager.getRes().getString("stop({0})", new Integer(exitCode)));
} return exitCode;
} public void controlEvent(int event) {
if (event != 202 || !WrapperManager.isLaunchedAsService() && !WrapperManager.isIgnoreUserLogoffs()) {
if (WrapperManager.isDebugEnabled()) {
m_outDebug.println(WrapperManager.getRes().getString("controlEvent({0}) Stopping", new Integer(event)));
} WrapperManager.stop(0);
} else {
m_outInfo.println(WrapperManager.getRes().getString("User logged out. Ignored."));
} } protected void showUsage() {
System.out.println();
System.out.println(WrapperManager.getRes().getString("WrapperSimpleApp Usage:"));
System.out.println(WrapperManager.getRes().getString(
" java org.tanukisoftware.wrapper.WrapperSimpleApp {app_class{/app_method}} [app_arguments]"));
System.out.println();
System.out.println(WrapperManager.getRes().getString("Where:"));
System.out.println(WrapperManager.getRes()
.getString(" app_class: The fully qualified class name of the application to run."));
System.out.println(WrapperManager.getRes()
.getString(" app_arguments: The arguments that would normally be passed to the"));
System.out.println(WrapperManager.getRes().getString(" application."));
} public static void main(String[] args) {
new WrapperSimpleApp(args);
}
}
 

4.开始注册服务以及测试

(1)控制台测试

C:\Users\Administrator>cd C:\Users\Administrator\Desktop\SW

C:\Users\Administrator\Desktop\SW>bin\wrapper.exe -c ..\conf\wrapper.conf
wrapper | --> Wrapper Started as Console
wrapper | Java Service Wrapper Community Edition -bit 3.5.
wrapper | Copyright (C) - Tanuki Software, Ltd. All Rights Reserved.
wrapper | http://wrapper.tanukisoftware.com
wrapper |
wrapper | Launching a JVM...
wrapper | Java Command Line:
wrapper | Command: "C:\Program Files\Java\jdk1.7.0_80\bin\java.exe" -Djava.library.path="../lib" -classpath "../lib/wrapper.jar;../mylib/commons-logging-1.0.4.jar;../mylib/commons.jar;../mylib/httpclient-4.3.1.jar;../mylib/httpcore-4.3.jar;../mylib/httpmime-4.3.1.jar;../mylib/log4j-1.2.12.jar" -Dwrapper.key="2azws1iyaXYR9r26" -Dwrapper.port= -Dwrapper.jvm.port.min= -Dwrapper.jvm.port.max= -Dwrapper.pid= -Dwrapper.version="3.5.25" -Dwrapper.native_library="wrapper" -Dwrapper.arch="x86" -Dwrapper.cpu.timeout="" -Dwrapper.jvmid= org.tanukisoftware.wrapper.WrapperSimpleApp MapTest
jvm | WrapperManager: Initializing...
jvm | WrapperManager:
jvm | WrapperManager: WARNING - Unable to load the Wrapper''s native library 'wrapper.dll'.
jvm | WrapperManager: The file is located on the path at the following location but
jvm | WrapperManager: could not be loaded:
jvm | WrapperManager: C:\Users\Administrator\Desktop\SW\bin\..\lib\wrapper.dll
jvm | WrapperManager: Please verify that the file is both readable and executable by the
jvm | WrapperManager: current user and that the file has not been corrupted in any way.
jvm | WrapperManager: One common cause of this problem is running a -bit version
jvm | WrapperManager: of the Wrapper with a -bit version of Java, or vica versa.
jvm | WrapperManager: This is a -bit JVM.
jvm | WrapperManager: Reported cause:
jvm | WrapperManager: C:\Users\Administrator\Desktop\SW\lib\wrapper.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
jvm | WrapperManager: System signals will not be handled correctly.
jvm | WrapperManager:
jvm | 访问次数:
jvm | log4j:WARN No appenders could be found for logger (org.apache.http.impl.conn.BasicClientConnectionManager).
jvm | log4j:WARN Please initialize the log4j system properly.
jvm | 访问次数:
jvm | 访问次数:
jvm | 访问次数:

(2)将程序注册为服务:

bin\wrapper.exe -i ..\conf\wrapper.conf

结果:

wrapper  | Hello World Server service installed.

Java Service Wrapper将java程序设置为服务

(3)启动服务:

net start helloworld

或者:

bin\wrapper.exe -t ..\conf\wrapper.conf

启动之后我们回看到程序输出在logs\wrapper.log文件内:

Java Service Wrapper将java程序设置为服务

(4)停止服务

net stop helloword

或者

bin\wrapper.exe -p ..\conf\wrapper.conf

(5)删除服务

sc delete helloword

或者

bin\wrapper.exe -r ..\conf\wrapper.conf

-----------上面是在系统有Java环境的情况下的设置,现在假设我们不存在Java运行环境,也就是没有JRE与JDK:-------------

(1)拷贝java安装目录下的JRE(包含bin目录和相关lib)目录到上面的目录%EXAMPLE_HOME%,如下:

Java Service Wrapper将java程序设置为服务

jre目录下:

Java Service Wrapper将java程序设置为服务

(2)修改配置文件,利用我们上面的jre目录下的jar包和bin\java.exe

#java.exe所在位置
wrapper.java.command=../jre/bin/java.exe
#日志级别
wrapper.java.command.loglevel=INFO #主类入口,第一个mainClass是固定写法,是wrapper自带的,不可以写成自己的,如果写成自己的入口程序自己的程序需要实现wrapper的WrapperListener接口
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
#parameter.1是自己的主程序入口所在类(从包名开始)
wrapper.app.parameter.1=MapTest #依赖的包,第一个是wrapper包,第二个是自己打的包以及程序依赖包
wrapper.java.classpath.1=../jre/lib/*.jar
wrapper.java.classpath.2=../lib/wrapper.jar
wrapper.java.classpath.3=../mylib/*.jar #固定写法,依赖的wrapper的包
wrapper.java.library.path.1=../lib #日志文件位置
wrapper.logfile=../logs/wrapper.log #服务名称以及描述信息
wrapper.console.title=Hello World Server
wrapper.name=helloworldserver
wrapper.displayname=Hello World Server
wrapper.description=Hello World Server

(3)安装服务与测试与上面一样:

C:\Users\liqiang\Desktop\xxx\SW>bin\wrapper.exe -c ..\conf\wrapper.conf
wrapper | --> Wrapper Started as Console
wrapper | Java Service Wrapper Community Edition 32-bit 3.5.25
wrapper | Copyright (C) 1999-2014 Tanuki Software, Ltd. All Rights Reserved.
wrapper | http://wrapper.tanukisoftware.com
wrapper |
wrapper | Launching a JVM...
wrapper | Java Command Line:
wrapper | Command: "..\jre\bin\java.exe" -Djava.library.path="../lib" -classp
arsets.jar;../jre/lib/deploy.jar;../jre/lib/javaws.jar;../jre/lib/jce.jar;../jre
/lib/jsse.jar;../jre/lib/management-agent.jar;../jre/lib/plugin.jar;../jre/lib/r
apper.jar;../mylib/commons-logging-1.0.4.jar;../mylib/commons.jar;../mylib/httpc
r;../mylib/httpmime-4.3.1.jar;../mylib/log4j-1.2.12.jar" -Dwrapper.key="408rjGp1
.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.pid=336144 -Dwrapper.
y="wrapper" -Dwrapper.arch="x86" -Dwrapper.cpu.timeout="10" -Dwrapper.jvmid=1 or
p MapTest
jvm 1 | WrapperManager: Initializing...
jvm 1 | WrapperManager:
jvm 1 | WrapperManager: WARNING - Unable to load the Wrapper''s native librar
jvm 1 | WrapperManager: The file is located on the path at the foll
jvm 1 | WrapperManager: could not be loaded:
jvm 1 | WrapperManager: C:\Users\liqiang\Desktop\xxx\SW\bin\..\li
jvm 1 | WrapperManager: Please verify that the file is both readabl
jvm 1 | WrapperManager: current user and that the file has not been
jvm 1 | WrapperManager: One common cause of this problem is running
jvm 1 | WrapperManager: of the Wrapper with a 64-bit version of Jav
jvm 1 | WrapperManager: This is a 64-bit JVM.
jvm 1 | WrapperManager: Reported cause:
jvm 1 | WrapperManager: C:\Users\liqiang\Desktop\xxx\SW\lib\wrapp
MD 64-bit platform
jvm 1 | WrapperManager: System signals will not be handled correctl
jvm 1 | WrapperManager:
jvm 1 | 访问次数:0
jvm 1 | log4j:WARN No appenders could be found for logger (org.apache.http.im
jvm 1 | log4j:WARN Please initialize the log4j system properly.
jvm 1 | 访问次数:1
wrapper | CTRL-C trapped. Shutting down.
wrapper | <-- Wrapper Stopped C:\Users\liqiang\Desktop\xxx\SW>bin\wrapper.exe -i ..\conf\wrapper.conf
wrapperm | Hello World Server service installed. C:\Users\liqiang\Desktop\xxx\SW>java
'java' 不是内部或外部命令,也不是可运行的程序
或批处理文件。 C:\Users\liqiang\Desktop\xxx\SW>javac
'javac' 不是内部或外部命令,也不是可运行的程序
或批处理文件。

  为了验证我们的服务是使用的jre目录下的JDK,我们可以将jre目录删掉进行测试,或者是将配置文件中jre\lib的引入注释掉进行测试。

  现在我们将jre目录删掉,启动服务报错如下:

Java Service Wrapper将java程序设置为服务

  在wrapper.log中查看到的日志文件如下:

Java Service Wrapper将java程序设置为服务

 

  至此我们实现了简单的有JRE与无JRE两种情况的注册服务,实际没有JRE环境的时候我们只需要将我们的JRE附到目录中并且在wrapper.conf中指明所在路径即可。

补充1.:上面配置会导致JVM不断重启,需要加JVM参数以及设置,同时设置服务开机启动:并且使用一变量记住我们的项目路径;修改wrapper.conf并且删掉服务重新添加:

#定义了一个根路径,注意set和.之间没有空格,下面就可以用%basePath%取此变量
set.basePath=C:\Users\liqiang\Desktop\xxx\SW #java.exe所在位置
wrapper.java.command=%basePath%/jre/bin/java.exe
#日志级别
wrapper.java.command.loglevel=INFO #主类入口,第一个mainClass是固定写法,是wrapper自带的,不可以写成自己的,如果写成自己的入口程序自己的程序需要实现wrapper的WrapperListener接口
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
#parameter.1是自己的主程序入口所在类(从包名开始)
wrapper.app.parameter.1=MapTest #依赖的包,第一个是wrapper包,第二个是自己打的包以及程序依赖包
wrapper.java.classpath.1=../jre/lib/*.jar
wrapper.java.classpath.2=../lib/wrapper.jar
wrapper.java.classpath.3=../mylib/*.jar #固定写法,依赖的wrapper的包
wrapper.java.library.path.1=../lib #日志文件位置
wrapper.logfile=../logs/wrapper.log #服务名称以及描述信息
wrapper.console.title=Hello World Server
wrapper.name=helloworldserver
wrapper.displayname=Hello World Server
wrapper.description=Hello World Server wrapper.jmx=false
wrapper.on_exit.0=SHUTDOWN
wrapper.on_exit.default=RESTART wrapper.ntservice.interactive = true
#服务开机启动
wrapper.ntservice.starttype=AUTO_START
wrapper.tray = true wrapper.java.monitor.deadlock = true
wrapper.java.monitor.heap = true
wrapper.java.monitor.gc.restart = true # Java Heap 初始化大小(单位:MB)
wrapper.java.initmemory=128
# Java Heap 最大值(单位:MB)
wrapper.java.maxmemory=128

删除重装服务:

C:\Users\liqiang\Desktop\xxx\SW>bin\wrapper.exe -r ..\conf\wrapper.conf
wrapperm | Hello World Server service removed.
C:\Users\liqiang\Desktop\xxx\SW>bin\wrapper.exe -c ..\conf\wrapper.conf
wrapper | --> Wrapper Started as Console
wrapper | Java Service Wrapper Community Edition -bit 3.5.
wrapper | Copyright (C) - Tanuki Software, Ltd. All Rights Reserved.
wrapper | http://wrapper.tanukisoftware.com
wrapper |
wrapper | Launching a JVM...
wrapper | Java Command Line:
wrapper | Command: "C:\Users\liqiang\Desktop\xxx\SW\jre\bin\java.exe" -Xms128
-classpath "../jre/lib/alt-rt.jar;../jre/lib/charsets.jar;../jre/lib/deploy.jar;
;../jre/lib/jfr.jar;../jre/lib/jfxrt.jar;../jre/lib/jsse.jar;../jre/lib/manageme
e/lib/resources.jar;../jre/lib/rt.jar;../lib/wrapper.jar;../mylib/commons-loggin
b/httpclient-4.3..jar;../mylib/httpcore-4.3.jar;../mylib/httpmime-4.3..jar;../
P7sqJIaMu25Avke" -Dwrapper.port=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvm
rapper.version="3.5.25" -Dwrapper.native_library="wrapper" -Dwrapper.arch="x86"
id= org.tanukisoftware.wrapper.WrapperSimpleApp MapTest
jvm | WrapperManager: Initializing...
jvm | WrapperManager:
jvm | WrapperManager: WARNING - Unable to load the Wrapper''s native librar
jvm | WrapperManager: The file is located on the path at the foll
jvm | WrapperManager: could not be loaded:
jvm | WrapperManager: C:\Users\liqiang\Desktop\xxx\SW\bin\..\li
jvm | WrapperManager: Please verify that the file is both readabl
jvm | WrapperManager: current user and that the file has not been
jvm | WrapperManager: One common cause of this problem is running
jvm | WrapperManager: of the Wrapper with a -bit version of Jav
jvm | WrapperManager: This is a -bit JVM.
jvm | WrapperManager: Reported cause:
jvm | WrapperManager: C:\Users\liqiang\Desktop\xxx\SW\lib\wrapp
MD -bit platform
jvm | WrapperManager: System signals will not be handled correctl
jvm | WrapperManager:
jvm | 访问次数:
wrapper | CTRL-C trapped. Shutting down.
jvm | log4j:WARN No appenders could be found for logger (org.apache.http.im
jvm | log4j:WARN Please initialize the log4j system properly.
wrapper | <-- Wrapper Stopped C:\Users\liqiang\Desktop\xxx\SW>bin\wrapper.exe -i ..\conf\wrapper.conf
wrapperm | Hello World Server service installed.

如果我们想检测JVM参数可以用jps+jmap监测即可:

C:\Users\liqiang>jps
505668 Jps
504320 WrapperSimpleApp
487768 C:\Users\liqiang>jmap -heap 504320
Attaching to process ID 504320, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.80-b11 using thread-local object allocation.
Parallel GC with 4 thread(s) Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 134217728 (128.0MB)
NewSize = 1310720 (1.25MB)
MaxNewSize = 17592186044415 MB
OldSize = 5439488 (5.1875MB)
NewRatio = 2
SurvivorRatio = 8
PermSize = 21757952 (20.75MB)
MaxPermSize = 85983232 (82.0MB)
G1HeapRegionSize = 0 (0.0MB) Heap Usage:
PS Young Generation
Eden Space:
capacity = 34603008 (33.0MB)
used = 9710016 (9.26019287109375MB)
free = 24892992 (23.73980712890625MB)
28.06119051846591% used
From Space:
capacity = 5242880 (5.0MB)
used = 0 (0.0MB)
free = 5242880 (5.0MB)
0.0% used
To Space:
capacity = 5242880 (5.0MB)
used = 0 (0.0MB)
free = 5242880 (5.0MB)
0.0% used
PS Old Generation
capacity = 89653248 (85.5MB)
used = 0 (0.0MB)
free = 89653248 (85.5MB)
0.0% used
PS Perm Generation
capacity = 22020096 (21.0MB)
used = 6630336 (6.32318115234375MB)
free = 15389760 (14.67681884765625MB)
30.110386439732142% used 3336 interned Strings occupying 269976 bytes.

补充2:我们可以用上面的service wrapper的bin包中的bat文件进行安装服务: (将上面安下载解压后的wrapper-windows-x86-32-3.5.25\bin中的文件复制到我们的%EXAMPLE_HOME%\bin目录下)

Java Service Wrapper将java程序设置为服务

  我们只需要点击上面的bat文件即可实现上面的操作。

*************************

我已经将我的一个例子上传到我的服务器,下载地址:http://qiaoliqiang.cn/fileDown/serviceWrapperExample.zip

下载解压之后只需要将自己程序打的包以及依赖包放到mylib目录下,修改conf\wrapper.conf文件中下面两处

set.basePath=C:\Users\liqiang\Desktop\xxx\SW   #替换为自己的目录
#parameter.1是自己的主程序入口所在类(从包名开始)
wrapper.app.parameter.1=MapTest #替换为自己的程序入口

然后点击bin目录的TestWrapper.bat测试即可

**************************

3.将log4j日志与servicewrapper日志进行整合

开发环境下一切正常。用JavaServiceWrapper部署到服务器上之后,发现log文件没有生成。同时在wrapper的log中有两行log4j的错误信息:

log4j:WARN No appenders could be found for logger (com.xxxxx).

log4j:WARN Please initialize the log4j system properly.

查找了一番,最后发现在wrapper.conf中加入一行,硬性指明log4j的配置文件就OK了:

 wrapper.java.additional.1=-Dlog4j.configuration=file:../conf/log4j.properties

*上有人回答类似的问题,用的方法是

wrapper.java.additional.1=-Dlog4j.configuration=../config/log4j.properties

但是我这样测试没成功。不知道是不是和版本有关。

(1) 测试代码:

package serviceWrapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class MapTest {
private static Logger log = LoggerFactory.getLogger(MapTest.class); public static void main(String[] args) {
int times = 0;
while (true) {
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
}
try {
int i = 1 / 0;
} catch (Exception e) {
log.error("error[{}] by / 0 ", times++, e);
}
}
} }

(2)测试代码打成包并一起复制到mylib目录下:

依赖的jar包以及自己打的包:

Java Service Wrapper将java程序设置为服务

(3)目录结构:

Java Service Wrapper将java程序设置为服务

(4)bin目录与上面一样,jre也一样,lang也一样,lib也一样,logs也一样,mylib在(2)中换过,将log4j.properties文件复制到conf目录下:

Java Service Wrapper将java程序设置为服务

log4j.properties文件:

log4j.rootLogger=info,A,B

log4j.appender.A=org.apache.log4j.ConsoleAppender
log4j.appender.A.layout=org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n log4j.appender.B=org.apache.log4j.RollingFileAppender
log4j.appender.B.File=E:\\test.log
log4j.appender.B.MaxFileSize=10MB
log4j.appender.B.MaxBackupIndex=5
log4j.appender.B.layout=org.apache.log4j.PatternLayout
log4j.appender.B.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n

(5)修改wrapper.conf文件:

#定义了一个根路径,注意set和.之间没有空格,下面就可以用%basePath%取此变量
set.basePath=C:\Users\liqiang\Desktop\logTest #java.exe所在位置
wrapper.java.command=%basePath%/jre/bin/java.exe #主类入口,第一个mainClass是固定写法,是wrapper自带的,不可以写成自己的,如果写成自己的入口程序自己的程序需要实现wrapper的WrapperListener接口
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
#parameter.1是自己的主程序入口所在类(从包名开始),生效的始终是第一个参数
wrapper.app.parameter.1=serviceWrapper.MapTest
wrapper.app.parameter.2=MapTest # Java Classpath配置,必须从序号"1"开始,添加新的jar包后序号递增
#JRE的包
wrapper.java.classpath.1=../jre/lib/*.jar
#wrapper.jar
wrapper.java.classpath.2=../lib/wrapper.jar
#自己的包以及程序依赖包
wrapper.java.classpath.3=../mylib/*.jar #固定写法,依赖的wrapper的包
wrapper.java.library.path.1=%basePath%/lib #日志文件位置
wrapper.logfile=../logs/wrapper.log
# 控制台信息输出格式
wrapper.console.format=PM
# 日志文件输出格式
wrapper.logfile.format=LPTM
# 日志文件日志级别
wrapper.logfile.loglevel=INFO #服务名称以及描述信息
wrapper.console.title=Hello World Server
wrapper.name=helloworldserver
wrapper.displayname=Hello World Server
wrapper.description=Hello World Server wrapper.jmx=false
wrapper.on_exit.0=SHUTDOWN
wrapper.on_exit.default=RESTART wrapper.ntservice.interactive = true
#服务开机启动
wrapper.ntservice.starttype=AUTO_START
wrapper.tray = true wrapper.java.monitor.deadlock = true
wrapper.java.monitor.heap = true
wrapper.java.monitor.gc.restart = true # Java Heap 初始化大小(单位:MB)
wrapper.java.initmemory=128
# Java Heap 最大值(单位:MB)
wrapper.java.maxmemory=128 # 32/64位选择,true为自动选择
wrapper.java.additional.auto_bits=TRUE #附加参数即为java命令可选参数,如下所示:
#设置log4J日志文件位置
wrapper.java.additional.1=-Dlog4j.configuration=file:../conf/log4j.properties

注意:  附加参数 wrapper.java.additional.1 即为java命令可选参数(可用于指定JVM参数与指定垃圾收集器组合),如下所示:

C:\Users\liqiang>java
用法: java [-options] class [args...]
(执行类)
或 java [-options] -jar jarfile [args...]
(执行 jar 文件)
其中选项包括:
-d32 使用 32 位数据模型 (如果可用)
-d64 使用 64 位数据模型 (如果可用)
-server 选择 "server" VM
-hotspot 是 "server" VM 的同义词 [已过时]
默认 VM 是 server. -cp <目录和 zip/jar 文件的类搜索路径>
-classpath <目录和 zip/jar 文件的类搜索路径>
用 ; 分隔的目录, JAR 档案
和 ZIP 档案列表, 用于搜索类文件。
-D<名称>=<值>
设置系统属性
-verbose:[class|gc|jni]
启用详细输出
-version 输出产品版本并退出
-version:<值>
需要指定的版本才能运行
-showversion 输出产品版本并继续
-jre-restrict-search | -no-jre-restrict-search
在版本搜索中包括/排除用户专用 JRE
-? -help 输出此帮助消息
-X 输出非标准选项的帮助
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
按指定的粒度启用断言
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
禁用具有指定粒度的断言
-esa | -enablesystemassertions
启用系统断言
-dsa | -disablesystemassertions
禁用系统断言
-agentlib:<libname>[=<选项>]
加载本机代理库 <libname>, 例如 -agentlib:hprof
另请参阅 -agentlib:jdwp=help 和 -agentlib:hprof=help
-agentpath:<pathname>[=<选项>]
按完整路径名加载本机代理库
-javaagent:<jarpath>[=<选项>]
加载 Java 编程语言代理, 请参阅 java.lang.instrument
-splash:<imagepath>
使用指定的图像显示启动屏幕
有关详细信息, 请参阅 http://www.oracle.com/technetwork/java/javase/documentation

(6)点击bin目录下的TestWrapper.bat测试控制台:

Java Service Wrapper将java程序设置为服务

logs\wrapper.log文件内容如下:

Java Service Wrapper将java程序设置为服务

E:\test.log文件内容如下:

Java Service Wrapper将java程序设置为服务

  log4j生成的日志文件与wrapper.logs文件的内容几乎一样,只是格式不一样。

  至此完成与log4j的整合,其实与log4j整合也简单,就是在配置文件下面加一行引入log4j.properties文件的位置

wrapper.java.additional.1=-Dlog4j.configuration=file:../conf/log4j.properties

4.将tomcat注册为服务:设置tomcat服务开机启动,还是在没有JRE的环境下进行:(公司集成部署系统可以采用这种方式)

1.新建一目录,解压一个全新的tomcat,在%TOMCAT%目录下新建一个servicewrapper目录:

Java Service Wrapper将java程序设置为服务

2.将自己的项目解压之后放在webapps目录下(这里采用目录解压部署,参考:https://www.cnblogs.com/qlqwjy/p/9478649.html)

Java Service Wrapper将java程序设置为服务

3.与上面部署一样将所需要的文件拷贝到servicewrapper目录下:

Java Service Wrapper将java程序设置为服务

  bin目录是启动服务的bat文件和wrapper.exe文件,conf是wrapper.conf文件,jre是拷贝的jre环境,lang是空目录,lib是wrapper.jar和wrapper.dll文件,logs用于存放wrapper.log文件,mylib可以存放自己以及第三方jar包(不过web项目用不到这个)。

4.修改wrapper.conf文件的配置:(重要)

#定义了一个根路径,注意set和.之间没有空格,下面就可以用%basePath%取此变量
set.basePath=C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.72 #java.exe所在位置
wrapper.java.command=%basePath%/servicewrapper/jre/bin/java.exe
#日志级别
wrapper.java.command.loglevel=INFO #依赖的包,第一个是wrapper包,第二个是自己打的包以及程序依赖包
#jre的lb路径
wrapper.java.classpath.1=%basePath%/servicewrapper/jre/lib/*.jar
#wrapper.jar路径
wrapper.java.classpath.2=%basePath%/servicewrapper/lib/wrapper.jar
#自己的依赖jar包
wrapper.java.classpath.3=%basePath%/servicewrapper/mylib/*.jar
#tomcat依赖的包(tomcat启动类在这个包中)
wrapper.java.classpath.4 = %basePath%/bin/bootstrap.jar
wrapper.java.classpath.5 = %basePath%/bin/tomcat-juli.jar #Wrapper集成主类。有4种集成方式,适合tomcat这样启动使用一个类,
#停止使用另一个类的应用的是WrapperStartStopApp类
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperStartStopApp #tomcat应用参数,无需修改(第一个参数是tomcat的启动类)
wrapper.app.parameter.1=org.apache.catalina.startup.Bootstrap
wrapper.app.parameter.2=1
wrapper.app.parameter.3=start
wrapper.app.parameter.4=org.apache.catalina.startup.Bootstrap
wrapper.app.parameter.5=TRUE
wrapper.app.parameter.6=1
wrapper.app.parameter.7=stop wrapper.working.dir = %basePath%/bin #固定写法,依赖的wrapper的包
wrapper.java.library.path.1=../lib #wrapper.log日志文件位置
wrapper.logfile=%basePath%/servicewrapper/logs/wrapper.log #服务名称以及描述信息
wrapper.console.title=Exam Server
wrapper.name=examservice
wrapper.displayname=Exam Server
wrapper.description=Exam Server #Tomcat的固定参数(一般不用修改)
wrapper.java.additional.1=-Djava.util.logging.config.file=%basePath%/conf/logging.properties
wrapper.java.additional.2 = -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
wrapper.java.additional.3 = -Djava.endorsed.dirs=%basePath%/endorsed -Dcatalina.base=%basePath% -Dcatalina.home=%basePath%
wrapper.java.additional.4 = -Djava.io.tmpdir=%basePath%/temp
wrapper.java.additional.5 = -Djava.net.preferIPv4Stack=true
wrapper.java.additional.6 = -XX:MaxNewSize=256m
wrapper.java.additional.7 = -XX:MaxPermSize=256m
wrapper.java.additional.8 = -XX:-UseGCOverheadLimit
wrapper.java.additional.9 = -Dcom.sun.management.jmxremote wrapper.jmx=false
wrapper.on_exit.0=SHUTDOWN
wrapper.on_exit.default=RESTART wrapper.ntservice.interactive = true
#服务开机启动
wrapper.ntservice.starttype=AUTO_START
wrapper.tray = true #监测JVM死锁
wrapper.java.monitor.deadlock = true
wrapper.java.monitor.heap = true
wrapper.java.monitor.gc.restart = true # Java Heap 初始化大小(单位:MB)
wrapper.java.initmemory=1024
# Java Heap 最大值(单位:MB)
wrapper.java.maxmemory=1024

5.注册为服务:

C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.\servicewrapper>bin\wrapper.exe -i ..\conf\wrapper.conf
wrapperm | Exam Server service installed. C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.\servicewrapper>sc qc examservice
[SC] QueryServiceConfig 成功 SERVICE_NAME: examservice
TYPE : WIN32_OWN_PROCESS (interactive)
START_TYPE : AUTO_START
ERROR_CONTROL : NORMAL
BINARY_PATH_NAME : C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.\servicewrapper\bin\wrapper.exe -s C
:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.\servicewrapper\conf\wrapper.conf wrapper.console.flush=true wrapp
er.internal.namedpipe=
LOAD_ORDER_GROUP :
TAG :
DISPLAY_NAME : Exam Server
DEPENDENCIES :
SERVICE_START_NAME : LocalSystem C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.\servicewrapper>

6.启动服务并进行日志查看:

Java Service Wrapper将java程序设置为服务

  由于项目中使用了log4j,所以现在可以从两个地方查看log日志。第一个是log4j指定的日志的输出的位置,第二个是wrapper.logs文件。

7.监测Tomcat的参数信息:

  可以jps+jmap进行查看(前提是安装JDK),如果没有安装JDK可以利用tomcat自带的manager项目进行监测(参考:https://www.cnblogs.com/qlqwjy/p/8037392.html)。如果需要对参数进行优化,只需要修改上面wrapper.conf文件即可。

  总结:

  上面还可以进行大量的优化,比如讲一些不用的文件删掉,将多个lib目录进行合并;

  至此完成了tomcat注册为web服务,项目中一般使用这种方式进行部署项目,而且mysql可以采用集成部署集成到系统中,到时候系统上线的时候只用一个压缩包传到服务器,避免大量的下载tomcat、mysql等以及大量的配置。我上个项目上线也确实是通过安装JDK、mysql、tomcat等进行部署安装的,采用这种集成的方式都不用安装JDK等软件,简化部署。

5.linux环境下的注册为服务

  参考:https://www.cnblogs.com/zifengli/archive/2015/11/30/5007568.html

附件:官网的一份配置:

#encoding=UTF-8
# Configuration files must begin with a line specifying the encoding
# of the the file. #********************************************************************
# Wrapper License Properties (Ignored by Community Edition)
#********************************************************************
# Professional and Standard Editions of the Wrapper require a valid
# License Key to start. Licenses can be purchased or a trial license
# requested on the following pages:
# http://wrapper.tanukisoftware.com/purchase
# http://wrapper.tanukisoftware.com/trial # Include file problems can be debugged by removing the first '#'
# from the following line:
##include.debug # The Wrapper will look for either of the following optional files for a
# valid License Key. License Key properties can optionally be included
# directly in this configuration file.
#include ../conf/wrapper-license.conf
#include ../conf/wrapper-license-%WRAPPER_HOST_NAME%.conf # The following property will output information about which License Key(s)
# are being found, and can aid in resolving any licensing problems.
#wrapper.license.debug=TRUE #********************************************************************
# Wrapper Localization
#********************************************************************
# Specify the locale which the Wrapper should use. By default the system
# locale is used.
#wrapper.lang=en_US # en_US or ja_JP # Specify the location of the Wrapper's language resources. If these are
# missing, the Wrapper will default to the en_US locale.
wrapper.lang.folder=../lang #********************************************************************
# Wrapper Java Properties
#********************************************************************
# Java Application
# Locate the java binary on the system PATH:
wrapper.java.command=java
# Specify a specific java binary:
#set.JAVA_HOME=/java/path
#wrapper.java.command=%JAVA_HOME%/bin/java # Tell the Wrapper to log the full generated Java command line.
#wrapper.java.command.loglevel=INFO # Java Main class. This class must implement the WrapperListener interface
# or guarantee that the WrapperManager class is initialized. Helper
# classes are provided to do this for you. See the Integration section
# of the documentation for details.
wrapper.java.mainclass=org.tanukisoftware.wrapper.demo.DemoApp # Java Classpath (include wrapper.jar) Add class path elements as
# needed starting from 1
wrapper.java.classpath.1=../lib/wrapperdemo.jar
wrapper.java.classpath.2=../lib/wrapper.jar # Java Library Path (location of Wrapper.DLL or libwrapper.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 # Java Additional Parameters
wrapper.java.additional.1= # Initial Java Heap Size (in MB)
#wrapper.java.initmemory=3 # Maximum Java Heap Size (in MB)
#wrapper.java.maxmemory=64 # Application parameters. Add parameters as needed starting from 1
wrapper.app.parameter.1= #********************************************************************
# Wrapper Logging Properties
#********************************************************************
# Enables Debug output from the Wrapper.
# wrapper.debug=TRUE # Format of output for the console. (See docs for formats)
wrapper.console.format=PM # Log Level for console output. (See docs for log levels)
wrapper.console.loglevel=INFO # Log file to use for wrapper output logging.
wrapper.logfile=../logs/wrapper.log # Format of output for the log file. (See docs for formats)
wrapper.logfile.format=LPTM # Log Level for log file output. (See docs for log levels)
wrapper.logfile.loglevel=INFO # Maximum size that the log file will be allowed to grow to before
# the log is rolled. Size is specified in bytes. The default value
# of 0, disables log rolling. May abbreviate with the 'k' (kb) or
# 'm' (mb) suffix. For example: 10m = 10 megabytes.
wrapper.logfile.maxsize=0 # Maximum number of rolled log files which will be allowed before old
# files are deleted. The default value of 0 implies no limit.
wrapper.logfile.maxfiles=0 # Log Level for sys/event log output. (See docs for log levels)
wrapper.syslog.loglevel=NONE #********************************************************************
# Wrapper General Properties
#********************************************************************
# Allow for the use of non-contiguous numbered properties
wrapper.ignore_sequence_gaps=TRUE # Do not start if the pid file already exists.
wrapper.pidfile.strict=TRUE # Title to use when running as a console
wrapper.console.title=Test Wrapper Sample Application #********************************************************************
# Wrapper JVM Checks
#********************************************************************
# Detect DeadLocked Threads in the JVM. (Requires Standard Edition)
wrapper.check.deadlock=TRUE
wrapper.check.deadlock.interval=10
wrapper.max_failed_invocations=99
wrapper.console.fatal_to_stderr=FALSE
wrapper.console.error_to_stderr=FALSE
wrapper.check.deadlock.action=RESTART
wrapper.check.deadlock.output=FULL # Out Of Memory detection.
# Ignore -verbose:class output to avoid false positives.
wrapper.filter.trigger.1000=[Loaded java.lang.OutOfMemoryError
wrapper.filter.action.1000=NONE
# (Simple match)
wrapper.filter.trigger.1001=java.lang.OutOfMemoryError
# (Only match text in stack traces if -XX:+PrintClassHistogram is being used.)
#wrapper.filter.trigger.1001=Exception in thread "*" java.lang.OutOfMemoryError
#wrapper.filter.allow_wildcards.1001=TRUE
wrapper.filter.action.1001=RESTART
wrapper.filter.message.1001=The JVM has run out of memory. #********************************************************************
# Wrapper Email Notifications. (Requires Professional Edition)
#********************************************************************
# Common Event Email settings.
#wrapper.event.default.email.debug=TRUE
#wrapper.event.default.email.smtp.host=<SMTP_Host>
#wrapper.event.default.email.smtp.port=25
#wrapper.event.default.email.subject=[%WRAPPER_HOSTNAME%:%WRAPPER_NAME%:%WRAPPER_EVENT_NAME%] Event Notification
#wrapper.event.default.email.sender=<Sender email>
#wrapper.event.default.email.recipient=<Recipient email> # Configure the log attached to event emails.
#wrapper.event.default.email.attach_log=TRUE
#wrapper.event.default.email.maillog.lines=50
#wrapper.event.default.email.maillog.format=LPTM
#wrapper.event.default.email.maillog.loglevel=INFO # Enable specific event emails.
#wrapper.event.wrapper_start.email=TRUE
#wrapper.event.jvm_prelaunch.email=TRUE
#wrapper.event.jvm_start.email=TRUE
#wrapper.event.jvm_started.email=TRUE
#wrapper.event.jvm_deadlock.email=TRUE
#wrapper.event.jvm_stop.email=TRUE
#wrapper.event.jvm_stopped.email=TRUE
#wrapper.event.jvm_restart.email=TRUE
#wrapper.event.jvm_failed_invocation.email=TRUE
#wrapper.event.jvm_max_failed_invocations.email=TRUE
#wrapper.event.jvm_kill.email=TRUE
#wrapper.event.jvm_killed.email=TRUE
#wrapper.event.jvm_unexpected_exit.email=TRUE
#wrapper.event.wrapper_stop.email=TRUE # Specify custom mail content
wrapper.event.jvm_restart.email.body=The JVM was restarted.\n\nPlease check on its status.\n #********************************************************************
# Wrapper Windows NT/2000/XP Service Properties
#********************************************************************
# WARNING - Do not modify any of these properties when an application
# using this configuration file has been installed as a service.
# Please uninstall the service before modifying this section. The
# service can then be reinstalled. # Name of the service
wrapper.name=testwrapper # Display name of the service
wrapper.displayname=Test Wrapper Sample Application # Description of the service
wrapper.description=Test Wrapper Sample Application Description # Service dependencies. Add dependencies as needed starting from 1
wrapper.ntservice.dependency.1= # Mode in which the service is installed. AUTO_START, DELAY_START or DEMAND_START
wrapper.ntservice.starttype=AUTO_START # Allow the service to interact with the desktop.
wrapper.ntservice.interactive=false

参考Java Service Wrapper官网介绍:https://wrapper.tanukisoftware.com/doc/english/qna-service.html

wrapper高级配置详解参考:https://blog.csdn.net/u010419967/article/details/37690269

补充:wrapper安装的服务不能正常启动

  报错Unable to execute Java command.  系统找不到指定的文件。

  原因是找不到Java命令,解决办法就是修改wrapper.conf将java路径设为绝对路径,如下:

# Path to JVM executable. By default it must be available in PATH.
# Can be an absolute path, for example:
#wrapper.java.command=/path/to/my/jdk/bin/java
wrapper.java.command=C:\Program Files\Java\jdk1.8.0_121\bin\java.exe