上回说到在HostConfig.start()会部署所有的项目:
public void start() {
if (log.isDebugEnabled())
log.debug(sm.getString("hostConfig.start"));
try {
ObjectName hostON = new ObjectName(host.getObjectName());
oname = new ObjectName
(hostON.getDomain() + ":type=Deployer,host=" + host.getName());
Registry.getRegistry(null, null).registerComponent
(this, oname, this.getClass().getName());
} catch (Exception e) {
log.error(sm.getString("hostConfig.jmx.register", oname), e);
}
if (host.getDeployOnStartup())
deployApps();
}
HostConfig.deployApps():
/**
* Deploy applications for any directories or WAR files that are found
* in our "application root" directory.
*/
protected void deployApps() {
File appBase = appBase();//webapps
File configBase = configBase();//conf/Catalina/localhost
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs, and loop if additional descriptors are found
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
}
HostConfig.deployDescriptors();是部署文件夹项目,deployWARs是部署war包的项目,主要过程一样,以HostConfig.deployWARs()为例:
protected void deployWARs(File appBase, String[] files) {
if (files == null)
return;
for (int i = 0; i < files.length; i++) {
if (files[i].equalsIgnoreCase("META-INF"))
continue;
if (files[i].equalsIgnoreCase("WEB-INF"))
continue;
File dir = new File(appBase, files[i]);
if (files[i].toLowerCase().endsWith(".war") && dir.isFile()
&& !invalidWars.contains(files[i]) ) {
// Calculate the context path and make sure it is unique
String contextPath = "/" + files[i].replace('#','/');
int period = contextPath.lastIndexOf(".");
contextPath = contextPath.substring(0, period);
// Check for WARs with /../ /./ or similar sequences in the name
if (!validateContextPath(appBase, contextPath)) {
log.error(sm.getString(
"hostConfig.illegalWarName", files[i]));
invalidWars.add(files[i]);
continue;
}
if (contextPath.equals("/ROOT"))
contextPath = "";
if (isServiced(contextPath))
continue;
String file = files[i];
deployWAR(contextPath, dir, file);
}
}
}
HostConfig.deployWAR():
protected void deployWAR(String contextPath, File war, String file) {
if (deploymentExists(contextPath))
return;
// Checking for a nested /META-INF/context.xml
JarFile jar = null;
JarEntry entry = null;
InputStream istream = null;
BufferedOutputStream ostream = null;
File xml = new File
(configBase, file.substring(0, file.lastIndexOf(".")) + ".xml");
if (deployXML && !xml.exists()) {
try {
jar = new JarFile(war);
entry = jar.getJarEntry(Constants.ApplicationContextXml);
if (entry != null) {
istream = jar.getInputStream(entry);
configBase.mkdirs();
ostream =
new BufferedOutputStream
(new FileOutputStream(xml), 1024);
byte buffer[] = new byte[1024];
while (true) {
int n = istream.read(buffer);
if (n < 0) {
break;
}
ostream.write(buffer, 0, n);
}
ostream.flush();
ostream.close();
ostream = null;
istream.close();
istream = null;
entry = null;
jar.close();
jar = null;
}
} catch (Exception e) {
// Ignore and continue
if (ostream != null) {
try {
ostream.close();
} catch (Throwable t) {
;
}
ostream = null;
}
if (istream != null) {
try {
istream.close();
} catch (Throwable t) {
;
}
istream = null;
}
} finally {
entry = null;
if (jar != null) {
try {
jar.close();
} catch (Throwable t) {
;
}
jar = null;
}
}
}
DeployedApplication deployedApp = new DeployedApplication(contextPath);
// Deploy the application in this WAR file
if(log.isInfoEnabled())
log.info(sm.getString("hostConfig.deployJar", file));
try {
Context context = null;
if (deployXML && xml.exists()) {
synchronized (digester) {
try {
context = (Context) digester.parse(xml);
if (context == null) {
log.error(sm.getString("hostConfig.deployDescriptor.error",
file));
return;
}
} finally {
digester.reset();
}
}
context.setConfigFile(xml.getAbsolutePath());
} else {
context = (Context) Class.forName(contextClass).newInstance();
}
// Populate redeploy resources with the WAR file
deployedApp.redeployResources.put
(war.getAbsolutePath(), new Long(war.lastModified()));
if (deployXML && xml.exists()) {
deployedApp.redeployResources.put
(xml.getAbsolutePath(), new Long(xml.lastModified()));
}
if (context instanceof Lifecycle) {
Class clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =
(LifecycleListener) clazz.newInstance();
((Lifecycle) context).addLifecycleListener(listener);
}
context.setPath(contextPath);
context.setDocBase(file);
host.addChild(context);
// If we're unpacking WARs, the docBase will be mutated after
// starting the context
if (unpackWARs && (context.getDocBase() != null)) {
String name = null;
String path = context.getPath();
if (path.equals("")) {
name = "ROOT";
} else {
if (path.startsWith("/")) {
name = path.substring(1);
} else {
name = path;
}
}
name = name.replace('/', '#');
File docBase = new File(name);
if (!docBase.isAbsolute()) {
docBase = new File(appBase(), name);
}
deployedApp.redeployResources.put(docBase.getAbsolutePath(),
new Long(docBase.lastModified()));
addWatchedResources(deployedApp, docBase.getAbsolutePath(), context);
} else {
addWatchedResources(deployedApp, null, context);
}
} catch (Throwable t) {
log.error(sm.getString("hostConfig.deployJar.error", file), t);
}
deployed.put(contextPath, deployedApp);
}
StandardHost.addChild():
/**
* Add a child Container, only if the proposed child is an implementation
* of Context.
*
* @param child Child container to be added
*/
public void addChild(Container child) {
if (child instanceof Lifecycle) {
((Lifecycle) child).addLifecycleListener(
new MemoryLeakTrackingListener());
}
if (!(child instanceof Context))
throw new IllegalArgumentException
(sm.getString("standardHost.notContext"));
super.addChild(child);
}
ContainerBase.addChild():
public void addChild(Container child) {
if (Globals.IS_SECURITY_ENABLED) {
PrivilegedAction dp =
new PrivilegedAddChild(child);
AccessController.doPrivileged(dp);
} else {
addChildInternal(child);
}
}
ContainerBase.addChildInternal():
private void addChildInternal(Container child) {
if( log.isDebugEnabled() )
log.debug("Add child " + child + " " + this);
synchronized(children) {
if (children.get(child.getName()) != null)
throw new IllegalArgumentException("addChild: Child name '" +
child.getName() +
"' is not unique");
child.setParent(this); // May throw IAE
children.put(child.getName(), child);
// Start child
if (started && startChildren && (child instanceof Lifecycle)) {
boolean success = false;
try {
((Lifecycle) child).start();
success = true;
} catch (LifecycleException e) {
log.error("ContainerBase.addChild: start: ", e);
throw new IllegalStateException
("ContainerBase.addChild: start: " + e);
} finally {
if (!success) {
children.remove(child.getName());
}
}
}
fireContainerEvent(ADD_CHILD_EVENT, child);
}
}
((Lifecycle) child).start();即StandardContext.start():
public synchronized void start() throws LifecycleException {
//if (lazy ) return;
if (started) {
if(log.isInfoEnabled())
log.info(sm.getString("containerBase.alreadyStarted", logName()));
return;
}
if( !initialized ) {
try {
init();
} catch( Exception ex ) {
throw new LifecycleException("Error initializaing ", ex);
}
}
if(log.isDebugEnabled())
log.debug("Starting " + ("".equals(getName()) ? "ROOT" : getName()));
// Set JMX object name for proper pipeline registration
preRegisterJMX();
if ((oname != null) &&
(Registry.getRegistry(null, null).getMBeanServer().isRegistered(oname))) {
// As things depend on the JMX registration, the context
// must be reregistered again once properly initialized
Registry.getRegistry(null, null).unregisterComponent(oname);
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
setAvailable(false);
setConfigured(false);
boolean ok = true;
// Add missing components as necessary
if (webappResources == null) { // (1) Required by Loader
if (log.isDebugEnabled())
log.debug("Configuring default Resources");
try {
if ((docBase != null) && (docBase.endsWith(".war")) && (!(new File(getBasePath())).isDirectory()))
setResources(new WARDirContext());
else
setResources(new FileDirContext());
} catch (IllegalArgumentException e) {
log.error("Error initializing resources: " + e.getMessage());
ok = false;
}
}
if (ok) {
if (!resourcesStart()) {
log.error( "Error in resourceStart()");
ok = false;
}
}
// Look for a realm - that may have been configured earlier.
// If the realm is added after context - it'll set itself.
// TODO: what is the use case for this ?
if( realm == null && mserver != null ) {
ObjectName realmName=null;
try {
realmName=new ObjectName( getEngineName() + ":type=Realm,host=" +
getHostname() + ",path=" + getPath());
if( mserver.isRegistered(realmName ) ) {
mserver.invoke(realmName, "init",
new Object[] {},
new String[] {}
);
}
} catch( Throwable t ) {
if(log.isDebugEnabled())
log.debug("No realm for this host " + realmName);
}
}
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
// Initialize character set mapper
getCharsetMapper();
// Post work directory
postWorkDirectory();
// Validate required extensions
boolean dependencyCheck = true;
try {
dependencyCheck = ExtensionValidator.validateApplication
(getResources(), this);
} catch (IOException ioe) {
log.error("Error in dependencyCheck", ioe);
dependencyCheck = false;
}
if (!dependencyCheck) {
// do not make application available if depency check fails
ok = false;
}
// Reading the "catalina.useNaming" environment variable
String useNamingProperty = System.getProperty("catalina.useNaming");
if ((useNamingProperty != null)
&& (useNamingProperty.equals("false"))) {
useNaming = false;
}
if (ok && isUseNaming()) {
if (namingContextListener == null) {
namingContextListener = new NamingContextListener();
namingContextListener.setName(getNamingContextName());
addLifecycleListener(namingContextListener);
}
}
// Standard container startup
if (log.isDebugEnabled())
log.debug("Processing standard container startup");
// Binding thread
ClassLoader oldCCL = bindThread();
boolean mainOk = false;
try {
if (ok) {
started = true;
// Start our subordinate components, if any
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();
// since the loader just started, the webapp classloader is now
// created.
// By calling unbindThread and bindThread in a row, we setup the
// current Thread CCL to be the webapp classloader
unbindThread(oldCCL);
oldCCL = bindThread();
// Initialize logger again. Other components might have used it too early,
// so it should be reset.
logger = null;
getLogger();
if ((logger != null) && (logger instanceof Lifecycle))
((Lifecycle) logger).start();
if ((cluster != null) && (cluster instanceof Lifecycle))
((Lifecycle) cluster).start();
if ((realm != null) && (realm instanceof Lifecycle))
((Lifecycle) realm).start();
if ((resources != null) && (resources instanceof Lifecycle))
((Lifecycle) resources).start();
// 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();
}
// Start the Valves in our pipeline (including the basic),
// if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(START_EVENT, null);
// Acquire clustered manager
Manager contextManager = null;
if (manager == null) {
if ( (getCluster() != null) && distributable) {
try {
contextManager = getCluster().createManager(getName());
} catch (Exception ex) {
log.error("standardContext.clusterFail", ex);
ok = false;
}
} else {
contextManager = new StandardManager();
}
}
// Configure default manager if none was specified
if (contextManager != null) {
setManager(contextManager);
}
if (manager!=null && (getCluster() != null) && distributable) {
//let the cluster know that there is a context that is distributable
//and that it has its own manager
getCluster().registerManager(manager);
}
mainOk = true;
}
} finally {
// Unbinding thread
unbindThread(oldCCL);
if (!mainOk) {
// An exception occurred
// Register with JMX anyway, to allow management
registerJMX();
}
}
if (!getConfigured()) {
log.error( "Error getConfigured");
ok = false;
}
// We put the resources into the servlet context
if (ok)
getServletContext().setAttribute
(Globals.RESOURCES_ATTR, getResources());
// Initialize associated mapper
mapper.setContext(getPath(), welcomeFiles, resources);
// Binding thread
oldCCL = bindThread();
// Set annotation processing parameter for Jasper (unfortunately, since
// this can be configured in many places and not just in /WEB-INF/web.xml,
// there are not many solutions)
// Initialize annotation processor
if (ok && !getIgnoreAnnotations()) {
if (annotationProcessor == null) {
if (isUseNaming() && namingContextListener != null) {
annotationProcessor =
new DefaultAnnotationProcessor(namingContextListener.getEnvContext());
} else {
annotationProcessor = new DefaultAnnotationProcessor(null);
}
}
getServletContext().setAttribute
(AnnotationProcessor.class.getName(), annotationProcessor);
}
try {
// Create context attributes that will be required
if (ok) {
postWelcomeFiles();
}
// Set up the context init params
mergeParameters();
if (ok) {
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}
// Configure and call application event listeners
if (ok) {
if (!listenerStart()) {
log.error( "Error listenerStart");
ok = false;
}
}
try {
// Start manager
if ((manager != null) && (manager instanceof Lifecycle)) {
((Lifecycle) getManager()).start();
}
// Start ContainerBackgroundProcessor thread
super.threadStart();
} catch(Exception e) {
log.error("Error manager.start()", e);
ok = false;
}
// Configure and call application filters
if (ok) {
if (!filterStart()) {
log.error( "Error filterStart");
ok = false;
}
}
// Load and initialize all "load on startup" servlets
if (ok) {
loadOnStartup(findChildren());
}
} finally {
// Unbinding thread
unbindThread(oldCCL);
}
// Set available status depending upon startup success
if (ok) {
if (log.isDebugEnabled())
log.debug("Starting completed");
setAvailable(true);
} else {
log.error(sm.getString("standardContext.startFailed", getName()));
try {
stop();
} catch (Throwable t) {
log.error(sm.getString("standardContext.startCleanup"), t);
}
setAvailable(false);
}
// JMX registration
registerJMX();
startTime=System.currentTimeMillis();
// Send j2ee.state.running notification
if (ok && (this.getObjectName() != null)) {
Notification notification =
new Notification("j2ee.state.running", this.getObjectName(),
sequenceNumber++);
broadcaster.sendNotification(notification);
}
// Close all JARs right away to avoid always opening a peak number
// of files on startup
if (getLoader() instanceof WebappLoader) {
((WebappLoader) getLoader()).closeJARs(true);
}
// Reinitializing if something went wrong
if (!ok && started) {
stop();
}
//cacheContext();
}
StandardContext.init():
public void init() throws Exception {
if( this.getParent() == null ) {
ObjectName parentName=getParentName();
if( ! mserver.isRegistered(parentName)) {
if(log.isDebugEnabled())
log.debug("No host, creating one " + parentName);
StandardHost host=new StandardHost();
host.setName(hostName);
host.setAutoDeploy(false);
Registry.getRegistry(null, null)
.registerComponent(host, parentName, null);
// We could do it the hard way...
//mserver.invoke(parentName, "init", new Object[] {}, new String[] {} );
// or same thing easier:
host.init();
}
// Add the main configuration listener
LifecycleListener config = null;
try {
String configClassName = null;
try {
configClassName = String.valueOf(mserver.getAttribute(parentName, "configClass"));
} catch (AttributeNotFoundException e) {
// Ignore, it's normal a host may not have this optional attribute
}
if (configClassName != null) {
Class clazz = Class.forName(configClassName);
config = (LifecycleListener) clazz.newInstance();
} else {
config = new ContextConfig();
}
} catch (Exception e) {
log.warn("Error creating ContextConfig for " + parentName, e);
throw e;
}
this.addLifecycleListener(config);
if (log.isDebugEnabled()) {
log.debug("AddChild " + parentName + " " + this);
}
try {
mserver.invoke(parentName, "addChild", new Object[] { this },
new String[] {"org.apache.catalina.Container"});
} catch (Exception e) {
destroy();
throw e;
}
// It's possible that addChild may have started us
if( initialized ) {
return;
}
}
if (processTlds) {
this.addLifecycleListener(new TldConfig());
}
super.init();
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(INIT_EVENT, null);
// Send j2ee.state.starting notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting",
this.getObjectName(),
sequenceNumber++);
broadcaster.sendNotification(notification);
}
}
lifecycle.fireLifecycleEvent(INIT_EVENT, null);LifecycleSupport.fireLifecycleEvent():
/**
* Notify all lifecycle event listeners that a particular event has
* occurred for this Container. The default implementation performs
* this notification synchronously using the calling thread.
*
* @param type Event type
* @param data Event data
*/
public void fireLifecycleEvent(String type, Object data) {
if (Lifecycle.INIT_EVENT.equals(type)) {
state = "INITIALIZED";
} else if (Lifecycle.BEFORE_START_EVENT.equals(type)) {
state = "STARTING_PREP";
} else if (Lifecycle.START_EVENT.equals(type)) {
state = "STARTING";
} else if (Lifecycle.AFTER_START_EVENT.equals(type)) {
state = "STARTED";
} else if (Lifecycle.BEFORE_STOP_EVENT.equals(type)) {
state = "STOPPING_PREP";
} else if (Lifecycle.STOP_EVENT.equals(type)) {
state = "STOPPING";
} else if (Lifecycle.AFTER_STOP_EVENT.equals(type)) {
state = "STOPPED";
} else if (Lifecycle.DESTROY_EVENT.equals(type)) {
state = "DESTROYED";
}
LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
LifecycleListener interested[] = listeners;
for (int i = 0; i < interested.length; i++)
interested[i].lifecycleEvent(event);
}
调用所有绑定的监听器的lifecycleEvent()方法,其中有ContextConfig.lifecycleEvent():
public void lifecycleEvent(LifecycleEvent event) {
// Identify the context we are associated with
try {
context = (Context) event.getLifecycle();
} catch (ClassCastException e) {
log.error(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(StandardContext.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(StandardContext.AFTER_START_EVENT)) {
// Restore docBase for management tools
if (originalDocBase != null) {
String docBase = context.getDocBase();
context.setDocBase(originalDocBase);
originalDocBase = docBase;
}
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
if (originalDocBase != null) {
String docBase = context.getDocBase();
context.setDocBase(originalDocBase);
originalDocBase = docBase;
}
stop();
} else if (event.getType().equals(Lifecycle.INIT_EVENT)) {
init();
} else if (event.getType().equals(Lifecycle.DESTROY_EVENT)) {
destroy();
}
}
INIT_EVENT参数调用的是ContextConfig.init():
protected void init() {
// Called from StandardContext.init()
if (webDigester == null){
webDigester = createWebDigester();
webDigester.getParser();
}
if (contextDigester == null){
contextDigester = createContextDigester();
contextDigester.getParser();
}
if (log.isDebugEnabled())
log.debug(sm.getString("contextConfig.init"));
context.setConfigured(false);
ok = true;
contextConfig();
try {
fixDocBase();
} catch (IOException e) {
log.error(sm.getString(
"contextConfig.fixDocBase", context.getPath()), e);
}
}
先设置web.xml、context.xml的解析规则,处理默认的配置文件。
createWebDigester()里调用WebRuleSet.addRuleInstances():
digester.addCallMethod(prefix + "web-app/listener/listener-class",
"addApplicationListener", 0);
Digester.addCallMethod()方法是解析到listenner-calss节点时,取其值当做addApplicationLister方法的参数。
StandardContext.addApplicationLister():
/**
* Add a new Listener class name to the set of Listeners
* configured for this application.
*
* @param listener Java class name of a listener class
*/
public void addApplicationListener(String listener) {
synchronized (applicationListenersLock) {
String results[] =new String[applicationListeners.length + 1];
for (int i = 0; i < applicationListeners.length; i++) {
if (listener.equals(applicationListeners[i])) {
log.info(sm.getString(
"standardContext.duplicateListener",listener));
return;
}
results[i] = applicationListeners[i];
}
results[applicationListeners.length] = listener;
applicationListeners = results;
}
fireContainerEvent("addApplicationListener", listener);
// FIXME - add instance if already started?
}
ContextConfig.fixDocBase():
protected void fixDocBase()
throws IOException {
Host host = (Host) context.getParent();
String appBase = host.getAppBase();
boolean unpackWARs = true;
if (host instanceof StandardHost) {
unpackWARs = ((StandardHost) host).isUnpackWARs()
&& ((StandardContext) context).getUnpackWAR();
}
File canonicalAppBase = new File(appBase);
if (canonicalAppBase.isAbsolute()) {
canonicalAppBase = canonicalAppBase.getCanonicalFile();
} else {
canonicalAppBase =
new File(System.getProperty("catalina.base"), appBase)
.getCanonicalFile();
}
String docBase = context.getDocBase();
if (docBase == null) {
// Trying to guess the docBase according to the path
String path = context.getPath();
if (path == null) {
return;
}
if (path.equals("")) {
docBase = "ROOT";
} else {
if (path.startsWith("/")) {
docBase = path.substring(1).replace('/', '#');
} else {
docBase = path.replace('/', '#');
}
}
}
File file = new File(docBase);
if (!file.isAbsolute()) {
docBase = (new File(canonicalAppBase, docBase)).getPath();
} else {
docBase = file.getCanonicalPath();
}
file = new File(docBase);
String origDocBase = docBase;
String pathName = context.getPath();
if (pathName.equals("")) {
pathName = "ROOT";
} else {
// Context path must start with '/'
pathName = pathName.substring(1).replace('/', '#');
}
if (docBase.toLowerCase().endsWith(".war") && !file.isDirectory() && unpackWARs) {
URL war = new URL("jar:" + (new File(docBase)).toURI().toURL() + "!/");
docBase = ExpandWar.expand(host, war, pathName);
file = new File(docBase);
docBase = file.getCanonicalPath();
if (context instanceof StandardContext) {
((StandardContext) context).setOriginalDocBase(origDocBase);
}
} else if (docBase.toLowerCase().endsWith(".war") &&
!file.isDirectory() && !unpackWARs) {
URL war =
new URL("jar:" + (new File (docBase)).toURI().toURL() + "!/");
ExpandWar.validate(host, war, pathName);
} else {
File docDir = new File(docBase);
if (!docDir.exists()) {
File warFile = new File(docBase + ".war");
if (warFile.exists()) {
URL war =
new URL("jar:" + warFile.toURI().toURL() + "!/");
if (unpackWARs) {
docBase = ExpandWar.expand(host, war, pathName);
file = new File(docBase);
docBase = file.getCanonicalPath();
} else {
docBase = warFile.getCanonicalPath();
ExpandWar.validate(host, war, pathName);
}
}
if (context instanceof StandardContext) {
((StandardContext) context).setOriginalDocBase(origDocBase);
}
}
}
if (docBase.startsWith(canonicalAppBase.getPath() + File.separatorChar)) {
docBase = docBase.substring(canonicalAppBase.getPath().length());
docBase = docBase.replace(File.separatorChar, '/');
if (docBase.startsWith("/")) {
docBase = docBase.substring(1);
}
} else {
docBase = docBase.replace(File.separatorChar, '/');
}
context.setDocBase(docBase);
}
解压war包,docBase = ExpandWar.expand(host, war, pathName);
/**
* Expand the WAR file found at the specified URL into an unpacked
* directory structure, and return the absolute pathname to the expanded
* directory.
*
* @param host Host war is being installed for
* @param war URL of the web application archive to be expanded
* (must start with "jar:")
* @param pathname Context path name for web application
*
* @exception IllegalArgumentException if this is not a "jar:" URL or if the
* WAR file is invalid
* @exception IOException if an input/output error was encountered
* during expansion
*/
public static String expand(Host host, URL war, String pathname)
throws IOException {
// Make sure that there is no such directory already existing
File appBase = new File(host.getAppBase());
if (!appBase.isAbsolute()) {
appBase = new File(System.getProperty("catalina.base"),
host.getAppBase());
}
if (!appBase.exists() || !appBase.isDirectory()) {
throw new IOException
(sm.getString("hostConfig.appBase",
appBase.getAbsolutePath()));
}
File docBase = new File(appBase, pathname);
if (docBase.exists()) {
// War file is already installed
return (docBase.getAbsolutePath());
}
// Create the new document base directory
docBase.mkdir();
// Expand the WAR into the new document base directory
String canonicalDocBasePrefix = docBase.getCanonicalPath();
if (!canonicalDocBasePrefix.endsWith(File.separator)) {
canonicalDocBasePrefix += File.separator;
}
JarURLConnection juc = (JarURLConnection) war.openConnection();
juc.setUseCaches(false);
JarFile jarFile = null;
InputStream input = null;
boolean success = false;
try {
jarFile = juc.getJarFile();
Enumeration jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = (JarEntry) jarEntries.nextElement();
String name = jarEntry.getName();
File expandedFile = new File(docBase, name);
if (!expandedFile.getCanonicalPath().startsWith(
canonicalDocBasePrefix)) {
// Trying to expand outside the docBase
// Throw an exception to stop the deployment
throw new IllegalArgumentException(
sm.getString("expandWar.illegalPath",war, name));
}
int last = name.lastIndexOf('/');
if (last >= 0) {
File parent = new File(docBase,
name.substring(0, last));
parent.mkdirs();
}
if (name.endsWith("/")) {
continue;
}
input = jarFile.getInputStream(jarEntry);
// Bugzilla 33636
expand(input, expandedFile);
long lastModified = jarEntry.getTime();
if ((lastModified != -1) && (lastModified != 0)) {
expandedFile.setLastModified(lastModified);
}
input.close();
input = null;
}
success = true;
} catch (IOException e) {
throw e;
} finally {
if (!success) {
// If something went wrong, delete expanded dir to keep things
// clean
deleteDir(docBase);
}
if (input != null) {
try {
input.close();
} catch (Throwable t) {
;
}
input = null;
}
if (jarFile != null) {
try {
jarFile.close();
} catch (Throwable t) {
;
}
jarFile = null;
}
}
// Return the absolute path to our new document base directory
return (docBase.getAbsolutePath());
}
初始化,配置都已完成,回到StandardContext.start():
lifecycle.fireLifecycleEvent(START_EVENT, null);
将调用ContextConfig.start():
protected synchronized void start() {
// Called from StandardContext.start()
if (log.isDebugEnabled())
log.debug(sm.getString("contextConfig.start"));
// Set properties based on DefaultContext
Container container = context.getParent();
if( !context.getOverride() ) {
if( container instanceof Host ) {
// Reset the value only if the attribute wasn't
// set on the context.
xmlValidation = context.getXmlValidation();
if (!xmlValidation) {
xmlValidation = ((Host)container).getXmlValidation();
}
xmlNamespaceAware = context.getXmlNamespaceAware();
if (!xmlNamespaceAware){
xmlNamespaceAware
= ((Host)container).getXmlNamespaceAware();
}
container = container.getParent();
}
}
// Process the default and application web.xml files
defaultWebConfig();
applicationWebConfig();
if (!context.getIgnoreAnnotations()) {
applicationAnnotationsConfig();
}
if (ok) {
validateSecurityRoles();
}
// Configure an authenticator if we need one
if (ok)
authenticatorConfig();
// Dump the contents of this pipeline if requested
if ((log.isDebugEnabled()) && (context instanceof ContainerBase)) {
log.debug("Pipeline Configuration:");
Pipeline pipeline = ((ContainerBase) context).getPipeline();
Valve valves[] = null;
if (pipeline != null)
valves = pipeline.getValves();
if (valves != null) {
for (int i = 0; i < valves.length; i++) {
log.debug(" " + valves[i].getInfo());
}
}
log.debug("======================");
}
// Make our application available if no problems were encountered
if (ok)
context.setConfigured(true);
else {
log.error(sm.getString("contextConfig.unavailable"));
context.setConfigured(false);
}
}
ContextConfig.applicationWebConfig():
/**
* Process the application configuration file, if it exists.
*/
protected void applicationWebConfig() {
String altDDName = null;
// Open the application web.xml file, if it exists
InputStream stream = null;
ServletContext servletContext = context.getServletContext();
if (servletContext != null) {
altDDName = (String)servletContext.getAttribute(
Globals.ALT_DD_ATTR);
if (altDDName != null) {
try {
stream = new FileInputStream(altDDName);
} catch (FileNotFoundException e) {
log.error(sm.getString("contextConfig.altDDNotFound",
altDDName));
}
}
else {
stream = servletContext.getResourceAsStream
(Constants.ApplicationWebXml);
}
}
if (stream == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.applicationMissing") + " " + context);
}
return;
}
long t1=System.currentTimeMillis();
URL url=null;
// Process the application web.xml file
synchronized (webDigester) {
try {
if (altDDName != null) {
url = new File(altDDName).toURL();
} else {
url = servletContext.getResource(
Constants.ApplicationWebXml);
}
if( url!=null ) {
InputSource is = new InputSource(url.toExternalForm());
is.setByteStream(stream);
if (context instanceof StandardContext) {
((StandardContext) context).setReplaceWelcomeFiles(true);
}
webDigester.push(context);
webDigester.setErrorHandler(new ContextErrorHandler());
if(log.isDebugEnabled()) {
log.debug("Parsing application web.xml file at " + url.toExternalForm());
}
webDigester.parse(is);
if (parseException != null) {
ok = false;
}
} else {
log.info("No web.xml, using defaults " + context );
}
} catch (SAXParseException e) {
log.error(sm.getString("contextConfig.applicationParse", url.toExternalForm()), e);
log.error(sm.getString("contextConfig.applicationPosition",
"" + e.getLineNumber(),
"" + e.getColumnNumber()));
ok = false;
} catch (Exception e) {
log.error(sm.getString("contextConfig.applicationParse", url.toExternalForm()), e);
ok = false;
} finally {
webDigester.reset();
parseException = null;
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
log.error(sm.getString("contextConfig.applicationClose"), e);
}
}
}
webRuleSet.recycle();
long t2=System.currentTimeMillis();
if (context instanceof StandardContext) {
((StandardContext) context).setStartupTime(t2-t1);
}
}
解析规则的栈底将是StandardContext对象,解析web.xml
webDigester.push(context);
webDigester.parse(is);
启动web.xml配置文件中的listener:
// Configure and call application event listeners
if (ok) {
if (!listenerStart()) {
log.error( "Error listenerStart");
ok = false;
}
}
StandardContext.listenerStart():
/**
* Configure the set of instantiated application event listeners
* for this Context. Return <code>true</code> if all listeners wre
* initialized successfully, or <code>false</code> otherwise.
*/
public boolean listenerStart() {
if (log.isDebugEnabled())
log.debug("Configuring application event listeners");
// Instantiate the required listeners
ClassLoader loader = getLoader().getClassLoader();
String listeners[] = findApplicationListeners();
Object results[] = new Object[listeners.length];
boolean ok = true;
for (int i = 0; i < results.length; i++) {
if (getLogger().isDebugEnabled())
getLogger().debug(" Configuring event listener class '" +
listeners[i] + "'");
try {
Class clazz = loader.loadClass(listeners[i]);
results[i] = clazz.newInstance();
// Annotation processing
if (!getIgnoreAnnotations()) {
getAnnotationProcessor().processAnnotations(results[i]);
getAnnotationProcessor().postConstruct(results[i]);
}
} catch (Throwable t) {
getLogger().error
(sm.getString("standardContext.applicationListener",
listeners[i]), t);
ok = false;
}
}
if (!ok) {
getLogger().error(sm.getString("standardContext.applicationSkipped"));
return (false);
}
// Sort listeners in two arrays
ArrayList eventListeners = new ArrayList();
ArrayList lifecycleListeners = new ArrayList();
for (int i = 0; i < results.length; i++) {
if ((results[i] instanceof ServletContextAttributeListener)
|| (results[i] instanceof ServletRequestAttributeListener)
|| (results[i] instanceof ServletRequestListener)
|| (results[i] instanceof HttpSessionAttributeListener)) {
eventListeners.add(results[i]);
}
if ((results[i] instanceof ServletContextListener)
|| (results[i] instanceof HttpSessionListener)) {
lifecycleListeners.add(results[i]);
}
}
setApplicationEventListeners(eventListeners.toArray());
setApplicationLifecycleListeners(lifecycleListeners.toArray());
// Send application start events
if (getLogger().isDebugEnabled())
getLogger().debug("Sending application start events");
Object instances[] = getApplicationLifecycleListeners();
if (instances == null)
return (ok);
ServletContextEvent event =
new ServletContextEvent(getServletContext());
for (int i = 0; i < instances.length; i++) {
if (instances[i] == null)
continue;
if (!(instances[i] instanceof ServletContextListener))
continue;
ServletContextListener listener =
(ServletContextListener) instances[i];
try {
fireContainerEvent("beforeContextInitialized", listener);
listener.contextInitialized(event);
fireContainerEvent("afterContextInitialized", listener);
} catch (Throwable t) {
fireContainerEvent("afterContextInitialized", listener);
getLogger().error
(sm.getString("standardContext.listenerStart",
instances[i].getClass().getName()), t);
ok = false;
}
}
return (ok);
}
启动web.xml配置文件中的filter:
// Configure and call application filters
if (ok) {
if (!filterStart()) {
log.error( "Error filterStart");
ok = false;
}
}
StandardContext.filterStart():
/**
* Configure and initialize the set of filters for this Context.
* Return <code>true</code> if all filter initialization completed
* successfully, or <code>false</code> otherwise.
*/
public boolean filterStart() {
if (getLogger().isDebugEnabled())
getLogger().debug("Starting filters");
// Instantiate and record a FilterConfig for each defined filter
boolean ok = true;
synchronized (filterConfigs) {
filterConfigs.clear();
Iterator names = filterDefs.keySet().iterator();
while (names.hasNext()) {
String name = (String) names.next();
if (getLogger().isDebugEnabled())
getLogger().debug(" Starting filter '" + name + "'");
ApplicationFilterConfig filterConfig = null;
try {
filterConfig = new ApplicationFilterConfig
(this, (FilterDef) filterDefs.get(name));
filterConfigs.put(name, filterConfig);
} catch (Throwable t) {
getLogger().error
(sm.getString("standardContext.filterStart", name), t);
ok = false;
}
}
}
return (ok);
}
其实就是创建Filter配置信息的对象,并未创建Filter实例。而至于要start多少监听器,那就得看项目的web.xml了。