How tomcat works——17 啟動Tomcat
概述
本章關注的重點是如何使用 org.apache.catalina.startup下面的Catalina和 Bootstrap 類來啟動Tomcat。Catalina類用於解析Tomcat配置檔案server.xml以及啟動和停止Server。Bootstrap 類建立一Catalina例項並呼叫它的 process()方法。理論上,這兩個類可以合成一個類。但是,為了支援 Tomcat 的多模式啟動,提供了多個引導類。例如前述的 Bootstrap 類是將 Tomcat 作為一個獨立的程式執行,而org.apache.catalina.startup.BootstrapService 則是將 Tomcat 作為一個Windows NT 系統的服務來執行。
為了使用方便,Tomcat 提供了批處理檔案及Shell 指令碼來啟動和停止servlet 容器。有了這些批處理檔案和 Shell 指令碼的幫助,使用者則就不需要記住java.exe 的選項來執行 Bootstrap 類,簡單地執行批處理檔案或Shell 指令碼即可。
本章第1節討論 Catalina 類,第2節討論 Bootstrap 類。要理解本章內容,首先要確保已經讀過 14、15、16 章的內容。本章還討論瞭如何在 Windows 以及 Unix/Linux 下面執行 Tomcat。一節介紹 Windows 環境下的批處理檔案,一節介紹 Unix/Linux 下面的 Shell 指令碼。
17.1 Catalina類
org.apache.catalina.startup.Catalina 是 Tomcat 的啟動類。它包含一個用於解析%CATALINE_HOME%/conf 目錄下面 server.xml配置檔案的 Digester。通過理解新增到此Digester的規則,我們可以按需配置Tomcat。
Catalina 類還封裝了一個 Server 物件用來提供服務。如第 15 章所介紹,一個Service物件包括一個容器以及一個或多個聯結器。我們可以使用Catalina 來啟動或停止 Server 物件。
可以首先初始化一個 Catalina 物件並呼叫它的 process()方法來啟動 Tomcat,在呼叫該方法時必須傳遞合適的引數。如果要啟動Tomcat,則第一個引數為startl;如果關閉,則傳送shutdown命令停止它。 還有其它可接受的命令,如-help,-config,-debug和-nonaming。
注意:nonaming 引數出現時,說明不支援 JNDI 名稱空間。有關更多的 Tomcat 對於 JNDI 名稱空間的支援請看 org.apache.naming 包。
通常,需要 Bootstrap 類來初始化 Catalina 物件並呼叫它的 process()方法,即使 Catalina 有自己的 main()方法。下一節裡將會介紹Bootstrap類,本節也會對它是如何工作的略做介紹。
Tomcat4 中的 process ()方法如 Listing17.1 所示:
Listing 17.1: The process method of the Catalina class
public void process(String args[]) {
setCatalinaHome();
setCatalinaBase();
try {
if (arguments(args))
execute();
}catch (Exception e) {
e.printStackTrace(System.out);
}
}
該方法設定2個系統屬性 catalina.home 和 catalina.base,catalina.home預設值是 user.dir 屬性的值。Catalina.base 被設定為 catalina.home 的值。因此這2個屬性的值都跟 user.dir 屬性值相同。
注意:user.dir 系統屬性是指當前使用者工作空間,即執行 Java 命令的目錄。可以通過java.lang.System 中getProperties()獲取到系統屬性列表。
然後 process()方法呼叫了arguments()方法,如Listing17.2。該方法處理引數列表,如果 Catalina 物件能繼續工作則返回 true。
Listing 17.2: The arguments method
protected boolean arguments(String args[]) {
boolean isConfig = false;
if (args.length < 1) {
usage();
return (false);
}
for (int i = 0; i < args.length; i++) {
if (isConfig) {
configFile = args[i];
isConfig = false;
} else if (args[i].equals("-config")) {
isConfig = true;
} else if (args[i].equals("-debug")) {
debug = true;
} else if (args[i].equals("-nonaming")) {
useNaming = false;
} else if (args[i].equals("-help")) {
usage();
return (false);
} else if (args[i].equals("start")) {
starting = true;
} else if (args[i].equals("stop")) {
stopping = true;
} else {
usage();
return (false);
}
}
return (true);
}
process()方法檢查 arguments()方法的返回值,如果為 true 就呼叫 execute()方法。該方法如 Listing17.3 所示:
Listing 17.3: The execute method
protected void execute() throws Exception {
if (starting)
start();
else if (stopping)
stop();
}
方法execute()用於呼叫 start()或stop() 方法來啟動或者停止 Tomcat。這2個方法將在下面的子章節中介紹。
注意:在 Tomcat5 中,沒有 execute() 方法,process()方法直接呼叫 start() 方法和stop()方法。
17.1.1 start()方法
start()方法建立一個 Digester 例項來處理 server.xml 檔案(Tomcat 的配置檔案)。在解析 XML 檔案之前,start()方法呼叫Digester 的 push()方法,傳遞當前Catalina 物件。這意味著Catalina 物件是Digester內部堆疊中第1個物件。解析檔案可以設定Server物件的變數,Server預設型別是org.apache.catalina.core.StandardServer。然後 start()方法呼叫Server物件的 initialize()和start()方法。然後再呼叫Server的await()方法,伺服器分配一個執行緒等待停止命令。該方法不返回,直到收到停止命令。當 await()方法返回後,則繼續呼叫Server物件的 stop()方法用於停止伺服器以及所有元件。start()方法也採用了關閉鉤子的方法以此確保Server物件的 stop()方法即使在使用者突然退出該應用程式情況下也總會執行。start()方法如 Listing17.4 所示。
Listing 17.4: The start method
protected void start() {
// Create and execute our Digester
Digester digester = createStartDigester();
File file = configFile();
try {
InputSource is =
new InputSource("file://" + file.getAbsolutePath());
FileInputStream fis = new FileInputStream(file);
is.setByteStream(fis);
digester.push(this);
digester.parse(is);
fis.close();
} catch (Exception e) {
System.out.println("Catalina.start: " + e);
e.printStackTrace(System.out);
System.exit(1);
}
// Setting additional variables
if (!useNaming) {
System.setProperty("catalina.useNaming", "false");
} else {
System.setProperty("catalina.useNaming", "true");
String value = "org.apache.naming";
String oldValue =
System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
if (oldValue != null) {
value = value + ":" + oldValue;
}
System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
value = System.getProperty
(javax.naming.Context.INITIAL_CONTEXT_FACTORY);
if (value == null) {
System.setProperty
(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
}
}
// If a SecurityManager is being used, set properties for
// checkPackageAccess() and checkPackageDefinition
if( System.getSecurityManager() != null ) {
String access = Security.getProperty("package.access");
if( access != null && access.length() > 0 )
access += ",";
else
access = "sun.,";
Security.setProperty("package.access",
access + "org.apache.catalina.,org.apache.jasper.");
String definition = Security.getProperty("package.definition");
if( definition != null && definition.length() > 0 )
definition += ",";
else
definition = "sun.,";
Security.setProperty("package.definition",
// FIX ME package "javax." was removed to prevent HotSpot
// fatal internal errors
definition + "java.,org.apache.catalina.,org.apache.jasper.");
}
// Replace System.out and System.err with a custom PrintStream
SystemLogHandler log = new SystemLogHandler(System.out);
System.setOut(log);
System.setErr(log);
Thread shutdownHook = new CatalinaShutdownHook();
// Start the new server
if (server instanceof Lifecycle) {
try {
server.initialize();
((Lifecycle) server).start();
try {
// Register shutdown hook
Runtime.getRuntime().addShutdownHook(shutdownHook);
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
// Wait for the server to be told to shut down
server.await();
} catch (LifecycleException e) {
System.out.println("Catalina.start: " + e);
e.printStackTrace(System.out);
if (e.getThrowable() != null) {
System.out.println("----- Root Cause -----");
e.getThrowable().printStackTrace(System.out);
}
}
}
// Shut down the server
if (server instanceof Lifecycle) {
try {
try {
// Remove the ShutdownHook first so that server.stop()
// doesn't get invoked twice
Runtime.getRuntime().removeShutdownHook(shutdownHook);
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
((Lifecycle) server).stop();
} catch (LifecycleException e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
if (e.getThrowable() != null) {
System.out.println("----- Root Cause -----");
e.getThrowable().printStackTrace(System.out);
}
}
}
}
17.1.2 stop()方法
stop()方法用於關閉 Catalina 並且關閉 Server 物件。Stop 方法如 Listing17.5所示。
Listing 17.5: The stop Method
protected void stop() {
// Create and execute our Digester
Digester digester = createStopDigester();
File file = configFile();
try {
InputSource is =
new InputSource("file://" + file.getAbsolutePath());
FileInputStream fis = new FileInputStream(file);
is.setByteStream(fis);
digester.push(this);
digester.parse(is);
fis.close();
} catch (Exception e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
System.exit(1);
}
// Stop the existing server
try {
Socket socket = new Socket("127.0.0.1", server.getPort());
OutputStream stream = socket.getOutputStream();
String shutdown = server.getShutdown();
for (int i = 0; i < shutdown.length(); i++)
stream.write(shutdown.charAt(i));
stream.flush();
stream.close();
socket.close();
} catch (IOException e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
System.exit(1);
}
}
注意stop()方法通過 createStopDigester()方法建立了一個 Digester 例項。把當前Catalina 物件壓入到 Digester 內部棧中,並解析其配置檔案,Digester 的規則新增將在下一小節中介紹。
在收到關閉命令後,stop()方法則停止伺服器。
17.1.3 啟動Digester
Catalina 的 createStartDigester()用於建立 Digester 例項然後向其新增規則來解析 server.xml。該檔案用於配置Tomcat,位於%CATALINE_HOME%/conf 目錄下面。向 Digester 新增的規則是理解 Tomcat 配置的關鍵。createStartDigester()方法如 Listing17.6 所示。
Listing 17.6: The createStartDigester method
protected Digester createStartDigester() {
// Initialize the digester
Digester digester = new Digester();
if (debug)
digester.setDebug(999);
digester.setValidating(false);
// Configure the actions we will be using
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResources");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResources");
digester.addObjectCreate("Server/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");
digester.addObjectCreate("Server/Service/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Listener");
digester.addSetNext("Server/Service/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
digester.addObjectCreate("Server/Service/Connector",
"org.apache.catalina.connector.http.HttpConnector",
"className");
digester.addSetProperties("Server/Service/Connector");
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.Connector");
digester.addObjectCreate("Server/Service/Connector/Factory",
"org.apache.catalina.net.DefaultServerSocketFactory",
"className");
digester.addSetProperties("Server/Service/Connector/Factory");
digester.addSetNext("Server/Service/Connector/Factory",
"setFactory",
"org.apache.catalina.net.ServerSocketFactory");
digester.addObjectCreate("Server/Service/Connector/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/Listener");
digester.addSetNext("Server/Service/Connector/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
// Add RuleSets for nested elements
digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Default"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/DefaultContext/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/Default"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/DefaultContext/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
digester.addRule("Server/Service/Engine",
new SetParentClassLoaderRule(digester,
parentClassLoader));
return (digester);
}
createStartDigester()方法建立一個 org.apache.commons.digester.Digester類的例項,然後新增規則。
在 server.xml 中的前三個規則是針對 server 元素的,server 元素是根元素,如下是 server 模式的規則:
digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer", "className");
digester.addSetProperties("Server");
digester.addSetNext("Server", "setServer","org.apache.catalina.Server");
在遇到 server 元素時,Digester 要建立org.apache.catalina.core.StandardServer 的例項。一種例外是 server 元素配置了className 屬性,此時它表示是要被初始化的類。
第二個規則使用具有相同名稱的元素屬性的值來填充Server物件的屬性。
第三條規則將 Server 物件壓入堆疊並將其與下一個物件(Catalina 物件)相關聯,使用的方法是 setServer()方法。是如何將一個 Catalina 的物件放入 Digester的棧中的?在start()方法中呼叫Digester的push()方法來解析server.xml:
digester.push (this);
上面的程式碼將 Catalina 物件壓入 Digester 的內部棧中。
其餘的規則可以根據方法中的程式碼得出,如果有困難,請重讀 第15 章。
17.1.4 停止Digester
createStopDigester()方法返回一個 Digester 物件用於優雅地停止伺服器物件,該方法如 Listing17.7所示。
Listing 17.7: The stop method
protected Digester createStopDigester() {
// Initialize the digester
Digester digester = new Digester();
if (debug)
digester.setDebug(999);
// Configure the rules we need for shutting down
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
return (digester);
}
跟開始 Digester 不同,停止 Digester 僅僅對根元素感興趣。
17.2 Bootstrap類
org.apache.catalina.startup.Bootstrap 類提供了 Tomcat 的啟動入口。當執行 startup.bat 或 startup.sh 時,實際上執行的就是該類中的main()方法。該方法建立3個類載入器並初始化 Catalina 類,然後呼叫 Catalina 的process()方法。
Bootstrap 類如 Listing17.8 所示:
package org.apache.catalina.startup;
import java.io.File;
import java.lang.reflect.Method;
/**
* Boostrap loader for Catalina. This application constructs a class loader
* for use in loading the Catalina internal classes (by accumulating all of the
* JAR files found in the "server" directory under "catalina.home"), and
* starts the regular execution of the container. The purpose of this
* roundabout approach is to keep the Catalina internal classes (and any
* other classes they depend on, such as an XML parser) out of the system
* class path and therefore not visible to application level classes.
*
* @author Craig R. McClanahan
* @version $Revision: 1.36 $ $Date: 2002/04/01 19:51:31 $
*/
public final class Bootstrap {
// ------------------------------------------------------- Static Variables
/**
* Debugging detail level for processing the startup.
*/
private static int debug = 0;
// ----------------------------------------------------------- Main Program
/**
* The main program for the bootstrap.
*
* @param args Command line arguments to be processed
*/
public static void main(String args[]) {
// Set the debug flag appropriately
for (int i = 0; i < args.length; i++) {
if ("-debug".equals(args[i]))
debug = 1;
}
// Configure catalina.base from catalina.home if not yet set
if (System.getProperty("catalina.base") == null)
System.setProperty("catalina.base", getCatalinaHome());
// Construct the class loaders we will need
ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;
try {
File unpacked[] = new File[1];
File packed[] = new File[1];
File packed2[] = new File[2];
ClassLoaderFactory.setDebug(debug);
unpacked[0] = new File(getCatalinaHome(),
"common" + File.separator + "classes");
packed2[0] = new File(getCatalinaHome(),
"common" + File.separator + "endorsed");
packed2[1] = new File(getCatalinaHome(),
"common" + File.separator + "lib");
commonLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed2, null);
unpacked[0] = new File(getCatalinaHome(),
"server" + File.separator + "classes");
packed[0] = new File(getCatalinaHome(),
"server" + File.separator + "lib");
catalinaLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
unpacked[0] = new File(getCatalinaBase(),
"shared" + File.separator + "classes");
packed[0] = new File(getCatalinaBase(),
"shared" + File.separator + "lib");
sharedLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
} catch (Throwable t) {
log("Class loader creation threw exception", t);
System.exit(1);
}
Thread.currentThread().setContextClassLoader(catalinaLoader);
// Load our startup class and call its process() method
try {
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Instantiate a startup class instance
if (debug >= 1)
log("Loading startup class");
Class startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
// Set the shared extensions class loader
if (debug >= 1)
log("Setting startup class properties");
String methodName = "setParentClassLoader";
Class paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
// Call the process() method
if (debug >= 1)
log("Calling startup class process() method");
methodName = "process";
paramTypes = new Class[1];
paramTypes[0] = args.getClass();
paramValues = new Object[1];
paramValues[0] = args;
method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
} catch (Exception e) {
System.out.println("Exception during startup processing");
e.printStackTrace(System.out);
System.exit(2);
}
}
/**
* Get the value of the catalina.home environment variable.
*/
private static String getCatalinaHome() {
return System.getProperty("catalina.home",
System.getProperty("user.dir"));
}
/**
* Get the value of the catalina.base environment variable.
*/
private static String getCatalinaBase() {
return System.getProperty("catalina.base", getCatalinaHome());
}
/**
* Log a debugging detail message.
*
* @param message The message to be logged
*/
private static void log(String message) {
System.out.print("Bootstrap: ");
System.out.println(message);
}
/**
* Log a debugging detail message with an exception.
*
* @param message The message to be logged
* @param exception The exception to be logged
*/
private static void log(String message, Throwable exception) {
log(message);
exception.printStackTrace(System.out);
}
}
Bootstrap 類有4個靜態方法:2個 log 方法、getCatalinaHome()以及getCatalinaBase()方法。getCatalinaHome()方法的實現如下:
return System.getProperty("catalina.home",System.getProperty("user.dir"));
它意味著如果在前面沒有提供 catalina.home 的值,它會使用 user.dir 的值。
getCatalinaBase()方法的實現如下:
return System.getProperty("catalina.base", getCatalinaHome());
它返回 catalina.base 的值,如果該值不存在則返回 catalina.home 的值。
getCatalinaHome() 和 getCatalinaBase() 都會被 Bootstrap 類的main()方法呼叫。
Bootstrap 類中main()方法還構造了3個用於不同用處的載入器。擁有不同類載入器的主要原因是阻止應用程式類(Web應用程式中的servlet和其它幫助類)在WEB-INF/classes和WEB-INF/lib目錄之外執行類。部署到%CATALINE_HOME%/common/lib目錄的Jar包也是被允許的。
這3個類載入器定義如下:
// Construct the class loaders we will need
ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;
每個類載入器都給定一個可以訪問的路徑。commonLoader 可以訪問如下3個目錄下的類:%CATALINA_HOME%/common/classes,%CATALINA_HOME%/common/endorsed
和 %CATALINA_HOME%/common/lib。
try {
File unpacked[] = new File[1];
File packed[] = new File[1];
File packed2[] = new File[2];
ClassLoaderFactory.setDebug(debug);
unpacked[0] = new File(getCatalinaHome(),"common" + File.separator + "classes");
packed2[0] = new File(getCatalinaHome(),"common" + File.separator + "endorsed");
packed2[1] = new File(getCatalinaHome(),"common" + File.separator + "lib");
commonLoader =ClassLoaderFactory.createClassLoader(unpacked, packed2, null);
catalinaLoader 負責載入 Catalina 容器需要的類,它可以載入%CATALINA_HOME%/server/classes 和%CATALINA_HOME%/server/lib 目錄下面的類。以及commonLoader類載入器可以訪問的所有目錄。
unpacked[0] = new File(getCatalinaHome(),"server" + File.separator + "classes");
packed[0] = new File(getCatalinaHome(),"server" + File.separator + "lib");
catalinaLoader =ClassLoaderFactory.createClassLoader(unpacked, packed,commonLoader);
sharedLoader 可以訪問%CATALINA_HOME%/shared/classes和%CATALJNA_HOME%/shared/lib 目錄下的類以及 commondLoader 類可以訪問的類。然後將sharedLoader類載入器分配為部署在Tomcat中該上下文相關聯的每個Web應用程式的每個類載入器的父類載入器。
unpacked[0] = new File(getCatalinaBase(),"shared" + File.separator + "classes");
packed[0] = new File(getCatalinaBase(),"shared" + File.separator + "lib");
sharedLoader =ClassLoaderFactory.createClassLoader(unpacked, packed,commonLoader);
}catch (Throwable t) {
log('Class loader creation threw exception", t);
System.exit(1);
}
請注意,sharedLoader類載入器無法訪問Catalina內部類和CLASSPATH環境中定義的類路徑變數。可以在第8章看到更多的載入器工作原理。
建立完3個類載入器後,載入了 Catalina 類並建立它的例項並將其賦值給 startupInstance 變數:
Class startupClass =catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
然後呼叫 setParentClassLoader()方法,將 sharedLoader 作為引數:
// Set the shared extensions class loader
if (debug >= 1)
log("Setting startup class properties");
String methodName = "setParentClassLoader";
Class paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
最後,呼叫 Catalina 物件的 process ()方法:
// Call the process() method
if (debug >= 1)
log("Calling startup class process() method");
methodName = "process";
paramTypes = new Class[1];
paramTypes[0] = args.getClass();
paramValues = new Object[1];
paramValues[0] = args;
method = startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
17.3 Windows平臺執行Tomcat
如在前面的小節中介紹的,可以使用 Bootstrap 類將 Tomcat 作為一個獨立程式執行。在 Windows 平臺下,可以使用 starup.bat和 shutdown.bat來啟動、停止Tomcat。這兩個批處理檔案都在%CATALINA_HOME%/bin 目錄下。本節主要討論批處理檔案,對於不熟悉 DOS命令的,首先閱讀“批處理檔案簡介”小節。
17.3.1 批處理檔案簡介
本節主要介紹批處理檔案,這樣就能理解用於啟動和停止 Tomcat 的批處理檔案。尤其,解釋瞭如下命令:rem, if, echo, goto, label 等。這裡並沒有做全面介紹,如果需要更多內容可以查閱其它資源。
首先,一個批處理檔案的副檔名必須為.bat。可以雙擊啟動或在DOS控制檯命令列中呼叫它。一旦被呼叫,會從頭至尾一行行的執行。在Tomcat的批處理檔案中用到的元素會在下面介紹到。
注意:DOS 命令和環境變數不區分大小寫。
rem:
命令 rem 用作註釋,rem 開頭的行會被忽略不做處理。
pause:
命令 pause 暫停批處理檔案處理並要求使用者按鍵,使用者按鍵後會繼續執行。
echo:
該命令將它後面的文字顯示到 DOS 控制檯上面。例如,下面的語句列印 Hello World 到控制檯上並暫停。需要 pause 命令的原因是這樣才能看到控制檯上顯示的資訊:
echo Hello World
pause
要打印出一個環境變數的值,需要使用%將該變數括起來。例如,下面的命令打印出 myVar 的值:echo %myVar%
要打印出作業系統的名字,可以使用如下命令:echo %OS%
echo off:
echo off 防止批處理檔案中的命令被顯示,只顯示執行結果。但是 echo off 命令仍然會顯示,要禁止 echo off 命令可以使用@echo off。
@echo off:
@echo off 跟 echo off 相似,但是它也禁止 echo off 命令本身。
set:
該命令用於設定使用者定義的或環境變數。設定的變數值臨時存放在記憶體中,在處理完成後被拋棄。例如,下面的命令建立一個名為 THE_KING 的環境變數,值為 Elvis 並將其顯示
在控制檯上:
set THE_KING=Elvis
echo %THE_KING%
pause
注意:要引用變數的值,使用%符號將變數名括起來。例如,echo %the_king%表示顯示 THE_KING 的值。
label:
使用冒號來表示一個標籤,你可以將標籤傳遞給 goto 命令,讓處理過程跳到該標籤處。下面是一個名為 end 的標籤:end。接下來看 goto 命令使用。
goto:
命令 goto 強制批處理檔案跳到標籤定義的行,看下面的例子:
echo Start
goto end
echo I can guarantee this line will not be executed
:end
echo End
pause
在第一行打印出 Start 之後,批處理檔案執行 goto 命令,這樣控制器跳到 end標籤。第三行被跳過。
if:
if 用於測試,它有如下三種使用方式
1. 測試一個變數的值
2. 測試檔案的存在性
3. 測試錯誤值
要測試一個變數的值,使用如下的格式:
if variable==value nextCommand
例如,如下的語句測試 myVar 的值是不是 3。如果是則列印Correct 到控制檯上:
set myVar=3
if %myVar%==3 echo Correct
執行上面的命令會測試 myVar 的值並打印出 Correct。
要測試一個檔案是否存在,可以使用如下格式:
if exist c:\temp\myFile.txt goto start
如果在 c:\temp 目錄中存在 myFile.txt 檔案,控制器會跳到 start 標籤。
也可以使用 not 關鍵字來對一個表示式取反。
not:
關鍵字 not 用於對一個表示式取反,例如,如下命令在 myVar 的值不等於 3 時打印出 Correct:
set myVar=3
if not %myVar%==3 echo Correct
pause
如下命令在 c:\temp 目錄中不存在 myFile.txt 檔案時跳到 end 標籤:
if not exist c:\temp\myFile.txt goto end
exist:
關鍵字 exist 跟 if 語句連線用於測試一個檔案是否存在,參考 if 語句的例子。
接收引數:
可以給批處理檔案傳遞引數,可以使用%1 引用第一個引數,%2 引用第二個引數。依次類推。例如,如下命令列印第一個引數到控制檯上:echo %1
如果批處理檔案的名字為 test.bat,可以使用 test Hello 命令來呼叫它,這樣就會在控制檯上顯示 Hello。
下面的批處理檔案檢查第一個引數,如果是 start,就打印出 Starting application。如果是 stop 就打印出 Stopping application。其它情況打印出:Invalid parameter。
echo off
if %1==start goto start
if %1==stop goto stop
goto invalid
:start
echo Starting application
goto end
:stop
echo Stopping application
goto end
:invalid
echo Invalid parameter
:end
可以用“%1”與空字串比較來檢查批處理檔案是否有第一個引數,如果沒有則打印出 No parameter:
if "%1"=="" echo No parameter
上面的語句跟下面的相同:
if ""%1""=="""" echo No parameter
shift:
命令 shift 向後移動引數,意味這%2 指向%1,%3 指向%2,依次類推。例如下面的批處理檔案使用了 shift 命令:
echo off
shift
echo %1
echo %2
如果執行該命令的時候傳遞 3 個引數 a、b、c,會獲得如下輸出:
b
c
第一個引數可以使用%0 引用,最後一個引數丟失。
call:
命令 call 用於呼叫另一個命令。
setLocal:
可以使用 setLocal 命令來指明對於環境變數的改變為本地修改。環境變數的值會在執行完檔案或碰到 endLocal 命令後恢復。
start:
開啟一個新的視窗,可以使用 start 命令,可以傳遞個引數作為視窗的標題:start “Title”
另外,可以傳遞給該視窗要執行的命令,格式如下:start “Title” commandName
17.3.2 catalina.bat批處理檔案
catalina.bat 檔案可以用於啟動和停止 Tomcat。但用 startup.bat 和shutdown.bat 可更簡便地啟動和停止 Tomcat,這兩個批處理檔案都是通過傳遞合適的引數給 catalina.bat 檔案來實現的。
必須在%CATALINA_HOME%下的 bin目錄下面使用如下命令來呼叫 catalina.bat:
catalina command
或者在%CATALINA_HOME%目錄下使用如下命令:
bin/catalina command
這兩種情況下,可以傳遞的值如下:
•debug. 在debugger模式下啟動
•debug -security. 安全模式除錯
•embedded.嵌入式啟動
•jpda start.在JPDA 模式下啟動除錯
•run. 在當前視窗啟動
•run -security. 在當前視窗安全模式下啟動
•start.獨立視窗啟動
•start -security. 獨立視窗安全模式啟動
•stop. 停止
例如,要在一個獨立的視窗執行 Catalina,可以使用命令:catalina start
Catalina.bat 如 Listing17.9 所示:
Listing 17.9: The catalina.bat File
@echo off
if "%OS%" == "Windows_NT" setlocal
rem
---------------------------------------------------------------------------
rem Start/Stop Script for the CATALINA Server
rem
rem Environment Variable Prequisites
rem
rem CATALINA_HOME May point at your Catalina "build" directory.
rem
rem CATALINA_BASE (Optional) Base directory for resolving dynamic
portions
rem of a Catalina installation. If not present,
resolves to
rem the same directory that CATALINA_HOME points to.
rem
rem CATALINA_OPTS (Optional) Java runtime options used when the
"start",
rem "stop", or "run" command is executed.
rem
rem CATALINA_TMPDIR (Optional) Directory path location of temporary
directory
rem the JVM should use (java.io.tmpdir). Defaults to
rem %CATALINA_BASE%\temp.
rem
rem JAVA_HOME Must point at your Java Development Kit
installation.
rem
rem JAVA_OPTS (Optional) Java runtime options used