package org.jboss.web;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.management.ObjectName;
import org.jboss.deployment.DeploymentException;
import org.jboss.deployment.DeploymentInfo;
import org.jboss.deployment.SubDeployerSupport;
import org.jboss.metadata.WebMetaData;
import org.jboss.metadata.XmlFileLoader;
import org.jboss.mx.loading.LoaderRepositoryFactory;
import org.jboss.mx.loading.LoaderRepositoryFactory.LoaderRepositoryConfig;
import org.jboss.mx.util.MBeanProxyExt;
import org.jboss.system.ServiceControllerMBean;
import org.jboss.util.file.JarUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public abstract class AbstractWebContainer
extends SubDeployerSupport
implements AbstractWebContainerMBean
{
public static final String DEPLOYER = "org.jboss.web.AbstractWebContainer.deployer";
public static final String WEB_APP = "org.jboss.web.AbstractWebContainer.webApp";
public static final String WEB_MODULE = "org.jboss.web.AbstractWebContainer.webModule";
public static final String ERROR = "org.jboss.web.AbstractWebContainer.error";
public static interface WebDescriptorParser
{
public void parseWebAppDescriptors(ClassLoader loader, WebMetaData metaData) throws Exception;
public DeploymentInfo getDeploymentInfo();
}
protected HashMap deploymentMap = new HashMap();
protected boolean java2ClassLoadingCompliance = false;
protected boolean unpackWars = true;
protected boolean acceptNonWarDirs = false;
protected boolean lenientEjbLink = false;
protected String defaultSecurityDomain = "java:/jaas/other";
private String subjectAttributeName = null;
private ServiceControllerMBean serviceController;
public AbstractWebContainer()
{
}
public boolean getJava2ClassLoadingCompliance()
{
return java2ClassLoadingCompliance;
}
public void setJava2ClassLoadingCompliance(boolean flag)
{
java2ClassLoadingCompliance = flag;
}
public boolean getUnpackWars()
{
return unpackWars;
}
public void setUnpackWars(boolean flag)
{
this.unpackWars = flag;
}
public boolean getAcceptNonWarDirs()
{
return acceptNonWarDirs;
}
public void setAcceptNonWarDirs(boolean flag)
{
this.acceptNonWarDirs = flag;
}
public boolean getLenientEjbLink()
{
return lenientEjbLink;
}
public void setLenientEjbLink(boolean flag)
{
lenientEjbLink = flag;
}
public String getDefaultSecurityDomain()
{
return defaultSecurityDomain;
}
public void setDefaultSecurityDomain(String defaultSecurityDomain)
{
this.defaultSecurityDomain = defaultSecurityDomain;
}
public String getSubjectAttributeName()
{
return subjectAttributeName;
}
public void setSubjectAttributeName(String subjectAttributeName)
{
this.subjectAttributeName = subjectAttributeName;
}
public abstract AbstractWebDeployer getDeployer(DeploymentInfo di) throws Exception;
public boolean accepts(DeploymentInfo sdi)
{
String warFile = sdi.url.getFile();
boolean accepts = warFile.endsWith("war") || warFile.endsWith("war/");
if ( accepts == false && acceptNonWarDirs == true )
{
if( sdi.url.getProtocol().equalsIgnoreCase("file") )
{
File webXml = new File(warFile, "WEB-INF/web.xml");
accepts = webXml.exists();
}
}
return accepts;
}
public synchronized void init(DeploymentInfo di)
throws DeploymentException
{
log.debug("Begin init");
this.server = di.getServer();
try
{
if (di.url.getPath().endsWith("/"))
{
di.watch = new URL(di.url, "WEB-INF/web.xml");
}
else
{
di.watch = di.url;
}
boolean hasWebservices = di.localCl.findResource("WEB-INF/webservices.xml") != null;
File warFile = new File(di.localUrl.getFile());
if (warFile.isDirectory() == false && (unpackWars || hasWebservices))
{
String prefix = warFile.getCanonicalPath();
prefix = prefix.substring(0, prefix.lastIndexOf(".war"));
File expWarFile = new File(prefix + "-exp.war");
if( expWarFile.mkdir() == false )
throw new DeploymentException("Was unable to mkdir: "+expWarFile);
log.debug("Unpacking war to: "+expWarFile);
FileInputStream fis = new FileInputStream(warFile);
JarUtils.unjar(fis, expWarFile);
fis.close();
log.debug("Replaced war with unpacked contents");
if (warFile.delete() && expWarFile.renameTo(warFile))
expWarFile = warFile;
di.localUrl = expWarFile.toURL();
URL[] localCl = new URL[]{di.localUrl};
di.localCl = new URLClassLoader(localCl);
}
WebMetaData metaData = new WebMetaData();
metaData.setResourceClassLoader(di.localCl);
metaData.setJava2ClassLoadingCompliance(this.java2ClassLoadingCompliance);
di.metaData = metaData;
String webContext = di.webContext;
if( webContext != null )
{
if( webContext.length() > 0 && webContext.charAt(0) != '/' )
webContext = "/" + webContext;
}
URL warURL = di.localUrl != null ? di.localUrl : di.url;
if (log.isDebugEnabled())
{
log.debug("webContext: " + webContext);
log.debug("warURL: " + warURL);
}
parseMetaData(webContext, warURL, di.shortName, metaData);
LoaderRepositoryConfig config = metaData.getLoaderConfig();
if (config != null)
di.setRepositoryInfo(config);
super.init(di);
}
catch (DeploymentException e)
{
log.error("Problem in init ", e);
throw e;
}
catch (Exception e)
{
log.error("Problem in init ", e);
throw new DeploymentException(e);
}
log.debug("End init");
}
public void create(DeploymentInfo di) throws DeploymentException
{
try
{
AbstractWebDeployer deployer = getDeployer(di);
di.context.put(DEPLOYER, deployer);
WebMetaData metaData = (WebMetaData) di.metaData;
Collection depends = metaData.getDepends();
WebModule module = new WebModule(di, this, deployer);
ObjectName jmxName = new ObjectName("jboss.web.deployment:war="
+ di.shortName + ",id="+di.hashCode());
server.registerMBean(module, jmxName);
di.context.put(WEB_MODULE, jmxName);
serviceController.create(jmxName, depends);
super.create(di);
}
catch(Exception e)
{
throw new DeploymentException("Failed to create web module", e);
}
}
public synchronized void start(DeploymentInfo di) throws DeploymentException
{
ObjectName jmxName = (ObjectName) di.context.get(WEB_MODULE);
try
{
serviceController.start(jmxName);
}
catch (DeploymentException e)
{
throw e;
}
catch(Exception e)
{
throw new DeploymentException("Unable to start web module", e);
}
DeploymentException e = (DeploymentException) di.context.get(ERROR);
if( e != null )
throw e;
super.start(di);
}
public synchronized void stop(DeploymentInfo di)
throws DeploymentException
{
ObjectName jmxName = (ObjectName) di.context.get(WEB_MODULE);
try
{
serviceController.stop(jmxName);
}
catch (DeploymentException e)
{
throw e;
}
catch(Exception e)
{
throw new DeploymentException("Unable to stop web module", e);
}
super.stop(di);
}
public synchronized void destroy(DeploymentInfo di)
throws DeploymentException
{
ObjectName jmxName = (ObjectName) di.context.get(WEB_MODULE);
try
{
if( jmxName != null )
{
serviceController.destroy(jmxName);
server.unregisterMBean(jmxName);
}
}
catch (DeploymentException e)
{
throw e;
}
catch(Exception e)
{
throw new DeploymentException("Unable to stop web module", e);
}
super.destroy(di);
}
public boolean isDeployed(String warUrl)
{
return deploymentMap.containsKey(warUrl);
}
public void addDeployedApp(URL warURL, WebApplication webApp)
{
deploymentMap.put(warURL, webApp);
}
public WebApplication getDeployedApp(String warUrl)
{
WebApplication appInfo = (WebApplication) deploymentMap.get(warUrl);
return appInfo;
}
public WebApplication removeDeployedApp(URL warURL)
{
WebApplication appInfo = (WebApplication) deploymentMap.remove(warURL);
return appInfo;
}
public Iterator getDeployedApplications()
{
return deploymentMap.values().iterator();
}
public Element getConfig()
{
return null;
}
public void setConfig(Element config)
{
}
public static URL[] getClassLoaderURLs(ClassLoader cl)
{
URL[] urls = {};
try
{
Class returnType = urls.getClass();
Class[] parameterTypes = {};
Method getURLs = cl.getClass().getMethod("getURLs", parameterTypes);
if( returnType.isAssignableFrom(getURLs.getReturnType()) )
{
Object[] args = {};
urls = (URL[]) getURLs.invoke(cl, args);
}
if( urls == null || urls.length == 0 )
{
getURLs = cl.getClass().getMethod("getAllURLs", parameterTypes);
if( returnType.isAssignableFrom(getURLs.getReturnType()) )
{
Object[] args = {};
urls = (URL[]) getURLs.invoke(cl, args);
}
}
}
catch(Exception ignore)
{
}
return urls;
}
public String[] getCompileClasspath(ClassLoader loader)
{
HashSet tmp = new HashSet();
ClassLoader cl = loader;
while( cl != null )
{
URL[] urls = getClassLoaderURLs(cl);
addURLs(tmp, urls);
cl = cl.getParent();
}
try
{
URL[] globalUrls = (URL[])server.getAttribute(LoaderRepositoryFactory.DEFAULT_LOADER_REPOSITORY,
"URLs");
addURLs(tmp, globalUrls);
}
catch (Exception e)
{
log.warn("Could not get global URL[] from default loader repository!");
} log.trace("JSP CompileClasspath: " + tmp);
String[] cp = new String[tmp.size()];
tmp.toArray(cp);
return cp;
}
protected void processNestedDeployments(DeploymentInfo di) throws DeploymentException
{
}
protected void startService() throws Exception
{
serviceController = (ServiceControllerMBean)
MBeanProxyExt.create(ServiceControllerMBean.class,
ServiceControllerMBean.OBJECT_NAME,
server);
super.startService();
}
protected void parseMetaData(String ctxPath, URL warURL, String warName,
WebMetaData metaData)
throws DeploymentException
{
InputStream jbossWebIS = null;
InputStream webIS = null;
try
{
File warDir = new File(warURL.getFile());
if( warURL.getProtocol().equals("file") && warDir.isDirectory() == true )
{
File webDD = new File(warDir, "WEB-INF/web.xml");
if( webDD.exists() == true )
webIS = new FileInputStream(webDD);
File jbossWebDD = new File(warDir, "WEB-INF/jboss-web.xml");
if( jbossWebDD.exists() == true )
jbossWebIS = new FileInputStream(jbossWebDD);
}
else
{
InputStream warIS = warURL.openStream();
java.util.zip.ZipInputStream zipIS = new java.util.zip.ZipInputStream(warIS);
java.util.zip.ZipEntry entry;
byte[] buffer = new byte[512];
int bytes;
while( (entry = zipIS.getNextEntry()) != null )
{
if( entry.getName().equals("WEB-INF/web.xml") )
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while( (bytes = zipIS.read(buffer)) > 0 )
{
baos.write(buffer, 0, bytes);
}
webIS = new ByteArrayInputStream(baos.toByteArray());
}
else if( entry.getName().equals("WEB-INF/jboss-web.xml") )
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while( (bytes = zipIS.read(buffer)) > 0 )
{
baos.write(buffer, 0, bytes);
}
jbossWebIS = new ByteArrayInputStream(baos.toByteArray());
}
}
zipIS.close();
}
XmlFileLoader xmlLoader = new XmlFileLoader();
String warURI = warURL.toExternalForm();
try
{
if( webIS != null )
{
Document webDoc = xmlLoader.getDocument(webIS, warURI+"/WEB-INF/web.xml");
Element web = webDoc.getDocumentElement();
metaData.importXml(web);
}
}
catch(Exception e)
{
throw new DeploymentException("Failed to parse WEB-INF/web.xml", e);
}
try
{
if( jbossWebIS != null )
{
Document jbossWebDoc = xmlLoader.getDocument(jbossWebIS, warURI+"/WEB-INF/jboss-web.xml");
Element jbossWeb = jbossWebDoc.getDocumentElement();
metaData.importXml(jbossWeb);
}
}
catch(Exception e)
{
throw new DeploymentException("Failed to parse WEB-INF/jboss-web.xml", e);
}
}
catch(Exception e)
{
log.warn("Failed to parse descriptors for war("+warURL+")", e);
}
String webContext = ctxPath;
if( webContext == null )
webContext = metaData.getContextRoot();
if( webContext == null )
{
webContext = warName;
webContext = webContext.replace('\\', '/');
if( webContext.endsWith("/") )
webContext = webContext.substring(0, webContext.length()-1);
int prefix = webContext.lastIndexOf('/');
if( prefix > 0 )
webContext = webContext.substring(prefix+1);
int suffix = webContext.lastIndexOf(".war");
if( suffix > 0 )
webContext = webContext.substring(0, suffix);
int index = 0;
for(; index < webContext.length(); index ++)
{
char c = webContext.charAt(index);
if( Character.isDigit(c) == false && c != '.' )
break;
}
webContext = webContext.substring(index);
}
if( webContext.length() > 0 && webContext.charAt(0) != '/' )
webContext = "/" + webContext;
else if( webContext.equals("/") )
webContext = "";
metaData.setContextRoot(webContext);
}
private void addURLs(Set urlSet, URL[] urls)
{
for(int u = 0; u < urls.length; u ++)
{
URL url = urls[u];
urlSet.add(url.toExternalForm());
}
}
}