Tomcat是Apache Jakarta软件组织的一个子项目,Tomcat是一个JSP/Servlet容器,它是在SUN公司的JSWDK
(Java Server Web Development Kit)基础上发展起来的一个JSP和Servlet规范的标准实现。
Tomcat和IIS、Apache等Web服务器一样,具有处理HTML页面的功能,不过,Tomcat处理静态HTML的能力不
如Apache服务器
<Server port="8005" shutdown="SHUTDOWN" debug="0"> <Service name="Tomcat-Standalone"> <!-- HTTP/1.1--> <Connector className="org.apache.coyote.tomcat4.CoyoteConnector" port="8080"..... <!--Coyote/JK2--> <Connector className="org.apache.coyote.tomcat4.CoyoteConnector" port="8009"..... <Engine name="Standalone" defaultHost="localhost" debug="0"> <Logger className="org.apache.catalina.logger.FileLogger" .../> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" .../> ..... <Host name="localhost" debug="0" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="mycontext" debug="0"/> <Context path="/wsota" docBase="wsotaProject" debug="0"/> </Host> <Host name="www.taobao.com"...... </Engine> </Service> <Service name=“Tomcat-demo”>…. </Service> </Server>
各组件基本介绍:
Container:
可以理解为处理某类型请求的容器,处理的方式一般为把处理请求的处理器包装为Valve对象,并按一定顺序放入类型为Pipeline的管道里。 Container里包含一些基础服务,如Loader、Manager和Realm。
包括以下类型:
Engine:
Engine包含Host和Context,接到请求后仍给相应的Host在相应的Context里处理。
Host:
Engine下可以配置多个虚拟主机Virtual Host,每个虚拟主机都有一个域名
当Engine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理
Engine有一个默认虚拟主机,当请求无法匹配到任何一个Host上的时候,将交给该默认Host来处理
Context:
一个Context对应于一个Web Application,一个Web Application由一个或者多个Servlet组成 Context在创建的时候将根据配置文件$CATALINA_HOME/conf/web.xml$WEBAPP_HOME/WEB-INF/web.xml载入Servlet类
当Context获得请求时,将在自己的映射表(mapping table)中寻找相匹配的Servlet类
Wrapper:
Wrapper是针对每个Servlet的Container,每个Servlet都有相应的Wrapper来管理。
包括以下类型:
Engine:
Engine包含Host和Context,接到请求后仍给相应的Host在相应的Context里处理。
Host:
Engine下可以配置多个虚拟主机Virtual Host,每个虚拟主机都有一个域名
当Engine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理
Engine有一个默认虚拟主机,当请求无法匹配到任何一个Host上的时候,将交给该默认Host来处理
Context:
一个Context对应于一个Web Application,一个Web Application由一个或者多个Servlet组成 Context在创建的时候将根据配置文件$CATALINA_HOME/conf/web.xml$WEBAPP_HOME/WEB-INF/web.xml载入Servlet类
当Context获得请求时,将在自己的映射表(mapping table)中寻找相匹配的Servlet类
Wrapper:
Wrapper是针对每个Servlet的Container,每个Servlet都有相应的Wrapper来管理。
1.listen port,create socket,get data
2.create a request objects whose class implements the org.apache.catalina.Request interface
3.create a response objects whose class implements the org.apache.catalina.Response interface
---------------------------------------------
Class Implementation of an HTTP/1.1 connector
1)Tomcat4: HttpConnector(简单,定下了整个架构的基调)
2)CoyoteConnector
3)Tomcat5: Connector
类图:
interface:
1.public Container getParent();
2.public void setParent(Container container);
3.public void addChild(Container child);
4.public Container[] findChildren();
5.public void invoke(Request request, Response response)
throws IOException, ServletException;
例子:
Ext: … Wrapper wrapper1 = new StandardWrapper(); Wrapper wrapper2 = new StandardWrapper(); Context context = new StandardContext(); context.addChild(wrapper1); context.addChild(wrapper2); Host host = new StandardHost(); host.addChild(context); host.setName("localhost"); host.setAppBase("webapps"); …
interface:
public interface Pipeline { public Valve getBasic(); public void setBasic(Valve valve); public void addValve(Valve valve); public Valve[] getValves(); public void invoke(Request request, Response response) throws IOException, ServletException; public void removeValve(Valve valve); } public interface Valve { public String getInfo(); public void invoke(Request request, Response response, ValveContext context) throws IOException, ServletException; }
pipeline如何工作:
//1.pipeline value public void invokeNext(Request request, Response response) throws IOException, ServletException { int subscript = stage; stage = stage + 1;//index 指针 // Invoke the requested Valve for the current request thread if (subscript < valves.length) { valves[subscript].invoke(request, response, this);//invoke next if has } else if ((subscript == valves.length) && (basic != null)) { basic.invoke(request, response, this);//last is basic } else { } } //2.value invoke public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { …. // Pass this request on to the next valve in our pipeline valveContext.invokeNext(request, response); …… }例子:
Valve valve1 = new HeaderLoggerValve(); Valve valve2 = new ClientIPLoggerValve(); ((Pipeline) context).addValve(valve1); ------1.pipeline.addValve(valve1); ((Pipeline) context).addValve(valve2); ------2.pipeline.addValve(valve2); public StandardContext() { super(); pipeline.setBasic(new StandardContextValve()); namingResources.setContainer(this); } ----- 3.basic value = StandardContextValve() Result: Value[]:value1,value2 basicValue: StandardContextValve
tomcat例子:
1. connector组装完request, response… connector.getContainer().invoke(request, response); 2.container的invoke(),如StandardContext public void invoke(Request request, Response response) throws IOException, ServletException { pipeline.invoke(request, response); } 3. HeaderLoggerValve invoke public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { // Pass this request on to the next valve in our pipeline valveContext.invokeNext(request, response); System.out.println("Header Logger Valve"); … } 4. ClientIPLoggerValve invoke public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { // Pass this request on to the next valve in our pipeline valveContext.invokeNext(request, response); System.out.println("Client IP Logger Valve"); … } 5. StandardContextValve invoke public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { …. response.setContext(context); wrapper.invoke(request, response); //执行下一个container的任务 }Lifecycle
interface:
public interface Lifecycle { public static final String START_EVENT = "start"; public static final String BEFORE_START_EVENT = "before_start"; public static final String AFTER_START_EVENT = "after_start"; public static final String STOP_EVENT = "stop"; public static final String BEFORE_STOP_EVENT = "before_stop"; public static final String AFTER_STOP_EVENT = "after_stop"; public void addLifecycleListener(LifecycleListener listener); public LifecycleListener[] findLifecycleListeners(); public void removeLifecycleListener(LifecycleListener listener); public void start() throws LifecycleException; public void stop() throws LifecycleException; }lifecycle原理:
Tomcat 中组件的生命周期是通过 Lifecycle 接口来控制的,组件只要继承这个接口并实现其中的方法就可以统一被拥有它的组件控制了,这样一层一层的直到一个最高级的组件就可以控制 Tomcat 中所有组件的生命周期,这个最高的组件就是 Server
public abstract class ContainerBase implements Container, Lifecycle, Pipeline
Lifecycle start&stop:
->((Lifecycle) connector).start()
->((Lifecycle) host).start()
->((Lifecycle) context).start()
->((Lifecycle) Wrapper).start()
->….
Start Code:
…… first start self container
// Start our child containers, if any
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Lifecycle)
((Lifecycle) children[i]).start();
}
Stop:
->((Lifecycle) Wrapper). end()
->((Lifecycle) context). end()
->((Lifecycle) host). end()
->((Lifecycle) connector).end()
->….
Stop code:
// Stop our child containers, if any
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Lifecycle)
((Lifecycle) children[i]).stop();
}
…… end stop self container
Ext code:
StandardContext.start()
StandardContext.stop()
LifecycleListener
interface and support class:
public interface LifecycleListener {
public void lifecycleEvent(LifecycleEvent event);
}
public final class LifecycleEvent
extends EventObject {
public LifecycleEvent(Lifecycle lifecycle, String type) {
this(lifecycle, type, null);
}
……
}
public final class LifecycleSupport {
public void addLifecycleListener(LifecycleListener listener)
public void fireLifecycleEvent(String type, Object data)
public void removeLifecycleListener(LifecycleListener listener)
……
}
类图:
LifecycleListener例子:
1:init
Context context = new StandardContext();
LifecycleListener listener = new ContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
//1.protected LifecycleSupport lifecycle = new LifecycleSupport(this);
//2. lifecycle.addLifecycleListener(listener);
2:start
StandardContext.start()
public synchronized void start() throws LifecycleException {
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
Start ….
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}
3:event
public void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
LifecycleListener interested[] = null;
synchronized (listeners) {
interested = (LifecycleListener[]) listeners.clone();
}
for (int i = 0; i < interested.length; i++)
interested[i].lifecycleEvent(event);
}
4: ContextConfig. lifecycleEvent
public void lifecycleEvent(LifecycleEvent event) {
// Identify the context we are associated with
try {
context = (Context) event.getLifecycle();
if (context instanceof StandardContext) {
int contextDebug = ((StandardContext) context).getDebug();
if (contextDebug > this.debug)
this.debug = contextDebug;
}
} catch (ClassCastException e) {
log(sm.getString("contextConfig.cce", event.getLifecycle()), e);
return;
}
// Process the event that has occurred
if (event.getType().equals(Lifecycle.START_EVENT))
start();
else if (event.getType().equals(Lifecycle.STOP_EVENT))
stop();
}
tomcat处理请求过程
基本过程:
官方:requestProcess.pdf
见:serverStartup.pdf
Tomcat Server的ClassLoader结构如下:
Bootstrap
|
System
|
Common
/ \
Catalina Shared
/ \
Webapp1 Webapp2 ...
其中:
- Bootstrap - 载入JVM自带的类和$JAVA_HOME/jre/lib/ext/*.jar
- System - 载入$CLASSPATH/*.class,$CATALINA_HOME/bin/bootstrap.jar...
- Common - 载入$CATALINA_HOME/common/...,它们对TOMCAT和所有的WEB APP都可见
- Catalina - 载入$CATALINA_HOME/server/...,它们仅对TOMCAT可见,对所有的WEB APP都不可见
- Shared - 载入$CATALINA_HOME/shared/...,它们仅对所有WEB APP可见,对TOMCAT不可见(也不必见)
- WebApp - 载入ContextBase/WEB-INF/classes...,ContextBase/WEB-INF/lib;它们仅对该WEB APP可见
ClassLoader被组织成树形,一般的工作原理是:
1) 线程需要用到某个类,于是contextClassLoader被请求来载入该类
2) contextClassLoader请求它的父ClassLoader来完成该载入请求
3) 如果父ClassLoader无法载入类,则contextClassLoader试图自己来载入
4) 都无法载入,报classNotFund
但是WebAppClassLoader的工作原理和上述有少许不同:
它先试图自己载入类,如果无法载入,再请求父ClassLoader完成
(自己的载入的类从下面目录拿:ContextBase/WEB-INF/classes,ContextBase/WEB-INF/lib)
创建3个loader,并且建立父子关系:
unpacked[0] = new File("D:\\ppt\\apache-tomcat-5.5.20\\apache-tomcat-5.5.20\\common\\classes"); packed2[0] = new File("D:\\ppt\\apache-tomcat-5.5.20\\apache-tomcat-5.5.20\\common\\endorsed"); packed2[1] = new File("D:\\ppt\\apache-tomcat-5.5.20\\apache-tomcat-5.5.20\\common\\lib");
ClassLoader commonLoader = null; ClassLoader catalinaLoader = null; ClassLoader sharedLoader = null; commonLoader = ClassLoaderFactory.createClassLoader(unpacked, packed2, null); catalinaLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader); sharedLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader);
createLoader实现代码,可以看见tomcat中Loader都是StandardLoader,但是WebappLoader并不是,因为两者的类加载机制不同:
classLoader = new StandardClassLoader(array, parent); public class StandardClassLoader extends URLClassLoader implements Reloader public StandardClassLoader(String repositories[]) { super(convert(repositories)); this.parent = getParent();//父类 this.system = getSystemClassLoader();//system securityManager = System.getSecurityManager(); if (repositories != null) { for (int i = 0; i < repositories.length; i++) addRepositoryInternal(repositories[i]); } }
WebappLoader与sharedLoader 关系如何建立?
Class startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); String methodName = "setParentClassLoader"; Class paramTypes[] = new Class[1]; paramTypes[0] = Class.forName("java.lang.ClassLoader"); Object paramValues[] = new Object[1]; paramValues[0] = sharedLoader;//定义parent Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues);
说明:systemLoader创建完3个loader后,马上将classLoader切换至catalinaLoader,
而catalinaLoader将负责加载tomcat的组件类,如Catalina,standardServer,standardContext,包括WebappLoader;
但是webappLoader初始化ok后,去Load应用的时候,将自己去加载,加载不了由父加载器加载,而父亲加载器就是上述组件的父加载器:sharedLoader
webappLoader代码:
public class WebappLoader implements Lifecycle, Loader, PropertyChangeListener, Runnable public WebappLoader(ClassLoader parent) { super(); this.parentClassLoader = parent;//parent is sharedLoader } //创建WebAppclassLoader classLoader = createClassLoader(); classLoader.setResources(container.getResources()); //loaderClass: //String loaderClass = "org.apache.catalina.loader.WebappClassLoader"; private WebappClassLoader createClassLoader() throws Exception { Class clazz = Class.forName(loaderClass); WebappClassLoader classLoader = null; if (parentClassLoader == null) { // Will cause a ClassCast is the class does not extend WCL, but // this is on purpose (the exception will be caught and rethrown) classLoader = (WebappClassLoader) clazz.newInstance(); } else { Class[] argTypes = { ClassLoader.class }; Object[] args = { parentClassLoader }; Constructor constr = clazz.getConstructor(argTypes); classLoader = (WebappClassLoader) constr.newInstance(args); } return classLoader; }
如何更改当前线程的classloader:
Thread.currentThread().setContextClassLoader(catalinaLoader);
1.tomcat之前是否已经加载缓存过,如果加载过的话,直接返回,如servletClass;
ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
2.查看该classLoader之前是否在家,jvm中有缓存class,如果有直接返回class;
3.用systemLoader去加载,如果J2SE。J2EE的jar包,此时可以直接加载返回;
4.到这里加载还不能成功,需要用securityManager来判断是否可以记载;
5.没办法,webappclassLoader来加载吧,如servlet class;
6.还不行,交parentClassLoader来记载,shareClassLoader;加载不到,返回CNE;
public interface Deployer { public static final String PRE_INSTALL_EVENT = "pre-install"; public static final String INSTALL_EVENT = "install"; public static final String REMOVE_EVENT = "remove"; // --------------------------------------------------------- Public Methods public String getName(); //部署 public void install(String contextPath, URL war) throws IOException; //部署 public void install(URL config, URL war) throws IOException; public Context findDeployedApp(String contextPath); public String[] findDeployedApps(); public void remove(String contextPath) throws IOException; public void start(String contextPath) throws IOException; public void stop(String contextPath) throws IOException; }
a:会把2个目录里面部署:
1。webapps
2。server/webapps
webapps部署自定义的应用,以war包,文件等形式;server/webapps部署后台管理应用,serlvetManager和hostManager。
b:部署的核心
部署的核心都是从web.xml开始,定位到应用目录后,开始解析web.xml文件,实例化出servlet,filter及各种变量等。除了解析应用的web.xml,tomcat自带默认的web.xml,目录conf/web.xml,创建出常用的3个serlvet:DefaultServlet,InvokerServlet,JspServlet
DefaultServlet:无法映射到servlet的请求,全部由它处理
InvokerServlet:未在web.xml的servlet,通过/servlet/*来访问
JspServlet:处理*.jsp
public static void main(String[] args) { System.setProperty("catalina.base", System.getProperty("user.dir")); Connector connector = new HttpConnector(); //Context Context context = new StandardContext(); LifecycleListener listener = new ContextConfig(); ((Lifecycle) context).addLifecycleListener(listener); //Host Host host = new StandardHost(); host.addChild(context); host.setName("localhost"); host.setAppBase("webapps"); LifecycleListener listener2 = new HostConfig(); ((Lifecycle) host).addLifecycleListener(listener2); //engine Engine engine = new StandardEngine(); engine.addChild(host); engine.setName("localhost"); Mapper mapper = new StandardEngineMapper(); mapper.setProtocol("http"); engine.addMapper(mapper); Loader loader = new WebappLoader(); context.setLoader(loader); connector.setContainer(engine); try { connector.initialize(); ((Lifecycle) connector).start(); ((Lifecycle) host).start(); Container[] c = context.findChildren(); int length = c.length; for (int i=0; i<length; i++) { Container child = c[i]; System.out.println(child.getName()); } System.in.read(); ((Lifecycle) host).stop(); } catch (Exception e) { e.printStackTrace(); } }
StandardSession包含大量底层方法,不适合暴露给给servlet,采用装饰模式给封装下:StandardSessionFacade
只包含javax.servlet.http.HttpSession方法
public HttpSession getSession() { if (facade == null) facade = new StandardSessionFacade(this); return (facade); }
public interface Manager { public Container getContainer(); public void setContainer(Container container); public DefaultContext getDefaultContext(); public void setDefaultContext(DefaultContext defaultContext); public boolean getDistributable(); public void setDistributable(boolean distributable); public String getInfo(); public int getMaxInactiveInterval(); public void setMaxInactiveInterval(int interval); public void add(Session session); public void addPropertyChangeListener(PropertyChangeListener listener); //创建一个session public Session createSession(); //map里面查找session public Session findSession(String id) throws IOException; public Session[] findSessions(); //启动的时候,从本地文件加载已有的session public void load() throws ClassNotFoundException, IOException; //移除一个session public void remove(Session session); public void removePropertyChangeListener(PropertyChangeListener listener); //tomcat关闭,还存活的session序列化入本地文件 public void unload() throws IOException; }
StandardManager启用一个map来维护session:
protected HashMap sessions = new HashMap();
所有session存放在内存中,都有一个最大存活时间,默认是60s;后台一个线程轮训监控着,超时的session将会销毁并回收。对于session的获取,也只需从map里面get即可。
PersistentManagerBase也是用map来维护session;但是相对standardManager来说,session的管理稍微复杂,对于超时的会销毁;同时若session同时存活数很大的话或者某些比较老的session,支持序列化到硬盘文件和数据库,释放内存压力;但同时session的获取会比较麻烦,map去完,还得硬盘文件中查找,并反序列化为对象。
以文件存储为例:
../server/webapps/manager
server目录下面部署着后台程序manager应用
webapps目录下面有manager.xml
<Context path="/manager" docBase="../server/webapps/manager" debug="0" privileged="true"> <!-- Link to the user database we will get roles from --> <ResourceLink name="users" global="UserDatabase" type="org.apache.catalina.UserDatabase"/> </Context>
tomcat部署时,该应用也会部署。
manager应用的web.xml:
<servlet> <servlet-name>Manager</servlet-name> <servlet-class>org.apache.catalina.servlets.ManagerServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> </servlet> <servlet> <servlet-name>HTMLManager</servlet-name> <servlet-class>org.apache.catalina.servlets.HTMLManagerServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> </servlet>
管理serlvet和html,看下ManagerServlet:
接口ContainerSerlvet
public interface ContainerServlet { // ------------------------------------------------------------- Properties /** * Return the Wrapper with which this Servlet is associated. */ public Wrapper getWrapper(); /** * Set the Wrapper with which this Servlet is associated. * * @param wrapper The new associated Wrapper */ public void setWrapper(Wrapper wrapper); }
类ManagerServlet:
public class ManagerServlet extends HttpServlet implements ContainerServlet { //....忽略 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { //...乎略 if (command == null) { writer.println(sm.getString("managerServlet.noCommand")); } else if (command.equals("/install")) { install(writer, config, path, war); } else if (command.equals("/list")) { list(writer); } else if (command.equals("/reload")) { reload(writer, path); } else if (command.equals("/remove")) { remove(writer, path); } else if (command.equals("/resources")) { resources(writer, type); } else if (command.equals("/roles")) { roles(writer); } else if (command.equals("/sessions")) { sessions(writer, path); } else if (command.equals("/start")) { start(writer, path); } else if (command.equals("/stop")) { stop(writer, path); } else if (command.equals("/undeploy")) { undeploy(writer, path); } else { writer.println(sm.getString("managerServlet.unknownCommand", command)); } } //.....忽略 }
比如启动应用:
protected void start(PrintWriter writer, String path) { if (debug >= 1) log("start: Starting web application at '" + path + "'"); if ((path == null) || (!path.startsWith("/") && path.equals(""))) { writer.println(sm.getString("managerServlet.invalidPath", path)); return; } String displayPath = path; if( path.equals("/") ) path = ""; try { //找到需要启动的应用 Context context = deployer.findDeployedApp(path); if (context == null) { writer.println(sm.getString("managerServlet.noContext", displayPath)); return; } //应用启动 deployer.start(path); if (context.getAvailable()) writer.println (sm.getString("managerServlet.started", displayPath)); else writer.println (sm.getString("managerServlet.startFailed", displayPath)); } catch (Throwable t) { getServletContext().log (sm.getString("managerServlet.startFailed", displayPath), t); writer.println (sm.getString("managerServlet.startFailed", displayPath)); writer.println(sm.getString("managerServlet.exception", t.toString())); } }
Runtime类封装了运行时的环境。每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。
一般不能实例化一个Runtime对象,应用程序也不能创建自己的 Runtime 类实例,但可以通过 getRuntime 方法获取当前Runtime运行时对象的引用。
一旦得到了一个当前的Runtime对象的引用,就可以调用Runtime对象的方法去控制Java虚拟机的状态和行为。
addShutdownHook(Thread hook) :注册新的虚拟机来关闭挂钩。
availableProcessors() :向 Java 虚拟机返回可用处理器的数目。
exec(String command) :在单独的进程中执行指定的字符串命令。
gc() :运行垃圾回收器
getRuntime() :返回与当前 Java 应用程序相关的运行时对象。
removeShutdownHook(Thread hook) :取消注册某个先前已注册的虚拟机关闭挂钩。
。。。
/** * Shutdown hook which will perform a clean shutdown of Catalina if needed. */ protected class CatalinaShutdownHook extends Thread { public void run() { if (server != null) { try { ((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); } } } } }
server start:
Thread shutdownHook = new CatalinaShutdownHook(); // Start the new server if (server instanceof Lifecycle) { try { server.initialize(); ((Lifecycle) server).start(); try { // Register shutdown hook //look here 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); } } }
server stop:
// 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); } } }
后续学习:jsp编译,https,http ssl,tomcat6,7。