blob: c63f2602fcd8f926781d17498b0fb06d75752860 [file] [log] [blame]
/*
* Copyright 1999,2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.startup;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.management.ObjectName;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.core.ContainerBase;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.util.StringManager;
import org.apache.commons.modeler.Registry;
import org.apache.tomcat.util.digester.Digester;
/**
* Startup event listener for a <b>Host</b> that configures the properties
* of that Host, and the associated defined contexts.
*
* @author Craig R. McClanahan
* @author Remy Maucherat
* @version $Revision$ $Date$
*/
public class HostConfig
implements LifecycleListener {
protected static org.apache.commons.logging.Log log=
org.apache.commons.logging.LogFactory.getLog( HostConfig.class );
// ----------------------------------------------------- Instance Variables
/**
* App base.
*/
protected File appBase = null;
/**
* Config base.
*/
protected File configBase = null;
/**
* The Java class name of the Context configuration class we should use.
*/
protected String configClass = "org.apache.catalina.startup.ContextConfig";
/**
* The Java class name of the Context implementation we should use.
*/
protected String contextClass = "org.apache.catalina.core.StandardContext";
/**
* The Host we are associated with.
*/
protected Host host = null;
/**
* The JMX ObjectName of this component.
*/
protected ObjectName oname = null;
/**
* The string resources for this package.
*/
protected static final StringManager sm =
StringManager.getManager(Constants.Package);
/**
* Should we deploy XML Context config files?
*/
protected boolean deployXML = false;
/**
* Should we unpack WAR files when auto-deploying applications in the
* <code>appBase</code> directory?
*/
protected boolean unpackWARs = false;
/**
* Map of deployed applications.
*/
protected HashMap deployed = new HashMap();
/**
* List of applications which are being serviced, and shouldn't be
* deployed/undeployed/redeployed at the moment.
*/
protected ArrayList serviced = new ArrayList();
/**
* Attribute value used to turn on/off XML validation
*/
protected boolean xmlValidation = false;
/**
* Attribute value used to turn on/off XML namespace awarenes.
*/
protected boolean xmlNamespaceAware = false;
/**
* The <code>Digester</code> instance used to parse context descriptors.
*/
protected static Digester digester = createDigester();
// ------------------------------------------------------------- Properties
/**
* Return the Context configuration class name.
*/
public String getConfigClass() {
return (this.configClass);
}
/**
* Set the Context configuration class name.
*
* @param configClass The new Context configuration class name.
*/
public void setConfigClass(String configClass) {
this.configClass = configClass;
}
/**
* Return the Context implementation class name.
*/
public String getContextClass() {
return (this.contextClass);
}
/**
* Set the Context implementation class name.
*
* @param contextClass The new Context implementation class name.
*/
public void setContextClass(String contextClass) {
this.contextClass = contextClass;
}
/**
* Return the deploy XML config file flag for this component.
*/
public boolean isDeployXML() {
return (this.deployXML);
}
/**
* Set the deploy XML config file flag for this component.
*
* @param deployXML The new deploy XML flag
*/
public void setDeployXML(boolean deployXML) {
this.deployXML= deployXML;
}
/**
* Return the unpack WARs flag.
*/
public boolean isUnpackWARs() {
return (this.unpackWARs);
}
/**
* Set the unpack WARs flag.
*
* @param unpackWARs The new unpack WARs flag
*/
public void setUnpackWARs(boolean unpackWARs) {
this.unpackWARs = unpackWARs;
}
/**
* Set the validation feature of the XML parser used when
* parsing xml instances.
* @param xmlValidation true to enable xml instance validation
*/
public void setXmlValidation(boolean xmlValidation){
this.xmlValidation = xmlValidation;
}
/**
* Get the server.xml <host> attribute's xmlValidation.
* @return true if validation is enabled.
*
*/
public boolean getXmlValidation(){
return xmlValidation;
}
/**
* Get the server.xml <host> attribute's xmlNamespaceAware.
* @return true if namespace awarenes is enabled.
*
*/
public boolean getXmlNamespaceAware(){
return xmlNamespaceAware;
}
/**
* Set the namespace aware feature of the XML parser used when
* parsing xml instances.
* @param xmlNamespaceAware true to enable namespace awareness
*/
public void setXmlNamespaceAware(boolean xmlNamespaceAware){
this.xmlNamespaceAware=xmlNamespaceAware;
}
// --------------------------------------------------------- Public Methods
/**
* Process the START event for an associated Host.
*
* @param event The lifecycle event that has occurred
*/
public void lifecycleEvent(LifecycleEvent event) {
if (event.getType().equals("check"))
check();
// Identify the host we are associated with
try {
host = (Host) event.getLifecycle();
if (host instanceof StandardHost) {
setDeployXML(((StandardHost) host).isDeployXML());
setUnpackWARs(((StandardHost) host).isUnpackWARs());
setXmlNamespaceAware(((StandardHost) host).getXmlNamespaceAware());
setXmlValidation(((StandardHost) host).getXmlValidation());
}
} catch (ClassCastException e) {
log.error(sm.getString("hostConfig.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();
}
/**
* Add a serviced application to the list.
*/
public synchronized void addServiced(String name) {
serviced.add(name);
}
/**
* Is application serviced ?
* @return state of the application
*/
public synchronized boolean isServiced(String name) {
return (serviced.contains(name));
}
/**
* Removed a serviced application from the list.
*/
public synchronized void removeServiced(String name) {
serviced.remove(name);
}
/**
* Get the instant where an application was deployed.
* @return 0L if no application with that name is deployed, or the instant
* on which the application was deployed
*/
public long getDeploymentTime(String name) {
DeployedApplication app = (DeployedApplication) deployed.get(name);
if (app == null) {
return 0L;
} else {
return app.timestamp;
}
}
// ------------------------------------------------------ Protected Methods
/**
* Create the digester which will be used to parse context config files.
*/
protected static Digester createDigester() {
Digester digester = new Digester();
digester.setValidating(false);
// Add object creation rule
digester.addObjectCreate("Context", "org.apache.catalina.core.StandardContext",
"className");
// Set the properties on that object (it doesn't matter if extra
// properties are set)
digester.addSetProperties("Context");
return (digester);
}
/**
* Return a File object representing the "application root" directory
* for our associated Host.
*/
protected File appBase() {
if (appBase != null) {
return appBase;
}
File file = new File(host.getAppBase());
if (!file.isAbsolute())
file = new File(System.getProperty("catalina.base"),
host.getAppBase());
try {
appBase = file.getCanonicalFile();
} catch (IOException e) {
appBase = file;
}
return (appBase);
}
/**
* Return a File object representing the "configuration root" directory
* for our associated Host.
*/
protected File configBase() {
if (configBase != null) {
return configBase;
}
File file = new File(System.getProperty("catalina.base"), "conf");
Container parent = host.getParent();
if ((parent != null) && (parent instanceof Engine)) {
file = new File(file, parent.getName());
}
file = new File(file, host.getName());
try {
configBase = file.getCanonicalFile();
} catch (IOException e) {
configBase = file;
}
return (configBase);
}
/**
* Get the name of the configBase.
* For use with JMX management.
*/
public String getConfigBaseName() {
return configBase().getAbsolutePath();
}
/**
* Given a context path, get the config file name.
*/
protected String getConfigFile(String path) {
String basename = null;
if (path.equals("")) {
basename = "ROOT";
} else {
basename = path.substring(1).replace('/', '#');
}
return (basename);
}
/**
* Given a context path, get the config file name.
*/
protected String getDocBase(String path) {
String basename = null;
if (path.equals("")) {
basename = "ROOT";
} else {
basename = path.substring(1);
}
return (basename);
}
/**
* Deploy applications for any directories or WAR files that are found
* in our "application root" directory.
*/
protected void deployApps() {
File appBase = appBase();
File configBase = configBase();
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs, and loop if additional descriptors are found
deployWARs(appBase, appBase.list());
// Deploy expanded folders
deployDirectories(appBase, appBase.list());
}
/**
* Deploy applications for any directories or WAR files that are found
* in our "application root" directory.
*/
protected void deployApps(String name) {
File appBase = appBase();
File configBase = configBase();
String baseName = getConfigFile(name);
String docBase = getConfigFile(name);
// Deploy XML descriptors from configBase
File xml = new File(configBase, baseName + ".xml");
if (xml.exists())
deployDescriptor(name, xml, baseName + ".xml");
// Deploy WARs, and loop if additional descriptors are found
File war = new File(appBase, docBase + ".war");
if (war.exists())
deployWAR(name, war, docBase + ".war");
// Deploy expanded folders
File dir = new File(appBase, docBase);
if (dir.exists())
deployDirectory(name, dir, docBase);
}
/**
* Deploy XML context descriptors.
*/
protected void deployDescriptors(File configBase, 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 contextXml = new File(configBase, files[i]);
if (files[i].toLowerCase().endsWith(".xml")) {
// Calculate the context path and make sure it is unique
String nameTmp = files[i].substring(0, files[i].length() - 4);
String contextPath = "/" + nameTmp.replace('#', '/');
if (nameTmp.equals("ROOT")) {
contextPath = "";
}
if (isServiced(contextPath))
continue;
String file = files[i];
deployDescriptor(contextPath, contextXml, file);
}
}
}
/**
* @param contextPath
* @param contextXml
* @param file
*/
protected void deployDescriptor(String contextPath, File contextXml, String file) {
DeployedApplication deployedApp = new DeployedApplication(contextPath);
if (deployed.containsKey(contextPath))
return;
// Assume this is a configuration descriptor and deploy it
log.debug(sm.getString("hostConfig.deployDescriptor", file));
try {
Context context = null;
synchronized (digester) {
context = (Context) digester.parse(contextXml);
}
if (context instanceof Lifecycle) {
Class clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =
(LifecycleListener) clazz.newInstance();
((Lifecycle) context).addLifecycleListener(listener);
}
context.setConfigFile(contextXml.getAbsolutePath());
context.setPath(contextPath);
// Add the context XML to the list of watched files
deployedApp.reloadResources.put
(contextXml.getAbsolutePath(), new Long(contextXml.lastModified()));
// Add the associated docBase to the redeployed list if it's a WAR
boolean isWar = false;
if (context.getDocBase() != null) {
File docBase = new File(context.getDocBase());
if (!docBase.isAbsolute()) {
docBase = new File(appBase(), context.getDocBase());
}
deployedApp.redeployResources.put(docBase.getAbsolutePath(),
new Long(docBase.lastModified()));
if (docBase.getAbsolutePath().toLowerCase().endsWith(".war")) {
isWar = true;
}
}
host.addChild(context);
// Add the eventual unpacked WAR and all the resources which will be
// watched inside it
if (isWar && unpackWARs) {
String name = null;
String path = context.getPath();
if (path.equals("")) {
name = "ROOT";
} else {
if (path.startsWith("/")) {
name = path.substring(1);
} else {
name = path;
}
}
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 {
if (context.getDocBase() != null) {
File docBase = new File(context.getDocBase());
if (!docBase.isAbsolute()) {
docBase = new File(appBase(), context.getDocBase());
}
try {
docBase = docBase.getCanonicalFile();
if (!docBase.getAbsolutePath().startsWith(appBase().getAbsolutePath())) {
deployedApp.redeployResources.put
(contextXml.getAbsolutePath(), new Long(contextXml.lastModified()));
deployedApp.reloadResources.remove(contextXml.getAbsolutePath());
}
} catch (IOException e) {
// Ignore
}
}
addWatchedResources(deployedApp, null, context);
}
} catch (Throwable t) {
log.error(sm.getString("hostConfig.deployDescriptor.error",
file), t);
}
deployed.put(contextPath, deployedApp);
}
/**
* Deploy WAR files.
*/
protected void deployWARs(File appBase, String[] files) {
if (files == null)
return;
boolean checkAdditionalDeployments = false;
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")) {
// Calculate the context path and make sure it is unique
String contextPath = "/" + files[i];
int period = contextPath.lastIndexOf(".");
if (period >= 0)
contextPath = contextPath.substring(0, period);
if (contextPath.equals("/ROOT"))
contextPath = "";
if (isServiced(contextPath))
continue;
String file = files[i];
deployWAR(contextPath, dir, file);
}
}
}
/**
* @param contextPath
* @param dir
* @param file
*/
protected void deployWAR(String contextPath, File dir, String file) {
if (deployed.containsKey(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(dir);
entry = jar.getJarEntry("META-INF/context.xml");
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
log.info(sm.getString("hostConfig.deployJar", file));
try {
Context context = (Context) Class.forName(contextClass).newInstance();
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);
if (xml.exists()) {
context.setConfigFile(xml.getAbsolutePath());
deployedApp.reloadResources.put
(xml.getAbsolutePath(), new Long(xml.lastModified()));
}
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;
}
}
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);
}
// Populate redeploy resources with the WAR file
deployedApp.redeployResources.put
(dir.getAbsolutePath(), new Long(dir.lastModified()));
deployed.put(contextPath, deployedApp);
}
/**
* Deploy directories.
*/
protected void deployDirectories(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 (dir.isDirectory()) {
// Make sure there is an application configuration directory
// This is needed if the Context appBase is the same as the
// web server document root to make sure only web applications
// are deployed and not directories for web space.
File webInf = new File(dir, "/WEB-INF");
if (!webInf.exists() || !webInf.isDirectory() ||
!webInf.canRead())
continue;
// Calculate the context path and make sure it is unique
String contextPath = "/" + files[i];
if (files[i].equals("ROOT"))
contextPath = "";
if (isServiced(contextPath))
continue;
String file = files[i];
deployDirectory(contextPath, dir, file);
}
}
}
/**
* @param contextPath
* @param dir
* @param file
*/
protected void deployDirectory(String contextPath, File dir, String file) {
DeployedApplication deployedApp = new DeployedApplication(contextPath);
if (deployed.containsKey(contextPath))
return;
// Deploy the application in this directory
if( log.isDebugEnabled() )
log.debug(sm.getString("hostConfig.deployDir", file));
try {
Context context = (Context) Class.forName(contextClass).newInstance();
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);
if (deployXML) {
context.setConfigFile
((new File(dir, "META-INF/context.xml")).getAbsolutePath());
}
host.addChild(context);
deployedApp.redeployResources.put(dir.getAbsolutePath(),
new Long(dir.lastModified()));
addWatchedResources(deployedApp, dir.getAbsolutePath(), context);
} catch (Throwable t) {
log.error(sm.getString("hostConfig.deployDir.error", file), t);
}
deployed.put(contextPath, deployedApp);
}
/**
* Add watched resources to the specified Context.
* @param app
*/
protected void addWatchedResources(DeployedApplication app, String docBase, Context context) {
// FIXME: Feature idea. Add support for patterns (ex: WEB-INF/*, WEB-INF/*.xml), where
// we would only check if at least one resource is newer than app.timestamp
File docBaseFile = null;
if (docBase != null) {
docBaseFile = new File(docBase);
if (!docBaseFile.isAbsolute()) {
docBaseFile = new File(appBase(), docBase);
}
}
String[] watchedResources = context.findWatchedResources();
for (int i = 0; i < watchedResources.length; i++) {
File resource = new File(watchedResources[i]);
if (!resource.isAbsolute()) {
if (docBase != null) {
resource = new File(docBaseFile, watchedResources[i]);
} else {
continue;
}
}
app.reloadResources.put(resource.getAbsolutePath(),
new Long(resource.lastModified()));
}
}
/**
* Check resources for redeployment and reloading.
*/
protected synchronized void checkResources(DeployedApplication app) {
String[] resources = (String[]) app.redeployResources.keySet().toArray(new String[0]);
for (int i = 0; i < resources.length; i++) {
File resource = new File(resources[i]);
if (log.isDebugEnabled())
log.debug("Checking context[" + app.name + "] redeploy resource " + resource);
if (resource.exists()) {
long lastModified = ((Long) app.redeployResources.get(resources[i])).longValue();
if ((!resource.isDirectory()) && resource.lastModified() > lastModified) {
// Redeploy application
ContainerBase context = (ContainerBase) host.findChild(app.name);
host.removeChild(context);
try {
context.destroy();
} catch (Exception e) {
log.warn(sm.getString
("hostConfig.context.destroy", app.name), e);
}
// Delete other redeploy resources
for (int j = 0; j < resources.length; j++) {
if (j != i) {
try {
File current = new File(resources[j]);
current = current.getCanonicalFile();
if ((current.getAbsolutePath().startsWith(appBase().getAbsolutePath()))
|| (current.getAbsolutePath().startsWith(configBase().getAbsolutePath()))) {
if (log.isDebugEnabled())
log.debug("Delete " + current);
ExpandWar.delete(current);
}
} catch (IOException e) {
log.warn(sm.getString
("hostConfig.canonicalizing", app.name), e);
}
}
}
deployed.remove(app.name);
return;
}
} else {
// Undeploy application
ContainerBase context = (ContainerBase) host.findChild(app.name);
host.removeChild(context);
try {
context.destroy();
} catch (Exception e) {
log.warn(sm.getString
("hostConfig.context.destroy", app.name), e);
}
// Delete all redeploy resources
for (int j = 0; j < resources.length; j++) {
try {
File current = new File(resources[j]);
current = current.getCanonicalFile();
if ((current.getAbsolutePath().startsWith(appBase().getAbsolutePath()))
|| (current.getAbsolutePath().startsWith(configBase().getAbsolutePath()))) {
if (log.isDebugEnabled())
log.debug("Delete " + current);
ExpandWar.delete(current);
}
} catch (IOException e) {
log.warn(sm.getString
("hostConfig.canonicalizing", app.name), e);
}
}
// Delete reload resources as well (to remove any remaining .xml descriptor)
String[] resources2 = (String[]) app.reloadResources.keySet().toArray(new String[0]);
for (int j = 0; j < resources2.length; j++) {
try {
File current = new File(resources2[j]);
current = current.getCanonicalFile();
if ((current.getAbsolutePath().startsWith(appBase().getAbsolutePath()))
|| ((current.getAbsolutePath().startsWith(configBase().getAbsolutePath())
&& (current.getAbsolutePath().endsWith(".xml"))))) {
if (log.isDebugEnabled())
log.debug("Delete " + current);
ExpandWar.delete(current);
}
} catch (IOException e) {
log.warn(sm.getString
("hostConfig.canonicalizing", app.name), e);
}
}
deployed.remove(app.name);
return;
}
}
resources = (String[]) app.reloadResources.keySet().toArray(new String[0]);
for (int i = 0; i < resources.length; i++) {
File resource = new File(resources[i]);
if (log.isDebugEnabled())
log.debug("Checking context[" + app.name + "] reload resource " + resource);
long lastModified = ((Long) app.reloadResources.get(resources[i])).longValue();
if ((!resource.exists() && lastModified != 0L)
|| (resource.lastModified() != lastModified)) {
// Reload application
Container context = host.findChild(app.name);
try {
((Lifecycle) context).stop();
} catch (Exception e) {
log.warn(sm.getString
("hostConfig.context.restart", app.name), e);
}
// If the context was not started (for example an error
// in web.xml) we'll still get to try to start
try {
((Lifecycle) context).start();
} catch (Exception e) {
log.warn(sm.getString
("hostConfig.context.restart", app.name), e);
}
// Update times
app.reloadResources.put(resources[i], new Long(resource.lastModified()));
app.timestamp = System.currentTimeMillis();
return;
}
}
}
/**
* Process a "start" event for this Host.
*/
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"), e);
}
deployApps();
}
/**
* Process a "stop" event for this Host.
*/
public void stop() {
if (log.isDebugEnabled())
log.debug(sm.getString("hostConfig.stop"));
undeployApps();
if (oname != null) {
try {
Registry.getRegistry(null, null).unregisterComponent(oname);
} catch (Exception e) {
log.error(sm.getString("hostConfig.jmx.unregister"), e);
}
}
oname = null;
appBase = null;
configBase = null;
}
/**
* Undeploy all deployed applications.
*/
protected void undeployApps() {
if (log.isDebugEnabled())
log.debug(sm.getString("hostConfig.undeploying"));
// Soft undeploy all contexts we have deployed
DeployedApplication[] apps =
(DeployedApplication[]) deployed.values().toArray(new DeployedApplication[0]);
for (int i = 0; i < apps.length; i++) {
host.removeChild(host.findChild(apps[i].name));
}
deployed.clear();
}
/**
* Check status of all webapps.
*/
protected void check() {
if (host.getAutoDeploy()) {
// Check for resources modification to trigger redeployment
DeployedApplication[] apps =
(DeployedApplication[]) deployed.values().toArray(new DeployedApplication[0]);
for (int i = 0; i < apps.length; i++) {
if (!isServiced(apps[i].name))
checkResources(apps[i]);
}
// Hotdeploy applications
deployApps();
}
}
/**
* Check status of a specific webapp, for use with stuff like management webapps.
*/
public void check(String name) {
DeployedApplication app = (DeployedApplication) deployed.get(name);
if (app != null) {
checkResources(app);
} else {
deployApps(name);
}
}
/**
* Add a new Context to be managed by us.
* Entry point for the admin webapp, and other JMX Context controlers.
*/
public void manageApp(Context context) {
String contextPath = context.getPath();
if (deployed.containsKey(contextPath))
return;
DeployedApplication deployedApp = new DeployedApplication(contextPath);
// Add the associated docBase to the redeployed list if it's a WAR
boolean isWar = false;
if (context.getDocBase() != null) {
File docBase = new File(context.getDocBase());
if (!docBase.isAbsolute()) {
docBase = new File(appBase(), context.getDocBase());
}
deployedApp.redeployResources.put(docBase.getAbsolutePath(),
new Long(docBase.lastModified()));
if (docBase.getAbsolutePath().toLowerCase().endsWith(".war")) {
isWar = true;
}
}
host.addChild(context);
// Add the eventual unpacked WAR and all the resources which will be
// watched inside it
if (isWar && unpackWARs) {
String name = null;
String path = context.getPath();
if (path.equals("")) {
name = "ROOT";
} else {
if (path.startsWith("/")) {
name = path.substring(1);
} else {
name = path;
}
}
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);
}
deployed.put(contextPath, deployedApp);
}
/**
* Remove a webapp from our control.
* Entry point for the admin webapp, and other JMX Context controlers.
*/
public void unmanageApp(String contextPath) {
if(isServiced(contextPath)) {
deployed.remove(contextPath);
host.removeChild(host.findChild(contextPath));
}
}
// ----------------------------------------------------- Instance Variables
/**
* This class represents the state of a deployed application, as well as
* the monitored resources.
*/
protected class DeployedApplication {
public DeployedApplication(String name) {
this.name = name;
}
/**
* Application context path. The assertion is that
* (host.getChild(name) != null).
*/
public String name;
/**
* Any modification of the specified (static) resources will cause a
* redeployment of the application. If any of the specified resources is
* removed, the application will be undeployed. Typically, this will
* contain resources like the context.xml file, a compressed WAR path.
* The value is the last modification time.
*/
public HashMap redeployResources = new HashMap();
/**
* Any modification of the specified (static) resources will cause a
* reload of the application. This will typically contain resources
* such as the web.xml of a webapp, but can be configured to contain
* additional descriptors.
* The value is the last modification time.
*/
public HashMap reloadResources = new HashMap();
/**
* Instant where the application was last put in service.
*/
public long timestamp = System.currentTimeMillis();
}
}