package org.jboss.web.tomcat.tc5;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Iterator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.security.CodeSource;
import java.security.cert.Certificate;
import javax.management.Attribute;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.apache.catalina.Loader;
import org.apache.catalina.session.StandardManager;
import org.jboss.deployment.DeploymentException;
import org.jboss.deployment.DeploymentInfo;
import org.jboss.metadata.WebMetaData;
import org.jboss.web.AbstractWebContainer;
import org.jboss.web.AbstractWebDeployer;
import org.jboss.web.WebApplication;
import org.jboss.web.tomcat.security.JaccContextValve;
import org.jboss.web.tomcat.security.SecurityAssociationValve;
import org.jboss.web.tomcat.security.CustomPrincipalValve;
import org.jboss.web.tomcat.tc5.session.ClusteredSessionValve;
import org.jboss.web.tomcat.tc5.session.ClusteringNotSupportedException;
import org.jboss.web.tomcat.tc5.session.InstantSnapshotManager;
import org.jboss.web.tomcat.tc5.session.IntervalSnapshotManager;
import org.jboss.web.tomcat.tc5.session.AbstractJBossManager;
import org.jboss.web.tomcat.tc5.session.SnapshotManager;
public class TomcatDeployer extends AbstractWebDeployer
{
private static final String CONTEXT_CONFIG_FILE = "WEB-INF/context.xml";
private DeployerConfig config;
private String[] javaVMs =
{" jboss.management.local:J2EEServer=Local,j2eeType=JVM,name=localhost"};
private String serverName = "jboss";
private HashMap vhostToHostNames = new HashMap();
public void init(Object containerConfig) throws Exception
{
this.config = (DeployerConfig) containerConfig;
super.setJava2ClassLoadingCompliance(config.isJava2ClassLoadingCompliance());
super.setUnpackWars(config.isUnpackWars());
super.setLenientEjbLink(config.isLenientEjbLink());
super.setDefaultSecurityDomain(config.getDefaultSecurityDomain());
}
protected void performDeploy(WebApplication appInfo, String warUrl,
AbstractWebContainer.WebDescriptorParser webAppParser)
throws Exception
{
WebMetaData metaData = appInfo.getMetaData();
String hostName = null;
Iterator vhostNames = metaData.getVirtualHosts();
Iterator hostNames = mapVirtualHosts(vhostNames);
if (hostNames.hasNext())
{
hostName = hostNames.next().toString();
}
performDeployInternal(hostName, appInfo, warUrl, webAppParser);
while (hostNames.hasNext())
{
String additionalHostName = hostNames.next().toString();
performDeployInternal(additionalHostName, appInfo, warUrl, webAppParser);
}
}
protected void performDeployInternal(String hostName,
WebApplication appInfo, String warUrl,
AbstractWebContainer.WebDescriptorParser webAppParser)
throws Exception
{
WebMetaData metaData = appInfo.getMetaData();
String ctxPath = metaData.getContextRoot();
if (ctxPath.equals("/") || ctxPath.equals("/ROOT") || ctxPath.equals(""))
{
log.debug("deploy root context=" + ctxPath);
ctxPath = "/";
metaData.setContextRoot(ctxPath);
}
log.info("deploy, ctxPath=" + ctxPath + ", warUrl=" + warUrl);
URL url = new URL(warUrl);
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Loader webLoader = null;
if (config.isUseJBossWebLoader())
{
WebCtxLoader jbossLoader = new WebCtxLoader(loader);
jbossLoader.setWarURL(url);
webLoader = jbossLoader;
}
else
{
String[] pkgs = config.getFilteredPackages();
WebAppLoader jbossLoader = new WebAppLoader(loader, pkgs);
webLoader = jbossLoader;
}
if (appInfo.getAppData() == null)
webAppParser.parseWebAppDescriptors(loader, appInfo.getMetaData());
appInfo.setName(url.getPath());
appInfo.setClassLoader(loader);
appInfo.setURL(url);
String objectNameS = config.getCatalinaDomain()
+ ":j2eeType=WebModule,name=//" +
((hostName == null) ? "localhost" : hostName)
+ ctxPath + ",J2EEApplication=none,J2EEServer=none";
ObjectName objectName = new ObjectName(objectNameS);
if (server.isRegistered(objectName))
{
log.debug("Already exists, destroying " + objectName);
server.invoke(objectName, "destroy", new Object[]{},
new String[]{});
}
server.createMBean("org.apache.commons.modeler.BaseModelMBean",
objectName, new Object[]{config.getContextClassName()},
new String[]{"java.lang.String"});
String ctxConfig = null;
try
{
ctxConfig = findConfig(url);
}
catch (IOException e)
{
log.debug("No " + CONTEXT_CONFIG_FILE + " in " + url, e);
}
server.setAttribute(objectName, new Attribute("docBase", url.getFile()));
server.setAttribute(objectName, new Attribute("configFile", ctxConfig));
server.setAttribute(objectName, new Attribute
("defaultContextXml", "context.xml"));
server.setAttribute(objectName, new Attribute
("defaultWebXml", "conf/web.xml"));
server.setAttribute(objectName, new Attribute("javaVMs", javaVMs));
server.setAttribute(objectName, new Attribute("server", serverName));
server.setAttribute(objectName, new Attribute
("saveConfig", Boolean.FALSE));
if (webLoader != null)
{
server.setAttribute(objectName, new Attribute
("loader", webLoader));
}
else
{
server.setAttribute(objectName, new Attribute
("parentClassLoader", loader));
}
server.setAttribute(objectName, new Attribute
("delegate", new Boolean(getJava2ClassLoadingCompliance())));
String[] jspCP = getCompileClasspath(loader);
StringBuffer classpath = new StringBuffer();
for (int u = 0; u < jspCP.length; u++)
{
String repository = jspCP[u];
if (repository == null)
continue;
if (repository.startsWith("file://"))
repository = repository.substring(7);
else if (repository.startsWith("file:"))
repository = repository.substring(5);
else
continue;
if (repository == null)
continue;
File fp = new File(repository);
if (!fp.isDirectory())
{
try
{
ZipFile zip = new ZipFile(fp);
zip.close();
}
catch (IOException e)
{
continue;
}
}
if (u > 0)
classpath.append(File.pathSeparator);
classpath.append(repository);
}
server.setAttribute(objectName, new Attribute
("compilerClasspath", classpath.toString()));
switch (metaData.getSessionCookies())
{
case WebMetaData.SESSION_COOKIES_ENABLED:
server.setAttribute(objectName, new Attribute
("cookies", new Boolean(true)));
log.debug("Enabling session cookies");
break;
case WebMetaData.SESSION_COOKIES_DISABLED:
server.setAttribute(objectName, new Attribute
("cookies", new Boolean(false)));
log.debug("Disabling session cookies");
break;
default:
log.debug("Using session cookies default setting");
}
Certificate[] certs = null;
CodeSource cs = new CodeSource(url, certs);
JaccContextValve jaccValve = new JaccContextValve(metaData.getJaccContextID(), cs);
server.invoke(objectName, "addValve",
new Object[]{jaccValve},
new String[]{"org.apache.catalina.Valve"});
server.invoke(objectName, "init", new Object[]{}, new String[]{});
if (metaData.getDistributable())
{
try
{
AbstractJBossManager manager = null;
String managerClassName = config.getManagerClass();
Class managerClass = Thread.currentThread().getContextClassLoader().loadClass(managerClassName);
manager = (AbstractJBossManager) managerClass.newInstance();
String name = "//" + ((hostName == null) ? "localhost" : hostName) + ctxPath;
manager.init(name, metaData, config.isUseJK(), config.isUseLocalCache());
server.setAttribute(objectName, new Attribute("manager", manager));
if (manager instanceof AbstractJBossManager)
{
SnapshotManager snap = null;
String snapshotMode = config.getSnapshotMode();
int snapshotInterval = config.getSnapshotInterval();
if (snapshotMode.equals("instant"))
{
snap = new InstantSnapshotManager(manager, ctxPath);
}
else if (snapshotMode.equals("interval"))
{
snap = new IntervalSnapshotManager(manager, ctxPath, snapshotInterval);
}
else
{
log.error("Snapshot mode must be 'instant' or 'interval' - using 'instant'");
snap = new InstantSnapshotManager(manager, ctxPath);
}
Object valve = new ClusteredSessionValve(snap);
server.invoke(objectName, "addValve",
new Object[]{valve},
new String[]{"org.apache.catalina.Valve"});
}
else
{
throw new ClusteringNotSupportedException("managerClass " + managerClassName + " not known");
}
log.debug("Enabled clustering support for ctxPath=" + ctxPath);
}
catch (ClusteringNotSupportedException e)
{
log.error("Failed to setup clustering, clustering disabled");
}
}
else
{
StandardManager manager = new StandardManager();
manager.setPathname("");
server.setAttribute(objectName, new Attribute("manager", manager));
}
SecurityAssociationValve valve = new SecurityAssociationValve(metaData,
config.getSecurityManagerService());
valve.setSubjectAttributeName(config.getSubjectAttributeName());
server.invoke(objectName, "addValve",
new Object[]{valve},
new String[]{"org.apache.catalina.Valve"});
CustomPrincipalValve cpvalve = new CustomPrincipalValve();
server.invoke(objectName, "addValve",
new Object[]{cpvalve},
new String[]{"org.apache.catalina.Valve"});
Integer state = (Integer) server.getAttribute(objectName, "state");
if (state.intValue() != 1)
{
throw new DeploymentException("URL " + warUrl + " deployment failed");
}
Loader ctxLoader = (Loader) server.getAttribute(objectName, "loader");
metaData.setContextLoader(ctxLoader.getClassLoader());
appInfo.setAppData(objectName);
DeploymentInfo di = webAppParser.getDeploymentInfo();
di.deployedObject = objectName;
ObjectName servletQuery = new ObjectName
(config.getCatalinaDomain() + ":j2eeType=Servlet,WebModule="
+ objectName.getKeyProperty("name") + ",*");
Iterator iterator = server.queryMBeans(servletQuery, null).iterator();
while (iterator.hasNext())
{
di.mbeans.add(((ObjectInstance) iterator.next()).getObjectName());
}
log.debug("Initialized: " + appInfo + " " + objectName);
}
protected void performUndeploy(String warUrl, WebApplication appInfo)
throws Exception
{
if (appInfo == null)
{
log.debug("performUndeploy, no WebApplication found for URL "
+ warUrl);
return;
}
log.info("undeploy, ctxPath=" + appInfo.getMetaData().getContextRoot()
+ ", warUrl=" + warUrl);
WebMetaData metaData = appInfo.getMetaData();
String hostName = null;
Iterator hostNames = metaData.getVirtualHosts();
if (hostNames.hasNext())
{
hostName = hostNames.next().toString();
}
performUndeployInternal(hostName, warUrl, appInfo);
while (hostNames.hasNext())
{
String additionalHostName = hostNames.next().toString();
performUndeployInternal(additionalHostName, warUrl, appInfo);
}
}
protected void performUndeployInternal(String hostName, String warUrl,
WebApplication appInfo)
throws Exception
{
WebMetaData metaData = appInfo.getMetaData();
String ctxPath = metaData.getContextRoot();
if (server == null)
return;
ObjectName objectName = new ObjectName(config.getCatalinaDomain()
+ ":j2eeType=WebModule,name=//" +
((hostName == null) ? "localhost" : hostName)
+ ctxPath + ",J2EEApplication=none,J2EEServer=none");
if (server.isRegistered(objectName))
{
server.invoke(objectName, "destroy", new Object[]{},
new String[]{});
}
}
protected synchronized Iterator mapVirtualHosts(Iterator vhostNames)
throws Exception
{
if( vhostToHostNames.size() == 0 )
{
String hostQuery = config.getCatalinaDomain() + ":type=Host,*";
ObjectName query = new ObjectName(hostQuery);
Set hosts = server.queryNames(query, null);
Iterator iter = hosts.iterator();
while( iter.hasNext() )
{
ObjectName host = (ObjectName) iter.next();
String name = host.getKeyProperty("host");
if( name != null )
{
vhostToHostNames.put(name, name);
String[] aliases = (String[])
server.invoke(host, "findAliases", null, null);
int count = aliases != null ? aliases.length : 0;
for(int n = 0;n < count; n ++)
{
vhostToHostNames.put(aliases[n], name);
}
}
}
}
HashSet hosts = new HashSet();
while( vhostNames.hasNext() )
{
String vhost = (String) vhostNames.next();
String host = (String) vhostToHostNames.get(vhost);
if( host == null )
{
log.warn("Failed to map vhost: "+vhost);
host = vhost;
}
hosts.add(host);
}
return hosts.iterator();
}
private String findConfig(URL warURL) throws IOException
{
String result = null;
File warFile = new File(warURL.getFile());
if (warURL.getProtocol().equals("file") && warFile.isDirectory() == true)
{
File webDD = new File(warFile, CONTEXT_CONFIG_FILE);
if (webDD.exists() == true) result = webDD.getAbsolutePath();
}
else
{
ZipFile zipFile = new ZipFile(warFile);
ZipEntry entry = zipFile.getEntry(CONTEXT_CONFIG_FILE);
if (entry != null)
{
InputStream zipIS = zipFile.getInputStream(entry);
byte[] buffer = new byte[512];
int bytes;
result = warFile.getAbsolutePath() + "-context.xml";
FileOutputStream fos = new FileOutputStream(result);
while ((bytes = zipIS.read(buffer)) > 0)
{
fos.write(buffer, 0, bytes);
}
zipIS.close();
fos.close();
}
zipFile.close();
}
return result;
}
}