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包:
将上面程序也打成包:(使用eclipse打包或者直接Jdk自带的jar打包)
1.下载serviceWrapper包
下载地址:http://qiaoliqiang.cn/fileDown/wrapper-windows-x86-32-3.5.25.zip
下载后是一个压缩包,解压目录如下:
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目录是存放支持其他语言的语言包,一般用不到
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:
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 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: "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=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.pid=21292 -Dwrapper.version="3.5.25" -Dwrapper.native_library="wrapper" -Dwrapper.arch="x86" -Dwrapper.cpu.timeout="10" -Dwrapper.jvmid=1 org.tanukisoftware.wrapper.WrapperSimpleApp MapTest jvm 1 | WrapperManager: Initializing... jvm 1 | WrapperManager: jvm 1 | WrapperManager: WARNING - Unable to load the Wrapper''s native library 'wrapper.dll'. jvm 1 | WrapperManager: The file is located on the path at the following location but jvm 1 | WrapperManager: could not be loaded: jvm 1 | WrapperManager: C:\Users\Administrator\Desktop\SW\bin\..\lib\wrapper.dll jvm 1 | WrapperManager: Please verify that the file is both readable and executable by the jvm 1 | WrapperManager: current user and that the file has not been corrupted in any way. jvm 1 | WrapperManager: One common cause of this problem is running a 32-bit version jvm 1 | WrapperManager: of the Wrapper with a 64-bit version of Java, or vica versa. jvm 1 | WrapperManager: This is a 64-bit JVM. jvm 1 | WrapperManager: Reported cause: jvm 1 | WrapperManager: C:\Users\Administrator\Desktop\SW\lib\wrapper.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform jvm 1 | WrapperManager: System signals will not be handled correctly. jvm 1 | WrapperManager: jvm 1 | 访问次数:0 jvm 1 | log4j:WARN No appenders could be found for logger (org.apache.http.impl.conn.BasicClientConnectionManager). jvm 1 | log4j:WARN Please initialize the log4j system properly. jvm 1 | 访问次数:1 jvm 1 | 访问次数:2 jvm 1 | 访问次数:3
(2)将程序注册为服务:
bin\wrapper.exe -i ..\conf\wrapper.conf
结果:
wrapper | Hello World Server service installed.
(3)启动服务:
net start helloworld
或者:
bin\wrapper.exe -t ..\conf\wrapper.conf
启动之后我们回看到程序输出在logs\wrapper.log文件内:
(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%,如下:
jre目录下:
(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目录删掉,启动服务报错如下:
在wrapper.log中查看到的日志文件如下:
至此我们实现了简单的有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 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: "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.1.jar;../mylib/httpcore-4.3.jar;../mylib/httpmime-4.3.1.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=1 org.tanukisoftware.wrapper.WrapperSimpleApp 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 wrapper | CTRL-C trapped. Shutting down. 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. 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目录下)
我们只需要点击上面的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
StackOverflow上有人回答类似的问题,用的方法是
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包以及自己打的包:
(3)目录结构:
(4)bin目录与上面一样,jre也一样,lang也一样,lib也一样,logs也一样,mylib在(2)中换过,将log4j.properties文件复制到conf目录下:
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测试控制台:
logs\wrapper.log文件内容如下:
E:\test.log文件内容如下:
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目录:
2.将自己的项目解压之后放在webapps目录下(这里采用目录解压部署,参考:https://www.cnblogs.com/qlqwjy/p/9478649.html)
3.与上面部署一样将所需要的文件拷贝到servicewrapper目录下:
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.72\servicewrapper>bin\wrapper.exe -i ..\conf\wrapper.conf wrapperm | Exam Server service installed. C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.72\servicewrapper>sc qc examservice [SC] QueryServiceConfig 成功 SERVICE_NAME: examservice TYPE : 110 WIN32_OWN_PROCESS (interactive) START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.72\servicewrapper\bin\wrapper.exe -s C :\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.72\servicewrapper\conf\wrapper.conf wrapper.console.flush=true wrapp er.internal.namedpipe=1139632464 LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : Exam Server DEPENDENCIES : SERVICE_START_NAME : LocalSystem C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.72\servicewrapper>
6.启动服务并进行日志查看:
由于项目中使用了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