解析Tomcat的启动脚本--catalina.bat
概述
Tomcat 的三个最重要的启动脚本:
startup.bat
catalina.bat
setclasspath.bat
上⼀篇咱们分析了 startup.bat 脚本
这⼀篇咱们来分析 catalina.bat 脚本.
⾄于 setclasspath.bat 这个脚本, 相信看完这⼀篇, 就可以⾃⼰看懂这个脚本了.
可以点击下载 [ ]查看附注释的 setclasspath.bat 脚本
catalina.bat
这个脚本的代码有点多, 就单独弄了⼀篇展⽰ catalina.bat 脚本中的内容. 点击 [ ]下载查看.
下⾯咱们就按照脚本中的内容⼀⾏⾏的来分析.
@echo off
setlocal
这两个命令可以参考上⼀篇的⽂章()的解释
第⼀块脚本代码
rem Suppress Terminate batch job on CTRL+C
if not ""%1"" == ""run"" goto mainEntry
if "%TEMP%" == "" goto mainEntry
if exist "%TEMP%\%~nx0.run" goto mainEntry
echo Y>"%TEMP%\%~nx0.run"
if not exist "%TEMP%\%~nx0.run" goto mainEntry
echo Y>"%TEMP%\%~nx0.Y"
call "%~f0" %* <"%TEMP%\%~nx0.Y"
rem Use provided errorlevel
set RETVAL=%ERRORLEVEL%
del /Q "%TEMP%\%~nx0.Y" >NUL 2>&1
exit /B %RETVAL%
:mainEntry
del /Q "%TEMP%\%~nx0.run" >NUL 2>&1
脚本的作⽤
判断⽤户是否使⽤
catalina.bat run
来启动 Tomcat 的.
如果⽤户使⽤ startup.bat 脚本启动 Tomcat, 那么这段脚本不会被执⾏.
这段代码看起来很乱, 慢慢分析.
第⼀⾏:
注释, 意思就是: 禁⽌使⽤ CTRL+C 来终⽌批处理任务, 也不知道是怎么禁⽌的.
第⼆⾏:
if not ""%1"" == ""run"" goto mainEntry
⾸先明⽩这⾥的这个 "%1" 这个变量代表的是什么? 正常情况下, 这个脚本是被 startup.bat 脚本调⽤的, 被调⽤的同时传递了⼀个 start 参数过来(上⼀篇分析得出的). 在批处理命令中 %1 就表⽰命令之后的第⼀个参数, 在这⾥指的就是 start. 所以 "%1" = start. 如果⽤户⽤ catalina.bat run 命令启动 Tomcat 的话, 那么这⾥的 "%1" = run.
第三⾏:
if "%TEMP%" == "" goto mainEntry
这⾥的 %TEMP% 很有可能被认为是空, 其实这⾥可以读取到系统的环境变量. 所以, 这⾥的 %TEMP% 就是系统的环境变量值, 通常装完 windows 系统的话, 系统会⾃动配置上这个环境变量. 所以这⾥⼀般是有值的. ⼤家可以去系统的环境变量看⼀下它指向那个⽬录, ⼀般就是 C:\Users\⽤户名\AppData\Local\Temp. 注意: AppData 是⼀个隐藏⽬录.
第四⾏:
if exist "%TEMP%\%~nx0.run" goto mainEntry
这⾥⼜出现了⼀个新的东西 %~nx0 . 在批处理中, 我们知道 %1 表⽰的是程序之后的第⼀个参数, 那么 %0 呢? %0 表⽰这个可执⾏程序的名称, %~nx0 的话就是程序的名称+扩展名
在这⾥就是 catalina.bat . ⼤家可以写⼀个⼩脚本(test.bat)验证⼀下: (我的脚本放在 D 盘下)
脚本内容:
@echo off
echo "%~nx0"
echo "%1"
执⾏结果:
PS D:\> .\test.bat Hello
"test.bat"
"Hello"
PS D:\>
第五⾏:
echo Y>"%TEMP%\%~nx0.run"
这段代码很简单, 就是写⼊字符 Y 到 %TEMP%\catalina.bat.run ⽂件中.
第六⾏:
if not exist "%TEMP%\%~nx0.run" goto mainEntry
⼜判断了⼀下 %TEMP%\catalina.bat.run ⽂件是否存在.
第七⾏:
echo Y>"%TEMP%\%~nx0.Y"
同第五⾏, 写⼊ Y 到 %TEMP%\catalina.bat.Y . 如果⽂件不存在, 则新建⼀个.
第⼋⾏:
call "%~f0" %* <"%TEMP%\%~nx0.Y"
这⼀⾏有点意思. ⼜出现了两个新的东西:
(因为 markdown 语法限制, 把下⾯代码写到代码块⾥)
- "%~f0" : 简单说就是表⽰当前命令的绝对路径.
- "%*" : 我们知道 %1 表⽰第⼀个参数, 依次类推, %2 表⽰第⼆个.... 那么 %* 就很好理解了, 代表所有参数.
验证⼀下
脚本内容:
@echo off
echo "%*"
echo "%~f0"
执⾏结果:
PS D:\> .\test.bat Hello World
"Hello World"
"D:\test.bat"
PS D:\>
那么后⾯的 <"%TEMP%\%~nx0.Y" 意思就是读取 %TEMP%\catalina.bat.Y ⽂件中的内容.
之后⼜通过 call 进⾏调⽤.
我们⾃⼰写⼀个例⼦, 在 D 盘建⽴ test.bat ⽂件, 再建⽴ catalina.bat.Y ⽂件
脚本内容:
call "%~f0" %* < D:/catalina.bat.Y
catalina.bat.Y ⽂件内容
Y
执⾏结果:
........
D:\>call "D:\test.bat" Hello World 0<D:/catalina.bat.Y
D:\>call "D:\test.bat" Hello World 0<D:/catalina.bat.Y
D:\>call "D:\test.bat" Hello World 0<D:/catalina.bat.Y
D:\>call "D:\test.bat" Hello World 0<D:/catalina.bat.Y
D:\>call "D:\test.bat" Hello World 0<D:/catalina.bat.Y
D:\>call "D:\test.bat" Hello World 0<D:/catalina.bat.Y
D:\>call "D:\test.bat" Hello World 0<D:/catalina.bat.Y
****** B A T C H R E C U R S I O N exceeds STACK limits ******
Recursion Count=593, Stack Usage=90 percent
****** B A T C H PROCESSING IS A B O R T E D ******
最上⾯省略了很多重复代码, 从这⾥发现它不断地调⽤⾃⼰本⾝, 直到超出了堆栈的限制才停⽌.
我们如果加上 @echo off 的话
@echo off
call "%~f0" %* < D:/catalina.bat.Y
结果只会出现
D:\>.\test.bat Hello World
****** B A T C H R E C U R S I O N exceeds STACK limits ******
Recursion Count=593, Stack Usage=90 percent
****** B A T C H PROCESSING IS A B O R T E D ******
我们这⾥只需要明⽩这些命令的作⽤就可以, 稍后我们会总结 Tomcat 执⾏这些命令的⽬的.
第⼗⾏:
set RETVAL=%ERRORLEVEL%
我们如果了解 Linux 的话都知道, 每个命令的执⾏都会返回⼀个执⾏完成之后的退出码. Linux执⾏完⼀条命令之后⽤ echo $? 来查看上⼀条命令的退出码. 在 Windows 中也是⼀样的, 命令执⾏完之后都有⾃⼰的退出码. 这⾥的 %ERRORLEVEL% 就是取的上⾯的 call 命令的退出码. 赋值给⼀个变量 RETVAL
第⼗⼀⾏:
del /Q "%TEMP%\%~nx0.Y" >NUL 2>&1
这⾥⼜出现了⼀个 del 命令, 很容易联想到 delete, 那么 /Q 是什么意思呢? 静默删除, 不会给你任何提⽰, 就⽐如 Linux 中的 rm -f ⼀样, 这⾥是删除 %TEMP%\catalina.bat.Y 这个⽂件.
后⾯的 >NUL 2>&1 ⼜是什么意思呢?
于 Linux 中的输出流的重定向原理是⼀样的.
(因为 markdown 语法限制, 把下⾯代码写到代码块⾥)
- >NUL : 表⽰将输出重定向到 NUL 中, 你什么也看不到
- 2>&1 : 2:错误输出, &1: 标准输出, 意思就是将错误消息输出到标准输出中.
- >NUL 2>&1 : 就是先将错误消息输出到标准输出中, 然后再输出到 NUL 中.
第⼗⼆⾏:
exit /B %RETVAL%
退出当前批处理, /B 指定退出时的编号, 把 RETVAL 最为退出码, 也就是 call 执⾏的命令的退出码.
最后两⾏:
:mainEntry
del /Q "%TEMP%\%~nx0.run" >NUL 2>&1
定义⼀个 mainEntry 标签, 然后删除临时⽬录中的 catalina.bat.run ⽂件.
总结第⼀段脚本的功能
简单说, 这段代码的作⽤就是调⽤本⾝, 判断临时⽬录中的⽂件是否存在来避免⼆次回调⾃⼰. 感觉写的好复杂.
下⾯就进⼊ Tomcat 的正式启动过程, 并没有开始执⾏ main ⽅法
第⼆段脚本代码
rem Guess CATALINA_HOME if not defined
set "CURRENT_DIR=%cd%"
if not "%CATALINA_HOME%" == "" goto gotHome
set "CATALINA_HOME=%CURRENT_DIR%"
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
cd ..
set "CATALINA_HOME=%cd%"
cd "%CURRENT_DIR%"
:gotHome
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
set "CATALINA_BASE=%CATALINA_HOME%"
:gotBase
这段脚本还是⽐较简单的, 主要是设置了两个环境变量 CATALINA_HOME 和 CATALINA_BASE .
如果没有配置 CATALINA_BASE 环境变量的话, 直接引⽤ CATALINA_HOME 的值
静下⼼来稍微看⼀下就懂了.
第三段脚本代码
rem Ensure that neither CATALINA_HOME nor CATALINA_BASE contains a semi-colon
rem as this is used as the separator in the classpath and Java provides no
rem mechanism for escaping if the same character appears in the path. Check this
rem by replacing all occurrences of ';' with '' and checking that neither
rem CATALINA_HOME nor CATALINA_BASE have changed
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
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
这⾥主要是判断 CATALINA_HOME 环境变量的值和 CATALINA_BASE 环境变量的值是否以分号为结尾, 如果以分号为结尾的话, 就报错退出.第四段脚本代码
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=
rem Get standard environment variables
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"
:setenvDone
rem Get standard Java environment variables
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
call "%CATALINA_HOME%\bin\setclasspath.bat" %1
if errorlevel 1 goto end
设置⼀个临时环境变量: CLASSPATH.
如果 Tomcat 的 bin ⽬录下⾯存在 setnv.bat 脚本的话, 就执⾏它. 通常情况下是没有的.
继⽽⼜判断 setclasspath.bat 脚本是否存在, 如果不存在的话, 直接报错, 停⽌启动 Tomcat.
如果存在的话, 就去调⽤它, 并把第⼀个参数传进去.
setclasspath.bat 这个脚本主要设置了⼏个环境变量
JAVA_HOME
JRE_HOME
JAVA_ENDORSED_DIRS = %CATALINA_HOME%\endorsed
_RUNJAVA = %JRE_HOME%\
_RUNJDB = %JAVA_HOME%\
第五段脚本代码
rem Add on extra jar file to CLASSPATH
rem Note that there are no quotes as we do not want to introduce random
rem quotes into the CLASSPATH
if "%CLASSPATH%" == "" goto emptyClasspath
set "CLASSPATH=%CLASSPATH%;"
:emptyClasspath
set "CLASSPATH=%CLASSPATH%%CATALINA_HOME%\bin\bootstrap.jar"
if not "%CATALINA_TMPDIR%" == "" goto gotTmpdir
set "CATALINA_TMPDIR=%CATALINA_BASE%\temp"
: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
set "CLASSPATH=%CLASSPATH%;%CATALINA_BASE%\bin\tomcat-juli.jar"
goto juliClasspathDone
:juliClasspathHome
set "CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\tomcat-juli.jar"
:juliClasspathDone
这段代码主要做了三件事:
把 Tomcat bin ⽬录下的 bootstrap.jar 加⼊到环境变量中
设置 CATALINA_TMPDIR 环境变量的值为 Tomcat ⽬录下的 temp ⽬录
把 Tomcat bin ⽬录下的 tomcat-juli.jar 加⼊到环境变量中
第六段脚本代码
if not "%JSSE_OPTS%" == "" goto gotJsseOpts
set JSSE_OPTS="-Djdk.tls.ephemeralDHKeySize=2048"
:gotJsseOpts
set "JAVA_OPTS=%JAVA_OPTS% %JSSE_OPTS%"
rem Register custom URL handlers
rem Do this here so custom URL handles (specifically 'war:...') can be used in the security policy
set "JAVA_OPTS=%JAVA_OPTS% -Djava.protocol.handler.pkgs=org.apache.catalina.webresources"
if not "%LOGGING_CONFIG%" == "" goto noJuliConfig
set LOGGING_CONFIG=-Dnop
if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfig
set LOGGING_CONFIG=-Djava.fig.file="%CATALINA_BASE%\conf\logging.properties"
: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
:noJuliManager
set "JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%"
主要是追加⼀系列的启动参数到 JAVA_OPTS 这个环境变量中.
第⼋段脚本代码
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
:use_jdk
echo Using JAVA_HOME: "%JAVA_HOME%"
: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=
设置⼀些列的环境变量:
_RUNJAVA : %JRE_HOME%\
MAINCLASS : 指定了 Tomcat 的启动类, 没错 main ⽅法就是在这个类⾥⾯.
ACTION : 动作: 就是启动
SECURITY_POLICY_FILE : 安全策略⽂件, 如果启动的时候加上了 -security 参数的话, 下⾯会对这个参数指定到 Tomcat 的 conf ⽬录下的 catalina.policy ⽂件.
JPDA : 这个参数可以百度⼀下, 我们平时⼏乎⽤不到.
第⼗段代码
if not ""%1"" == ""jpda"" goto 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
:noJpda
好像直接从第⼀⾏跳到了最后⼀⾏, 没错, ⼀般我没启动的时候没有加 jpda 参数的话, 这⾥会直接跳过, ⾥⾯的脚本是关于 JPDA 的设置等.
第⼗⼀段脚本代码
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 l
echo version What version of tomcat are you running?
goto end
好似⼀个 switch 开关.
如果我们⽤ startup.bat 启动 Tomcat 的话, 这⾥的 "%1" 的值是 start
如果通过 catalina.bat run 启动 Tomcat 的话, 这⾥的 "%1" 的值是 run
第⼗⼆段脚本代码
:doRun
shift
if not ""%1"" == ""-security"" goto execCmd
shift
echo Using Security Manager
set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"
goto execCmd
:doStart
shift
if "%TITLE%" == "" set TITLE=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
⾸先分析⼀下其中的两个 shift 命令
第⼀个 shift 是把 start 或者 run 参数移除, 然后下⾯还是利⽤ "%1" 来取参数, 这时候, 取出来的就是参数列表中的第⼆个.
第⼆个 shift 是在第⼆个参数移除掉.
我们再来⽐较⼀下 start 和 run 的启动区别.
差别
if "%TITLE%" == "" set TITLE=Tomcat
set _EXECJAVA=start "%TITLE%" %_RUNJAVA%
如果是 startup.bat 脚本启动的话, 会启动⼀个新的 cmd 窗⼝, 并且把 cmd 的 title 设置为 Tomcat.
如果是 catalina.bat run 启动的话, 不会新建 cmd 窗⼝, 也不会设置 cmd 的 title.
最后都跳到了 execCmd 标签处.
第⼗三段脚本代码
:execCmd
rem Get remaining unshifted command line arguments and save them in the
set CMD_LINE_ARGS=
:setArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
:doneSetArgs
这⾥还是利⽤ "%1" 来取出启动命令之后的参数, 如果存的话, 追加到 CMD_LINE_ARGS 环境变量上, 并把这个参数移除.
通常情况下, 我们这⾥是不会有什么参数了, -security 这个参数我们都不会追加.
继续往下⾛.
第⼗四段脚本代码
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% -dorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io goto end
很明显, 我们的 %JPDA% 没有值, 不会跳转; 由于我们没有加 -security 参数, 所以 %SECURITY_POLICY_FILE% 没有值, 不会跳转.
下⾯这段长命令就是来启动 BootStrap 类, 并把相应的参数传进去.
只要把对应的环境变量替换为它们的值, 就可以解析出这个长命令的内容. 相信你可以的. Be patient!
总结⼀下
1. ⾸先判断⼀下⽤户直接使⽤ catalina.bat run 来启动 Tocmat
2. 设置 CATALINA_HOME 和 CATALINA_BASE 环境变量值
3. 验证 CATALINA_HOME 和 CATALINA_BASE 环境变量值的正确性
4. 调⽤ setnv.bat 脚本
5. 调⽤ setclasspath.bat 脚本
6. 添加 bootstrap.jar 和 tomcat-juli.jar 到 CLASSPATH 中
7. 设置 CATALINA_TMPDIR 临时⽬录的值为 Tomcat ⽬录下的 temp
8. 追加⼀系列的参数到 JAVA_OPTS 中
9. 整合相关的启动信息, 参数
10. 启动 Tomcat
以上就是本⽂的全部内容,希望本⽂的内容对⼤家的学习或者⼯作能带来⼀定的帮助,有兴趣的朋友可以看下上篇⽂章《》
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论