package org.jboss.web;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.security.Policy;
import javax.management.MBeanServer;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.LinkRef;
import javax.naming.NamingException;
import javax.security.jacc.PolicyConfiguration;
import javax.security.jacc.PolicyConfigurationFactory;
import javax.security.jacc.PolicyContextException;
import org.jboss.deployment.DeploymentException;
import org.jboss.deployment.DeploymentInfo;
import org.jboss.deployment.J2eeApplicationMetaData;
import org.jboss.ejb.Container;
import org.jboss.ejb.EjbUtil;
import org.jboss.logging.Logger;
import org.jboss.metadata.EjbLocalRefMetaData;
import org.jboss.metadata.EjbRefMetaData;
import org.jboss.metadata.EnvEntryMetaData;
import org.jboss.metadata.MessageDestinationMetaData;
import org.jboss.metadata.MessageDestinationRefMetaData;
import org.jboss.metadata.ResourceEnvRefMetaData;
import org.jboss.metadata.ResourceRefMetaData;
import org.jboss.metadata.WebMetaData;
import org.jboss.mx.loading.LoaderRepositoryFactory;
import org.jboss.naming.NonSerializableFactory;
import org.jboss.naming.Util;
import org.jboss.web.AbstractWebContainer.WebDescriptorParser;
import org.jboss.webservice.WebServiceClientHandler;
import org.omg.CORBA.ORB;
public abstract class AbstractWebDeployer
{
public static final String ERROR = "org.jboss.web.AbstractWebContainer.error";
protected Logger log;
protected MBeanServer server;
protected boolean java2ClassLoadingCompliance = false;
protected boolean unpackWars = true;
protected boolean lenientEjbLink = false;
protected String defaultSecurityDomain;
public AbstractWebDeployer()
{
log = Logger.getLogger(getClass());
}
public abstract void init(Object containerConfig) throws Exception;
public MBeanServer getServer()
{
return server;
}
public void setServer(MBeanServer server)
{
this.server = server;
}
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 getLenientEjbLink ()
{
return lenientEjbLink;
}
public void setLenientEjbLink (boolean flag)
{
lenientEjbLink = flag;
}
public String getDefaultSecurityDomain()
{
return defaultSecurityDomain;
}
public void setDefaultSecurityDomain(String defaultSecurityDomain)
{
this.defaultSecurityDomain = defaultSecurityDomain;
}
public synchronized WebApplication start(DeploymentInfo di) throws DeploymentException
{
Thread thread = Thread.currentThread();
ClassLoader appClassLoader = thread.getContextClassLoader();
WebApplication warInfo = null;
try
{
URL[] empty = {};
URLClassLoader warLoader = URLClassLoader.newInstance(empty, di.ucl);
thread.setContextClassLoader(warLoader);
WebDescriptorParser webAppParser = new DescriptorParser(di);
String webContext = di.webContext;
if (webContext != null && webContext.startsWith("/") == false)
webContext = "/" + webContext;
URL warURL = di.localUrl != null ? di.localUrl : di.url;
if (log.isDebugEnabled())
{
log.debug("webContext: " + webContext);
log.debug("warURL: " + warURL);
log.debug("webAppParser: " + webAppParser);
}
WebMetaData webMetaData = (WebMetaData) di.metaData;
if (di.parent != null && di.parent.metaData instanceof J2eeApplicationMetaData)
{
J2eeApplicationMetaData appMetaData = (J2eeApplicationMetaData)di.parent.metaData;
if (webMetaData.getSecurityDomain() == null)
webMetaData.setSecurityDomain(appMetaData.getSecurityDomain());
webMetaData.mergeSecurityRoles(appMetaData.getSecurityRoles());
}
String contextID = di.shortName;
if( contextID == null )
contextID = di.shortName;
webMetaData.setJaccContextID(contextID);
PolicyConfigurationFactory pcFactory = PolicyConfigurationFactory.getPolicyConfigurationFactory();
PolicyConfiguration pc = pcFactory.getPolicyConfiguration(contextID, true);
createPermissions(webMetaData, pc);
DeploymentInfo current = di;
while( current.parent != null )
current = current.parent;
PolicyConfiguration parentPC = (PolicyConfiguration)
current.context.get("javax.security.jacc.PolicyConfiguration");
if( parentPC != null && parentPC != pc )
parentPC.linkConfiguration(pc);
pc.commit();
Policy.getPolicy().refresh();
warInfo = new WebApplication(webMetaData);
warInfo.setDeploymentInfo(di);
warInfo.setClassLoader(warLoader);
performDeploy(warInfo, warURL.toString(), webAppParser);
}
catch(DeploymentException e)
{
di.context.put(ERROR, e);
throw e;
}
catch(Exception e)
{
DeploymentException ex = new DeploymentException("Error during deploy", e);
di.context.put(ERROR, ex);
throw ex;
}
finally
{
thread.setContextClassLoader(appClassLoader);
}
return warInfo;
}
protected abstract void performDeploy(WebApplication webApp, String warUrl,
WebDescriptorParser webAppParser) throws Exception;
public synchronized void stop(DeploymentInfo di)
throws DeploymentException
{
URL warURL = di.localUrl != null ? di.localUrl : di.url;
String warUrl = warURL.toString();
try
{
WebApplication webApp = (WebApplication) di.context.get(AbstractWebContainer.WEB_APP);
performUndeploy(warUrl, webApp);
WebMetaData webMetaData = (WebMetaData) di.metaData;
String contextID = webMetaData.getJaccContextID();
PolicyConfigurationFactory pcFactory = PolicyConfigurationFactory.getPolicyConfigurationFactory();
PolicyConfiguration pc = pcFactory.getPolicyConfiguration(contextID, true);
pc.delete();
}
catch(DeploymentException e)
{
throw e;
}
catch(Exception e)
{
throw new DeploymentException("Error during deploy", e);
}
}
protected abstract void performUndeploy(String warUrl, WebApplication webApp)
throws Exception;
protected void parseWebAppDescriptors(DeploymentInfo di, ClassLoader loader,
WebMetaData metaData)
throws Exception
{
log.debug("AbstractWebContainer.parseWebAppDescriptors, Begin");
InitialContext iniCtx = new InitialContext();
Context envCtx = null;
Thread currentThread = Thread.currentThread();
ClassLoader currentLoader = currentThread.getContextClassLoader();
try
{
log.debug("Creating ENC using ClassLoader: "+loader);
ClassLoader parent = loader.getParent();
while( parent != null )
{
log.debug(".."+parent);
parent = parent.getParent();
}
currentThread.setContextClassLoader(loader);
metaData.setENCLoader(loader);
envCtx = (Context) iniCtx.lookup("java:comp");
ORB orb = null;
try
{
orb = (ORB) server.getAttribute(Container.ORB_NAME, "ORB");
}
catch (Throwable t)
{
log.debug("Unable to retrieve orb" + t.toString());
}
if (orb != null)
{
NonSerializableFactory.rebind(envCtx, "ORB", orb);
log.debug("Bound java:comp/ORB");
}
envCtx.bind("UserTransaction", new LinkRef("UserTransaction"));
log.debug("Linked java:comp/UserTransaction to JNDI name: UserTransaction");
envCtx = envCtx.createSubcontext("env");
}
finally
{
currentThread.setContextClassLoader(currentLoader);
}
Iterator envEntries = metaData.getEnvironmentEntries();
log.debug("addEnvEntries");
addEnvEntries(envEntries, envCtx);
Iterator resourceEnvRefs = metaData.getResourceEnvReferences();
log.debug("linkResourceEnvRefs");
linkResourceEnvRefs(resourceEnvRefs, envCtx);
Iterator resourceRefs = metaData.getResourceReferences();
log.debug("linkResourceRefs");
linkResourceRefs(resourceRefs, envCtx);
log.debug("linkMessageDestinationRefs");
linkMessageDestinationRefs(metaData, envCtx, di);
Iterator ejbRefs = metaData.getEjbReferences();
log.debug("linkEjbRefs");
linkEjbRefs(ejbRefs, envCtx, di);
Iterator ejbLocalRefs = metaData.getEjbLocalReferences();
log.debug("linkEjbLocalRefs");
linkEjbLocalRefs(ejbLocalRefs, envCtx, di);
Iterator serviceRefs = metaData.getServiceReferences();
log.debug("linkServiceRefs");
WebServiceClientHandler.setupServiceRefEnvironment(envCtx, serviceRefs, di);
String securityDomain = metaData.getSecurityDomain();
log.debug("linkSecurityDomain");
linkSecurityDomain(securityDomain, envCtx);
log.debug("AbstractWebContainer.parseWebAppDescriptors, End");
}
protected void addEnvEntries(Iterator envEntries, Context envCtx)
throws ClassNotFoundException, NamingException
{
while( envEntries.hasNext() )
{
EnvEntryMetaData entry = (EnvEntryMetaData) envEntries.next();
log.debug("Binding env-entry: "+entry.getName()+" of type: " +
entry.getType()+" to value:"+entry.getValue());
EnvEntryMetaData.bindEnvEntry(envCtx, entry);
}
}
protected void linkResourceEnvRefs(Iterator resourceEnvRefs, Context envCtx)
throws NamingException
{
while( resourceEnvRefs.hasNext() )
{
ResourceEnvRefMetaData ref = (ResourceEnvRefMetaData) resourceEnvRefs.next();
String resourceName = ref.getJndiName();
String refName = ref.getRefName();
if( ref.getType().equals("java.net.URL") )
{
try
{
log.debug("Binding '"+refName+"' to URL: "+resourceName);
URL url = new URL(resourceName);
Util.bind(envCtx, refName, url);
}
catch(MalformedURLException e)
{
throw new NamingException("Malformed URL:"+e.getMessage());
}
}
else if( resourceName != null )
{
log.debug("Linking '"+refName+"' to JNDI name: "+resourceName);
Util.bind(envCtx, refName, new LinkRef(resourceName));
}
else
{
throw new NamingException("resource-env-ref: "+refName
+" has no valid JNDI binding. Check the jboss-web/resource-env-ref.");
}
}
}
protected void linkResourceRefs(Iterator resourceRefs, Context envCtx)
throws NamingException
{
while( resourceRefs.hasNext() )
{
ResourceRefMetaData ref = (ResourceRefMetaData) resourceRefs.next();
String jndiName = ref.getJndiName();
String refName = ref.getRefName();
if( ref.getType().equals("java.net.URL") )
{
try
{
String resURL = ref.getResURL();
if( ref.getResURL() != null )
{
log.debug("Binding '"+refName+"' to URL: "+resURL);
URL url = new URL(resURL);
Util.bind(envCtx, refName, url);
}
else
{
log.debug("Linking '"+refName+"' to URL: "+resURL);
LinkRef urlLink = new LinkRef(jndiName);
Util.bind(envCtx, refName, urlLink);
}
}
catch(MalformedURLException e)
{
throw new NamingException("Malformed URL:"+e.getMessage());
}
}
else if( jndiName != null )
{
log.debug("Linking '"+refName+"' to JNDI name: "+jndiName);
Util.bind(envCtx, refName, new LinkRef(jndiName));
}
else
{
throw new NamingException("resource-ref: "+refName
+" has no valid JNDI binding. Check the jboss-web/resource-ref.");
}
}
}
protected void linkMessageDestinationRefs(WebMetaData metaData, Context envCtx, DeploymentInfo di)
throws NamingException, DeploymentException
{
Iterator enum = metaData.getMessageDestinationReferences();
while (enum.hasNext())
{
MessageDestinationRefMetaData ref = (MessageDestinationRefMetaData) enum.next();
String refName = ref.getRefName();
String jndiName = ref.getJNDIName();
String link = ref.getLink();
if (link != null)
{
if (jndiName == null)
{
MessageDestinationMetaData messageDestination = EjbUtil.findMessageDestination(server, di, link);
if (messageDestination == null)
throw new DeploymentException("message-destination-ref '" + refName +
"' message-destination-link '" + link + "' not found and no jndi-name in jboss-web.xml");
else
{
String linkJNDIName = messageDestination.getJNDIName();
if (linkJNDIName == null)
log.warn("message-destination '" + link + "' has no jndi-name in jboss-web.xml");
else
jndiName = linkJNDIName;
}
}
else
log.warn("message-destination-ref '" + refName +
"' ignoring message-destination-link '" + link + "' because it has a jndi-name in jboss-web.xml");
}
else if (jndiName == null)
throw new DeploymentException("message-destination-ref '" + refName +
"' has no message-destination-link in web.xml and no jndi-name in jboss-web.xml");
Util.bind(envCtx, refName, new LinkRef(jndiName));
}
}
protected void linkEjbRefs(Iterator ejbRefs, Context envCtx, DeploymentInfo di)
throws NamingException
{
while( ejbRefs.hasNext() )
{
EjbRefMetaData ejb = (EjbRefMetaData) ejbRefs.next();
String name = ejb.getName();
String linkName = ejb.getLink();
String jndiName = null;
if ( linkName != null )
{
jndiName = EjbUtil.findEjbLink(server, di, linkName);
if ( ( jndiName == null ) && !(getLenientEjbLink()) )
throw new NamingException("ejb-ref: "+name+", no ejb-link match");
}
if ( jndiName == null )
{
jndiName = ejb.getJndiName();
if (jndiName == null )
throw new NamingException("ejb-ref: "+name+", no ejb-link in web.xml and no jndi-name in jboss-web.xml");
}
log.debug("Linking ejb-ref: "+name+" to JNDI name: "+jndiName);
Util.bind(envCtx, name, new LinkRef(jndiName));
}
}
protected void linkEjbLocalRefs(Iterator ejbRefs, Context envCtx, DeploymentInfo di)
throws NamingException
{
while( ejbRefs.hasNext() )
{
EjbLocalRefMetaData ejb = (EjbLocalRefMetaData) ejbRefs.next();
String name = ejb.getName();
String linkName = ejb.getLink();
String jndiName = null;
if ( linkName != null )
{
jndiName = EjbUtil.findLocalEjbLink(server, di, linkName);
if ( ( jndiName == null ) && !(getLenientEjbLink()) )
throw new NamingException("ejb-ref: "+name+", no ejb-link match");
}
if (jndiName == null)
{
jndiName = ejb.getJndiName();
if ( jndiName == null )
{
String msg = null;
if( linkName == null )
{
msg = "ejb-local-ref: '"+name+"', no ejb-link in web.xml and "
+ "no local-jndi-name in jboss-web.xml";
}
else
{
msg = "ejb-local-ref: '"+name+"', with web.xml ejb-link: '"
+ linkName + "' failed to resolve to an ejb with a LocalHome";
}
throw new NamingException(msg);
}
}
log.debug("Linking ejb-local-ref: "+name+" to JNDI name: "+jndiName);
Util.bind(envCtx, name, new LinkRef(jndiName));
}
}
protected void linkSecurityDomain(String securityDomain, Context envCtx)
throws NamingException
{
if( securityDomain == null )
{
securityDomain = getDefaultSecurityDomain();
log.debug("No security-domain given, using default: "+securityDomain);
}
log.debug("Linking security/securityMgr to JNDI name: "+securityDomain);
Util.bind(envCtx, "security/securityMgr", new LinkRef(securityDomain));
Util.bind(envCtx, "security/realmMapping", new LinkRef(securityDomain));
Util.bind(envCtx, "security/security-domain", new LinkRef(securityDomain));
Util.bind(envCtx, "security/subject", new LinkRef(securityDomain+"/subject"));
}
public String[] getStandardCompileClasspath(ClassLoader loader)
{
String[] jspResources = {
"javax/servlet/resources/web-app_2_3.dtd",
"org/apache/jasper/resources/jsp12.dtd",
"javax/ejb/EJBHome.class"
};
ArrayList tmp = new ArrayList();
for(int j = 0; j < jspResources.length; j ++)
{
URL rsrcURL = loader.getResource(jspResources[j]);
if( rsrcURL != null )
{
String url = rsrcURL.toExternalForm();
if( rsrcURL.getProtocol().equals("jar") )
{
url = url.substring(4);
int seperator = url.indexOf('!');
url = url.substring(0, seperator);
}
tmp.add(url);
}
else
{
log.warn("Failed to fin jsp rsrc: "+jspResources[j]);
}
}
log.trace("JSP StandardCompileClasspath: " + tmp);
String[] cp = new String[tmp.size()];
tmp.toArray(cp);
return cp;
}
public String[] getCompileClasspath(ClassLoader loader)
{
HashSet tmp = new HashSet();
ClassLoader cl = loader;
while( cl != null )
{
URL[] urls = AbstractWebContainer.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!", e);
} log.trace("JSP CompileClasspath: " + tmp);
String[] cp = new String[tmp.size()];
tmp.toArray(cp);
return cp;
}
private void addURLs(Set urlSet, URL[] urls)
{
for(int u = 0; u < urls.length; u ++)
{
URL url = urls[u];
urlSet.add(url.toExternalForm());
}
}
protected void createPermissions(WebMetaData metaData, PolicyConfiguration pc)
throws PolicyContextException
{
WebPermissionMapping.createPermissions(metaData, pc);
}
private class DescriptorParser implements WebDescriptorParser
{
DeploymentInfo di;
DescriptorParser(DeploymentInfo di)
{
this.di = di;
}
public void parseWebAppDescriptors(ClassLoader loader, WebMetaData metaData)
throws Exception
{
AbstractWebDeployer.this.parseWebAppDescriptors(di, loader, metaData);
}
public DeploymentInfo getDeploymentInfo()
{
return di;
}
}
}