Windows下,Tomcat可以以服务形式启动、停止,也可以执行脚本启动(startup.bat)、停止(shutdown.bat)。执行startup.bat时会调用catalina.bat,catalina.bat脚本又会调用setclasspath.bat进行java class path指定。上篇讲了startup.bat启动代码实现,本文将剖析catalina.bat的代码实现。
版本:8.0.36
安装目录:E:\tomcat8
由于catalina.bat脚本代码较多,为了讲解方便,代码说明统一采用高级语言注释//,并移除原代码中的很多rem英文注释。
@echo off //关闭回显
setlocal //开启局部变量
//startup.bat调用过来第一个参数为start, 跳到mainEntry
if not ""%1"" == ""run"" goto mainEntry
if "%TEMP%" == "" goto mainEntry //TEMP为空执行mainEntry,如:C:\Users\ADMINI~1\AppData\Local\Temp
if exist "%TEMP%\%~nx0.run" goto mainEntry //%~nx为脚本文件名:catalina.bat
echo Y>"%TEMP%\%~nx0.run" //临时目录下生成.run文件,内容为Y
if not exist "%TEMP%\%~nx0.run" goto mainEntry //文件不存在,执行mainEntry
echo Y>"%TEMP%\%~nx0.Y" //临时目录下生成.Y文件,内容为Y
//调用catalina.bat,%*所有参数空格分割的拼接串,如:e:\tomcat8\bin\catalina.bat arg1 arg2 Y
call "%~f0" %* <"%TEMP%\%~nx0.Y"
set RETVAL=%ERRORLEVEL% //脚本调用返回码
del /Q "%TEMP%\%~nx0.Y" >NUL 2>&1 //删除文件,/Q 静默删除,2>&1 成功和错误信息都输出到 NUL
exit /B %RETVAL%
:mainEntry
//安静删除temp目录下的catalina.bat.run文件,成功和错误信息都输出到nul
del /Q "%TEMP%\%~nx0.run" >NUL 2>&1
set "CURRENT_DIR=%cd%" // 设置CURRENT_DIR=当前目录
//CATALINA_HOME为空,跳到gotHome
if not "%CATALINA_HOME%" == "" goto gotHome
set "CATALINA_HOME=%CURRENT_DIR%" //设置CATALINA_HOME=CURRENT_DIR
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome //存在catalina.bat,执行okHome
cd .. //跳到上层目录
set "CATALINA_HOME=%cd%" //CATALINA_HOME当前目录
cd "%CURRENT_DIR%" //进入CURRENT_DIR
:gotHome
// 存在catalina.bat,执行okHome
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
echo The CATALINA_HOME environment variable is not defined correctly
echo This environment variable is needed to run this program
goto end
:okHome
rem Copy CATALINA_BASE from CATALINA_HOME if not defined
if not "%CATALINA_BASE%" == "" goto gotBase //CATALINA_BASE不为空,执行gotBase
set "CATALINA_BASE=%CATALINA_HOME%" //变量赋值
//bat逐行执行,okHome执行完毕后执行gotBase
:gotBase
// CATALINA_HOME=E:\tomcat8 没有分号 执行 homeNoSemicolon
if "%CATALINA_HOME%" == "%CATALINA_HOME:;=%" goto homeNoSemicolon
echo Using CATALINA_HOME: "%CATALINA_HOME%"
echo Unable to start as CATALINA_HOME contains a semicolon (;) character
goto end
:homeNoSemicolon
// CATALINA_BASE=E:\tomcat8 没有分号 执行 baseNoSemicolon
if "%CATALINA_BASE%" == "%CATALINA_BASE:;=%" goto baseNoSemicolon
echo Using CATALINA_BASE: "%CATALINA_BASE%"
echo Unable to start as CATALINA_BASE contains a semicolon (;) character
goto end
:baseNoSemicolon
rem Ensure that any user defined CLASSPATH variables are not used on startup,
rem but allow them to be specified in setenv.bat, in rare case when it is needed.
set CLASSPATH=
// 没有setenv.bat文件,执行checkSetenvHome
if not exist "%CATALINA_BASE%\bin\setenv.bat" goto checkSetenvHome
call "%CATALINA_BASE%\bin\setenv.bat"
goto setenvDone
:checkSetenvHome
if exist "%CATALINA_HOME%\bin\setenv.bat" call "%CATALINA_HOME%\bin\setenv.bat"
// 顺序执行,checkSetenvHome执行完执行setenvDone
:setenvDone
// 存在setclasspath.bat脚本,执行okSetclasspath
if exist "%CATALINA_HOME%\bin\setclasspath.bat" goto okSetclasspath
echo Cannot find "%CATALINA_HOME%\bin\setclasspath.bat"
echo This file is needed to run this program
goto end
:okSetclasspath
// 调用setclasspath.bat脚本,e:tomcat8\bin\setclasspath.bat start
// setclasspath.bat, 主要设置java_home,jre_home,java.exe路径
call "%CATALINA_HOME%\bin\setclasspath.bat" %1
if errorlevel 1 goto end //errorlevel非0表示出错,结束脚本
//CLASSPATH为空,跳到emptyClasspath
if "%CLASSPATH%" == "" goto emptyClasspath
set "CLASSPATH=%CLASSPATH%;"
:emptyClasspath
//设置classpath,临时目录
set "CLASSPATH=%CLASSPATH%%CATALINA_HOME%\bin\bootstrap.jar"
if not "%CATALINA_TMPDIR%" == "" goto gotTmpdir
set "CATALINA_TMPDIR=%CATALINA_BASE%\temp"
// 顺序执行,emptyClasspath执行完执行gotTmpdir
// gotTmpdir执行到最后,跳到juliClasspathDone
:gotTmpdir
rem Add tomcat-juli.jar to classpath
rem tomcat-juli.jar can be over-ridden per instance
if not exist "%CATALINA_BASE%\bin\tomcat-juli.jar" goto juliClasspathHome
//设置变量为日志jar,tomcat-juli.jar路径
set "CLASSPATH=%CLASSPATH%;%CATALINA_BASE%\bin\tomcat-juli.jar"
goto juliClasspathDone
:juliClasspathHome
set "CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\tomcat-juli.jar"
//由gotTmpdir跳过来
:juliClasspathDone
if not "%JSSE_OPTS%" == "" goto gotJsseOpts
set JSSE_OPTS="-Djdk.tls.ephemeralDHKeySize=2048" //本例因为JSSE_OPTS为空,会在此处赋值
// 顺序执行,juliClasspathDone后执行gotJsseOpts
:gotJsseOpts
set "JAVA_OPTS=%JAVA_OPTS% %JSSE_OPTS%"
if not "%LOGGING_CONFIG%" == "" goto noJuliConfig
set LOGGING_CONFIG=-Dnop
if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfig
set LOGGING_CONFIG=-Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties"
// 顺序执行,gotJsseOpts后执行noJuliConfig
:noJuliConfig
set "JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%"
if not "%LOGGING_MANAGER%" == "" goto noJuliManager
set LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
// 顺序执行,noJuliConfig后执行noJuliManager,%1=start
:noJuliManager
set "JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%"
echo Using CATALINA_BASE: "%CATALINA_BASE%"
echo Using CATALINA_HOME: "%CATALINA_HOME%"
echo Using CATALINA_TMPDIR: "%CATALINA_TMPDIR%"
if ""%1"" == ""debug"" goto use_jdk
echo Using JRE_HOME: "%JRE_HOME%"
goto java_dir_displayed //本例会执行java_dir_displayed
:use_jdk
echo Using JAVA_HOME: "%JAVA_HOME%"
// 由noJuliManager跳过来
:java_dir_displayed
echo Using CLASSPATH: "%CLASSPATH%"
set _EXECJAVA=%_RUNJAVA%
set MAINCLASS=org.apache.catalina.startup.Bootstrap
set ACTION=start
set SECURITY_POLICY_FILE=
set DEBUG_OPTS=
set JPDA=
if not ""%1"" == ""jpda"" goto noJpda //%1=start, 所以会执行noJpda
set JPDA=jpda
if not "%JPDA_TRANSPORT%" == "" goto gotJpdaTransport
set JPDA_TRANSPORT=dt_socket
:gotJpdaTransport
if not "%JPDA_ADDRESS%" == "" goto gotJpdaAddress
set JPDA_ADDRESS=localhost:8000
:gotJpdaAddress
if not "%JPDA_SUSPEND%" == "" goto gotJpdaSuspend
set JPDA_SUSPEND=n
:gotJpdaSuspend
if not "%JPDA_OPTS%" == "" goto gotJpdaOpts
set JPDA_OPTS=-agentlib:jdwp=transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%
:gotJpdaOpts
shift
// 由java_dir_displayed跳过来,%1=start,因此会跳到doStart
:noJpda
if ""%1"" == ""debug"" goto doDebug
if ""%1"" == ""run"" goto doRun
if ""%1"" == ""start"" goto doStart
if ""%1"" == ""stop"" goto doStop
if ""%1"" == ""configtest"" goto doConfigTest
if ""%1"" == ""version"" goto doVersion
echo Usage: catalina ( commands ... )
echo commands:
echo debug Start Catalina in a debugger
echo debug -security Debug Catalina with a security manager
echo jpda start Start Catalina under JPDA debugger
echo run Start Catalina in the current window
echo run -security Start in the current window with security manager
echo start Start Catalina in a separate window
echo start -security Start in a separate window with security manager
echo stop Stop Catalina
echo configtest Run a basic syntax check on server.xml
echo version What version of tomcat are you running?
goto end
:doDebug
shift
set _EXECJAVA=%_RUNJDB%
set DEBUG_OPTS=-sourcepath "%CATALINA_HOME%\..\..\java"
if not ""%1"" == ""-security"" goto execCmd
shift
echo Using Security Manager
set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"
goto execCmd
:doRun
shift
if not ""%1"" == ""-security"" goto execCmd
shift
echo Using Security Manager
set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"
goto execCmd
// 由noJpda跳过来
:doStart
shift // %2 变成 %1
if "%TITLE%" == "" set TITLE=Tomcat // 设置cmd命令框标题为Tomcat
set _EXECJAVA=start "%TITLE%" %_RUNJAVA%
if not ""%1"" == ""-security"" goto execCmd
shift
echo Using Security Manager
set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"
goto execCmd
:doStop
shift
set ACTION=stop
set CATALINA_OPTS=
goto execCmd
:doConfigTest
shift
set ACTION=configtest
set CATALINA_OPTS=
goto execCmd
:doVersion
%_EXECJAVA% -classpath "%CATALINA_HOME%\lib\catalina.jar" org.apache.catalina.util.ServerInfo
goto end
// 由doStart跳过来
:execCmd
rem Get remaining unshifted command line arguments and save them in the
set CMD_LINE_ARGS=
// execCmd 顺序执行而来
:setArgs
// startup.bat start 带入的参数在之前的shift 操作中已经移出,此处%1为空,跳到doneSetArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
//由setArgs跳过来
:doneSetArgs
rem Execute Java with the applicable properties
if not "%JPDA%" == "" goto doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurity
//执行 %_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION% 随后脚本结束
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurity
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurityJpda
%_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurityJpda
%_EXECJAVA% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:end
因为catalina.bat脚本确实有点长,里面的跳转比较多,看起来不够直接,这儿列出catalina.bat脚本的跳转关系,以startup.bat 调用catalina.bat start为例。
if not ""%1"" == ""run"" goto mainEntry
:mainEntry
if not "%CATALINA_HOME%" == "" goto gotHome
:gotHome
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
:okHome
set "CATALINA_BASE=%CATALINA_HOME%"
顺序执行gotBase
:gotBase
if "%CATALINA_HOME%" == "%CATALINA_HOME:;=%" goto homeNoSemicolon
:homeNoSemicolon
if "%CATALINA_BASE%" == "%CATALINA_BASE:;=%" goto baseNoSemicolon
:baseNoSemicolon
if not exist "%CATALINA_BASE%\bin\setenv.bat" goto checkSetenvHome
顺序执行setenvDone
:setenvDone
if exist "%CATALINA_HOME%\bin\setclasspath.bat" goto okSetclasspath
:okSetclasspath
if "%CLASSPATH%" == "" goto emptyClasspath
:emptyClasspath
按顺序执行 gotTmpdir
:gotTmpdir
//E:\tomcat8\bin\tomcat-juli.jar
set "CLASSPATH=%CLASSPATH%;%CATALINA_BASE%\bin\tomcat-juli.jar"
goto juliClasspathDone
:juliClasspathDone
set JSSE_OPTS="-Djdk.tls.ephemeralDHKeySize=2048"
:gotJsseOpts
会顺序执行到 :noJuliConfig
:noJuliConfig
会顺序执行到 :noJuliManager
:noJuliManager
会顺序执行到goto java_dir_displayed
:java_dir_displayed
if not ""%1"" == ""jpda"" goto noJpda, 会跳到noJpda
:noJpda
if ""%1"" == ""start"" goto doStart, 会跳到doStart
:doStart
if not ""%1"" == ""-security"" goto execCmd, 会跳到execCmd
:execCmd
set CMD_LINE_ARGS=
:setArgs
if ""%1""=="""" goto doneSetArgs
:doneSetArgs
//start "Tomcat" "E:\software\java\jdk1.8.0_92\bin\java.exe" "-Djdk.tls.ephemeralDHKeySize=2048" -Djava.util.logging.config.file="E:\tomcat8\conf\logging.properties" -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs="E:\tomcat8\endorsed" -classpath "E:\tomcat8\bin\bootstrap.jar;E:\tomcat8\bin\tomcat-juli.jar" -Dcatalina.base="E:\tomcat8" -Dcatalina.home="E:\tomcat8" -Djava.io.tmpdir="E:\tomcat8\temp" org.apache.catalina.startup.Bootstrap start
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
脚本最后调用的是:
start "Tomcat" "E:\software\java\jdk1.8.0_92\bin\java.exe" "-Djdk.tls.ephemeralDHKeySize=2048" -Djava.util.logging.config.file="E:\tomcat8\conf\logging.properties" -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs="E:\tomcat8\endorsed" -classpath "E:\tomcat8\bin\bootstrap.jar;E:\tomcat8\bin\tomcat-juli.jar" -Dcatalina.base="E:\tomcat8" -Dcatalina.home="E:\tomcat8" -Djava.io.tmpdir="E:\tomcat8\temp" org.apache.catalina.startup.Bootstrap start
启动java进程,运行org.apache.catalina.startup.Bootstrap类。