/**
*
* Copyright 2003-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.geronimo.tomcat.deployment;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.security.jacc.WebResourcePermission;
import javax.security.jacc.WebRoleRefPermission;
import javax.security.jacc.WebUserDataPermission;
import javax.transaction.UserTransaction;
import javax.xml.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.common.DeploymentException;
import org.apache.geronimo.deployment.service.ServiceConfigBuilder;
import org.apache.geronimo.deployment.util.DeploymentUtil;
import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
import org.apache.geronimo.deployment.xbeans.ClassFilterType;
import org.apache.geronimo.deployment.xbeans.DependencyType;
import org.apache.geronimo.deployment.xbeans.GbeanType;
import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
import org.apache.geronimo.gbean.GBeanData;
import org.apache.geronimo.gbean.GBeanInfo;
import org.apache.geronimo.gbean.GBeanInfoBuilder;
import org.apache.geronimo.j2ee.deployment.EARContext;
import org.apache.geronimo.j2ee.deployment.Module;
import org.apache.geronimo.j2ee.deployment.ModuleBuilder;
import org.apache.geronimo.j2ee.deployment.WebModule;
import org.apache.geronimo.j2ee.deployment.WebServiceBuilder;
import org.apache.geronimo.j2ee.j2eeobjectnames.J2eeContext;
import org.apache.geronimo.j2ee.j2eeobjectnames.J2eeContextImpl;
import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
import org.apache.geronimo.kernel.Kernel;
import org.apache.geronimo.kernel.StoredObject;
import org.apache.geronimo.kernel.repository.Repository;
import org.apache.geronimo.naming.deployment.ENCConfigBuilder;
import org.apache.geronimo.naming.deployment.GBeanResourceEnvironmentBuilder;
import org.apache.geronimo.schema.SchemaConversionUtils;
import org.apache.geronimo.security.deploy.DefaultPrincipal;
import org.apache.geronimo.security.deployment.SecurityBuilder;
import org.apache.geronimo.security.deployment.SecurityConfiguration;
import org.apache.geronimo.security.jacc.ComponentPermissions;
import org.apache.geronimo.security.util.URLPattern;
import org.apache.geronimo.tomcat.ManagerGBean;
import org.apache.geronimo.tomcat.RealmGBean;
import org.apache.geronimo.tomcat.TomcatClassLoader;
import org.apache.geronimo.tomcat.TomcatWebAppContext;
import org.apache.geronimo.tomcat.ValveGBean;
import org.apache.geronimo.tomcat.cluster.CatalinaClusterGBean;
import org.apache.geronimo.tomcat.util.SecurityHolder;
import org.apache.geronimo.transaction.context.OnlineUserTransaction;
import org.apache.geronimo.web.deployment.GenericToSpecificPlanConverter;
import org.apache.geronimo.web.deployment.AbstractWebModuleBuilder;
import org.apache.geronimo.xbeans.geronimo.naming.GerMessageDestinationType;
import org.apache.geronimo.xbeans.geronimo.web.tomcat.TomcatWebAppDocument;
import org.apache.geronimo.xbeans.geronimo.web.tomcat.TomcatWebAppType;
import org.apache.geronimo.xbeans.geronimo.web.tomcat.config.GerTomcatDocument;
import org.apache.geronimo.xbeans.j2ee.FilterMappingType;
import org.apache.geronimo.xbeans.j2ee.HttpMethodType;
import org.apache.geronimo.xbeans.j2ee.MessageDestinationType;
import org.apache.geronimo.xbeans.j2ee.RoleNameType;
import org.apache.geronimo.xbeans.j2ee.SecurityConstraintType;
import org.apache.geronimo.xbeans.j2ee.SecurityRoleRefType;
import org.apache.geronimo.xbeans.j2ee.SecurityRoleType;
import org.apache.geronimo.xbeans.j2ee.ServletMappingType;
import org.apache.geronimo.xbeans.j2ee.ServletType;
import org.apache.geronimo.xbeans.j2ee.UrlPatternType;
import org.apache.geronimo.xbeans.j2ee.WebAppDocument;
import org.apache.geronimo.xbeans.j2ee.WebAppType;
import org.apache.geronimo.xbeans.j2ee.WebResourceCollectionType;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
/**
* @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $
*/
public class TomcatModuleBuilder extends AbstractWebModuleBuilder {
private static final Log log = LogFactory.getLog(TomcatModuleBuilder.class);
private final List defaultParentId;
private final boolean defaultContextPriorityClassloader;
private final ObjectName tomcatContainerObjectName;
private final WebServiceBuilder webServiceBuilder;
private final Repository repository;
private static final String TOMCAT_NAMESPACE = TomcatWebAppDocument.type.getDocumentElementName().getNamespaceURI();
public TomcatModuleBuilder(URI[] defaultParentId,
boolean defaultContextPriorityClassloader,
ObjectName tomcatContainerObjectName,
WebServiceBuilder webServiceBuilder,
Repository repository) {
this.defaultParentId = defaultParentId == null ? Collections.EMPTY_LIST : Arrays.asList(defaultParentId);
this.defaultContextPriorityClassloader = defaultContextPriorityClassloader;
this.tomcatContainerObjectName = tomcatContainerObjectName;
this.webServiceBuilder = webServiceBuilder;
this.repository = repository;
}
public Module createModule(File plan, JarFile moduleFile) throws DeploymentException {
return createModule(plan, moduleFile, "war", null, true, null);
}
public Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, URI earConfigId, Object moduleContextInfo) throws DeploymentException {
return createModule(plan, moduleFile, targetPath, specDDUrl, false, (String) moduleContextInfo);
}
private Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, boolean standAlone, String contextRoot) throws DeploymentException {
assert moduleFile != null: "moduleFile is null";
assert targetPath != null: "targetPath is null";
assert !targetPath.endsWith("/"): "targetPath must not end with a '/'";
// parse the spec dd
String specDD;
WebAppType webApp;
try {
if (specDDUrl == null) {
specDDUrl = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/web.xml");
}
// read in the entire specDD as a string, we need this for getDeploymentDescriptor
// on the J2ee management object
specDD = DeploymentUtil.readAll(specDDUrl);
} catch (Exception e) {
//no web.xml, not for us
return null;
}
//we found web.xml, if it won't parse that's an error.
try {
// parse it
XmlObject parsed = XmlBeansUtil.parse(specDD);
WebAppDocument webAppDoc = SchemaConversionUtils.convertToServletSchema(parsed);
webApp = webAppDoc.getWebApp();
} catch (XmlException xmle) {
throw new DeploymentException("Error parsing web.xml", xmle);
}
check(webApp);
// parse vendor dd
TomcatWebAppType tomcatWebApp = getTomcatWebApp(plan, moduleFile, standAlone, targetPath, webApp);
// get the ids from either the application plan or for a stand alone module from the specific deployer
URI configId = null;
try {
configId = new URI(tomcatWebApp.getConfigId());
} catch (URISyntaxException e) {
throw new DeploymentException("Invalid configId " + tomcatWebApp.getConfigId(), e);
}
List parentId = ServiceConfigBuilder.getParentID(tomcatWebApp.getParentId(), tomcatWebApp.getImportArray());
if (parentId.isEmpty()) {
parentId = new ArrayList(defaultParentId);
}
if (contextRoot == null) {
if (tomcatWebApp.isSetContextRoot()) {
contextRoot = tomcatWebApp.getContextRoot();
} else {
contextRoot = determineDefaultContextRoot(webApp, standAlone, moduleFile, targetPath);
}
}
//look for a webservices dd
Map portMap = Collections.EMPTY_MAP;
//TODO Jeff, please review
Map servletNameToPathMap = buildServletNameToPathMap(webApp, contextRoot);
if (webServiceBuilder != null) {
try {
URL wsDDUrl = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/webservices.xml");
portMap = webServiceBuilder.parseWebServiceDescriptor(wsDDUrl, moduleFile, false, servletNameToPathMap);
} catch (MalformedURLException e) {
//no descriptor
}
}
WebModule module = new WebModule(standAlone, configId, parentId, moduleFile, targetPath, webApp, tomcatWebApp, specDD, contextRoot, portMap, TOMCAT_NAMESPACE);
return module;
}
/**
* Some servlets will have multiple url patterns. However, webservice servlets
* will only have one, which is what this method is intended for.
*
* @param webApp
* @param contextRoot
* @return
*/
private Map buildServletNameToPathMap(WebAppType webApp, String contextRoot) {
contextRoot = "/" + contextRoot;
Map map = new HashMap();
ServletMappingType[] servletMappings = webApp.getServletMappingArray();
for (int j = 0; j < servletMappings.length; j++) {
ServletMappingType servletMapping = servletMappings[j];
String servletName = servletMapping.getServletName().getStringValue().trim();
map.put(servletName, contextRoot + servletMapping.getUrlPattern().getStringValue().trim());
}
return map;
}
TomcatWebAppType getTomcatWebApp(Object plan, JarFile moduleFile, boolean standAlone, String targetPath, WebAppType webApp) throws DeploymentException {
XmlObject rawPlan = null;
try {
// load the geronimo-web.xml from either the supplied plan or from the earFile
try {
if (plan instanceof XmlObject) {
rawPlan = (XmlObject) plan;
} else {
if (plan != null) {
rawPlan = XmlBeansUtil.parse(((File) plan).toURL());
} else {
URL path = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/geronimo-web.xml");
try {
rawPlan = XmlBeansUtil.parse(path);
} catch (FileNotFoundException e) {
path = DeploymentUtil.createJarURL(moduleFile, "WEB-INF/geronimo-tomcat.xml");
try {
rawPlan = XmlBeansUtil.parse(path);
} catch (FileNotFoundException e1) {
log.warn("Web application does not contain a WEB-INF/geronimo-web.xml deployment plan. This may or may not be a problem, depending on whether you have things like resource references that need to be resolved. You can also give the deployer a separate deployment plan file on the command line.");
}
}
}
}
} catch (IOException e) {
log.warn(e);
}
TomcatWebAppType tomcatWebApp = null;
if (rawPlan != null) {
XmlObject webPlan = new GenericToSpecificPlanConverter(GerTomcatDocument.type.getDocumentElementName().getNamespaceURI(),
TomcatWebAppDocument.type.getDocumentElementName().getNamespaceURI(), "tomcat").convertToSpecificPlan(rawPlan);
tomcatWebApp = (TomcatWebAppType) webPlan.changeType(TomcatWebAppType.type);
SchemaConversionUtils.validateDD(tomcatWebApp);
} else {
String defaultContextRoot = determineDefaultContextRoot(webApp, standAlone, moduleFile, targetPath);
tomcatWebApp = createDefaultPlan(defaultContextRoot);
}
return tomcatWebApp;
} catch (XmlException e) {
throw new DeploymentException("xml problem", e);
}
}
private String determineDefaultContextRoot(WebAppType webApp, boolean isStandAlone, JarFile moduleFile, String targetPath) {
if (webApp != null && webApp.getId() != null) {
return webApp.getId();
}
if (isStandAlone) {
// default configId is based on the moduleFile name
return trimPath(new File(moduleFile.getName()).getName());
}
// default configId is based on the module uri from the application.xml
return trimPath(targetPath);
}
private String trimPath(String path) {
if (path == null) {
return null;
}
if (path.endsWith(".war")) {
path = path.substring(0, path.length() - 4);
}
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
return path;
}
private TomcatWebAppType createDefaultPlan(String path) {
TomcatWebAppType tomcatWebApp = TomcatWebAppType.Factory.newInstance();
tomcatWebApp.setConfigId(path);
tomcatWebApp.setContextRoot("/" + path);
tomcatWebApp.setContextPriorityClassloader(defaultContextPriorityClassloader);
return tomcatWebApp;
}
public void installModule(JarFile earFile, EARContext earContext, Module module) throws DeploymentException {
TomcatWebAppType tomcatWebApp = (TomcatWebAppType) module.getVendorDD();
earContext.addParentId(defaultParentId);
try {
URI baseDir = URI.create(module.getTargetPath() + "/");
// add the warfile's content to the configuration
JarFile warFile = module.getModuleFile();
Enumeration entries = warFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
URI targetPath = baseDir.resolve(new URI(null, entry.getName(), null));
if (entry.getName().equals("WEB-INF/web.xml")) {
earContext.addFile(targetPath, module.getOriginalSpecDD());
} else {
earContext.addFile(targetPath, warFile, entry);
}
}
// add the manifest classpath entries declared in the war to the class loader
// we have to explicitly add these since we are unpacking the web module
// and the url class loader will not pick up a manifiest from an unpacked dir
earContext.addManifestClassPath(warFile, URI.create(module.getTargetPath()));
// add the dependencies declared in the geronimo-web.xml file
DependencyType[] dependencies = tomcatWebApp.getDependencyArray();
ServiceConfigBuilder.addDependencies(earContext, dependencies, repository);
} catch (IOException e) {
throw new DeploymentException("Problem deploying war", e);
} catch (URISyntaxException e) {
throw new DeploymentException("Could not construct URI for location of war entry", e);
}
if (tomcatWebApp.isSetInverseClassloading()) {
earContext.setInverseClassloading(tomcatWebApp.getInverseClassloading());
}
ClassFilterType[] filters = tomcatWebApp.getHiddenClassesArray();
ServiceConfigBuilder.addHiddenClasses(earContext, filters);
filters = tomcatWebApp.getNonOverridableClassesArray();
ServiceConfigBuilder.addNonOverridableClasses(earContext, filters);
}
public void initContext(EARContext earContext, Module module, ClassLoader cl) throws DeploymentException {
WebAppType webApp = (WebAppType) module.getSpecDD();
MessageDestinationType[] messageDestinations = webApp.getMessageDestinationArray();
TomcatWebAppType gerWebApp = (TomcatWebAppType) module.getVendorDD();
GerMessageDestinationType[] gerMessageDestinations = gerWebApp.getMessageDestinationArray();
ENCConfigBuilder.registerMessageDestinations(earContext.getRefContext(), module.getName(), messageDestinations, gerMessageDestinations);
if (gerWebApp.isSetSecurity()) {
if (!gerWebApp.isSetSecurityRealmName()) {
throw new DeploymentException("You have supplied a security configuration for web app " + module.getName() + " but no security-realm-name to allow login");
}
SecurityConfiguration securityConfiguration = SecurityBuilder.buildSecurityConfiguration(gerWebApp.getSecurity(), cl);
earContext.setSecurityConfiguration(securityConfiguration);
}
}
public void addGBeans(EARContext earContext, Module module, ClassLoader cl) throws DeploymentException {
J2eeContext earJ2eeContext = earContext.getJ2eeContext();
J2eeContext moduleJ2eeContext = J2eeContextImpl.newModuleContextFromApplication(earJ2eeContext, NameFactory.WEB_MODULE, module.getName());
WebModule webModule = (WebModule) module;
WebAppType webApp = (WebAppType) webModule.getSpecDD();
TomcatWebAppType tomcatWebApp = (TomcatWebAppType) webModule.getVendorDD();
boolean contextPriorityClassLoader = defaultContextPriorityClassloader;
if (tomcatWebApp != null && tomcatWebApp.isSetContextPriorityClassloader()) {
contextPriorityClassLoader = tomcatWebApp.getContextPriorityClassloader();
}
// construct the webClassLoader
ClassLoader webClassLoader = getWebClassLoader(earContext, webModule, cl, contextPriorityClassLoader);
if (tomcatWebApp != null) {
GbeanType[] gbeans = tomcatWebApp.getGbeanArray();
ServiceConfigBuilder.addGBeans(gbeans, webClassLoader, moduleJ2eeContext, earContext);
}
ObjectName webModuleName = null;
try {
webModuleName = NameFactory.getModuleName(null, null, null, null, null, moduleJ2eeContext);
} catch (MalformedObjectNameException e) {
throw new DeploymentException("Could not construct module name", e);
}
UserTransaction userTransaction = new OnlineUserTransaction();
//this may add to the web classpath with enhanced classes.
Map compContext = buildComponentContext(earContext, webModule, webApp, tomcatWebApp, userTransaction, webClassLoader);
GBeanData webModuleData = new GBeanData(webModuleName, TomcatWebAppContext.GBEAN_INFO);
try {
webModuleData.setReferencePattern("J2EEServer", earContext.getServerObjectName());
if (!earContext.getJ2EEApplicationName().equals("null")) {
webModuleData.setReferencePattern("J2EEApplication", earContext.getApplicationObjectName());
}
webModuleData.setAttribute("deploymentDescriptor", module.getOriginalSpecDD());
Set securityRoles = collectRoleNames(webApp);
Map rolePermissions = new HashMap();
URI baseUri = URI.create(webModule.getTargetPath() + "/");
webModuleData.setAttribute("webAppRoot", baseUri);
webModuleData.setAttribute("contextPath", webModule.getContextRoot());
//Add dependencies on managed connection factories and ejbs in this app
//This is overkill, but allows for people not using java:comp context (even though we don't support it)
//and sidesteps the problem of circular references between ejbs.
Set dependencies = findGBeanDependencies(earContext);
webModuleData.getDependencies().addAll(dependencies);
webModuleData.setAttribute("componentContext", compContext);
webModuleData.setAttribute("userTransaction", userTransaction);
//classpath may have been augmented with enhanced classes
webModuleData.setAttribute("webClassPath", webModule.getWebClasspath());
// unsharableResources, applicationManagedSecurityResources
GBeanResourceEnvironmentBuilder rebuilder = new GBeanResourceEnvironmentBuilder(webModuleData);
ENCConfigBuilder.setResourceEnvironment(earContext, webModule.getModuleURI(), rebuilder, webApp.getResourceRefArray(), tomcatWebApp.getResourceRefArray());
webModuleData.setAttribute("contextPriorityClassLoader", Boolean.valueOf(contextPriorityClassLoader));
webModuleData.setReferencePattern("TransactionContextManager", earContext.getTransactionContextManagerObjectName());
webModuleData.setReferencePattern("TrackedConnectionAssociator", earContext.getConnectionTrackerObjectName());
webModuleData.setReferencePattern("Container", tomcatContainerObjectName);
// Process the Tomcat container-config elements
if (tomcatWebApp.isSetHost()) {
String virtualServer = tomcatWebApp.getHost().trim();
webModuleData.setAttribute("virtualServer", virtualServer);
}
if (tomcatWebApp.isSetCrossContext()) {
webModuleData.setAttribute("crossContext", Boolean.TRUE);
}
if (tomcatWebApp.isSetTomcatRealm()) {
String tomcatRealm = tomcatWebApp.getTomcatRealm().trim();
ObjectName realmName = NameFactory.getComponentName(null, null, null, null, tomcatRealm, RealmGBean.GBEAN_INFO.getJ2eeType(), moduleJ2eeContext);
webModuleData.setReferencePattern("TomcatRealm", realmName);
}
if (tomcatWebApp.isSetValveChain()) {
String valveChain = tomcatWebApp.getValveChain().trim();
ObjectName valveName = NameFactory.getComponentName(null, null, null, null, valveChain, ValveGBean.J2EE_TYPE, moduleJ2eeContext);
webModuleData.setReferencePattern("TomcatValveChain", valveName);
}
if (tomcatWebApp.isSetCluster()) {
String cluster = tomcatWebApp.getCluster().trim();
ObjectName clusterName = NameFactory.getComponentName(null, null, null, null, cluster, CatalinaClusterGBean.J2EE_TYPE, moduleJ2eeContext);
webModuleData.setReferencePattern("Cluster", clusterName);
}
if (tomcatWebApp.isSetManager()) {
String manager = tomcatWebApp.getManager().trim();
ObjectName managerName = NameFactory.getComponentName(null, null, null, null, manager, ManagerGBean.J2EE_TYPE, moduleJ2eeContext);
webModuleData.setReferencePattern("Manager", managerName);
}
Map portMap = webModule.getPortMap();
//Handle the role permissions and webservices on the servlets.
ServletType[] servletTypes = webApp.getServletArray();
Map webServices = new HashMap();
for (int i = 0; i < servletTypes.length; i++) {
ServletType servletType = servletTypes[i];
//Handle the Role Ref Permissions
processRoleRefPermissions(servletType, securityRoles, rolePermissions);
//Do we have webservices configured?
if (portMap != null) {
//Check if the Servlet is a Webservice
String servletName = servletType.getServletName().getStringValue().trim();
if (portMap.containsKey(servletName)) {
//Yes, this servlet is a web service so let the web service builder
// deal with configuring the web service stack
String servletClassName = servletType.getServletClass().getStringValue().trim();
Object portInfo = portMap.get(servletName);
if (portInfo == null) {
throw new DeploymentException("No web service deployment info for servlet name " + servletName);
}
StoredObject wsContainer = configurePOJO(webModule.getModuleFile(), portInfo, servletClassName, webClassLoader);
webServices.put(servletName, wsContainer);
}
}
}
// JACC v1.0 secion B.19
addUnmappedJSPPermissions(securityRoles, rolePermissions);
webModuleData.setAttribute("webServices", webServices);
if (tomcatWebApp.isSetSecurityRealmName()) {
if (earContext.getSecurityConfiguration() == null) {
throw new DeploymentException("You have specified a login security realm for the webapp " + webModuleName + " but no security configuration is supplied in the application plan");
}
SecurityHolder securityHolder = new SecurityHolder();
securityHolder.setSecurityRealm(tomcatWebApp.getSecurityRealmName().trim());
/**
* TODO - go back to commented version when possible.
*/
String policyContextID = webModuleName.getCanonicalName().replaceAll("[, :]", "_");
securityHolder.setPolicyContextID(policyContextID);
ComponentPermissions componentPermissions = buildSpecSecurityConfig(webApp, securityRoles, rolePermissions);
securityHolder.setExcluded(componentPermissions.getExcludedPermissions());
PermissionCollection checkedPermissions = new Permissions();
for (Iterator iterator = rolePermissions.values().iterator(); iterator.hasNext();) {
PermissionCollection permissionsForRole = (PermissionCollection) iterator.next();
for (Enumeration iterator2 = permissionsForRole.elements(); iterator2.hasMoreElements();) {
Permission permission = (Permission) iterator2.nextElement();
checkedPermissions.add(permission);
}
}
securityHolder.setChecked(checkedPermissions);
earContext.addSecurityContext(policyContextID, componentPermissions);
DefaultPrincipal defaultPrincipal = earContext.getSecurityConfiguration().getDefaultPrincipal();
securityHolder.setDefaultPrincipal(defaultPrincipal);
if (defaultPrincipal != null) {
securityHolder.setSecurity(true);
}
webModuleData.setAttribute("securityHolder", securityHolder);
webModuleData.setReferencePattern("RoleDesignateSource", earContext.getJaccManagerName());
}
earContext.addGBean(webModuleData);
} catch (DeploymentException de) {
throw de;
} catch (Exception e) {
throw new DeploymentException("Unable to initialize webapp GBean", e);
}
}
public String getSchemaNamespace() {
return TOMCAT_NAMESPACE;
}
private ClassLoader getWebClassLoader(EARContext earContext, WebModule webModule, ClassLoader cl, boolean contextPriorityClassLoader) throws DeploymentException {
getWebClassPath(earContext, webModule);
URI[] webClassPath = webModule.getWebClasspath();
URI baseUri = earContext.getBaseDir().toURI();
URL baseUrl = null;
try {
baseUrl = baseUri.resolve(webModule.getTargetPathURI()).toURL();
} catch (MalformedURLException e) {
throw new DeploymentException("Invalid module location: " + webModule.getTargetPathURI() + ", baseUri: " + baseUri);
}
URL[] webClassPathURLs = new URL[webClassPath.length];
for (int i = 0; i < webClassPath.length; i++) {
URI path = baseUri.resolve(webClassPath[i]);
try {
webClassPathURLs[i] = path.toURL();
} catch (MalformedURLException e) {
throw new DeploymentException("Invalid web class path element: path=" + path + ", baseUri=" + baseUri);
}
}
ClassLoader webClassLoader = new TomcatClassLoader(webClassPathURLs, baseUrl, cl, contextPriorityClassLoader);
return webClassLoader;
}
private void addUnmappedJSPPermissions(Set securityRoles, Map rolePermissions) {
for (Iterator iter = securityRoles.iterator(); iter.hasNext();) {
String roleName = (String) iter.next();
addPermissionToRole(roleName, new WebRoleRefPermission("", roleName), rolePermissions);
}
}
private void processRoleRefPermissions(ServletType servletType,
Set securityRoles,
Map rolePermissions) {
String servletName = servletType.getServletName().getStringValue().trim();
//WebRoleRefPermissions
SecurityRoleRefType[] securityRoleRefTypeArray = servletType.getSecurityRoleRefArray();
Set unmappedRoles = new HashSet(securityRoles);
for (int j = 0; j < securityRoleRefTypeArray.length; j++) {
SecurityRoleRefType securityRoleRefType = securityRoleRefTypeArray[j];
String roleName = securityRoleRefType.getRoleName().getStringValue().trim();
String roleLink = securityRoleRefType.getRoleLink().getStringValue().trim();
//jacc 3.1.3.2
addPermissionToRole(roleLink, new WebRoleRefPermission(servletName, roleName), rolePermissions);
unmappedRoles.remove(roleName);
}
for (Iterator iterator = unmappedRoles.iterator(); iterator.hasNext();) {
String roleName = (String) iterator.next();
addPermissionToRole(roleName, new WebRoleRefPermission(servletName, roleName), rolePermissions);
}
// servletData.setAttribute("webRoleRefPermissions", webRoleRefPermissions);
}
private ComponentPermissions buildSpecSecurityConfig(WebAppType webApp, Set securityRoles, Map rolePermissions) {
Map uncheckedPatterns = new HashMap();
Map uncheckedResourcePatterns = new HashMap();
Map uncheckedUserPatterns = new HashMap();
Map excludedPatterns = new HashMap();
Map rolesPatterns = new HashMap();
Set allSet = new HashSet(); // == allMap.values()
Map allMap = new HashMap(); //uncheckedPatterns union excludedPatterns union rolesPatterns.
SecurityConstraintType[] securityConstraintArray = webApp.getSecurityConstraintArray();
for (int i = 0; i < securityConstraintArray.length; i++) {
SecurityConstraintType securityConstraintType = securityConstraintArray[i];
Map currentPatterns;
if (securityConstraintType.isSetAuthConstraint()) {
if (securityConstraintType.getAuthConstraint().getRoleNameArray().length == 0) {
currentPatterns = excludedPatterns;
} else {
currentPatterns = rolesPatterns;
}
} else {
currentPatterns = uncheckedPatterns;
}
String transport = "";
if (securityConstraintType.isSetUserDataConstraint()) {
transport = securityConstraintType.getUserDataConstraint().getTransportGuarantee().getStringValue().trim().toUpperCase();
}
WebResourceCollectionType[] webResourceCollectionTypeArray = securityConstraintType.getWebResourceCollectionArray();
for (int j = 0; j < webResourceCollectionTypeArray.length; j++) {
WebResourceCollectionType webResourceCollectionType = webResourceCollectionTypeArray[j];
UrlPatternType[] urlPatternTypeArray = webResourceCollectionType.getUrlPatternArray();
for (int k = 0; k < urlPatternTypeArray.length; k++) {
UrlPatternType urlPatternType = urlPatternTypeArray[k];
//presumably, don't trim
String url = urlPatternType.getStringValue().trim();
URLPattern pattern = (URLPattern) currentPatterns.get(url);
if (pattern == null) {
pattern = new URLPattern(url);
currentPatterns.put(url, pattern);
}
URLPattern allPattern = (URLPattern) allMap.get(url);
if (allPattern == null) {
allPattern = new URLPattern(url);
allSet.add(allPattern);
allMap.put(url, allPattern);
}
HttpMethodType[] httpMethodTypeArray = webResourceCollectionType.getHttpMethodArray();
if (httpMethodTypeArray.length == 0) {
pattern.addMethod("");
allPattern.addMethod("");
} else {
for (int l = 0; l < httpMethodTypeArray.length; l++) {
HttpMethodType httpMethodType = httpMethodTypeArray[l];
//TODO is trim OK?
String method = httpMethodType.getStringValue().trim();
pattern.addMethod(method);
allPattern.addMethod(method);
}
}
if (currentPatterns == rolesPatterns) {
RoleNameType[] roleNameTypeArray = securityConstraintType.getAuthConstraint().getRoleNameArray();
for (int l = 0; l < roleNameTypeArray.length; l++) {
RoleNameType roleNameType = roleNameTypeArray[l];
String role = roleNameType.getStringValue().trim();
if (role.equals("*")) {
pattern.addAllRoles(securityRoles);
} else {
pattern.addRole(role);
}
}
}
pattern.setTransport(transport);
}
}
}
PermissionCollection excludedPermissions = new Permissions();
PermissionCollection uncheckedPermissions = new Permissions();
Iterator iter = excludedPatterns.keySet().iterator();
while (iter.hasNext()) {
URLPattern pattern = (URLPattern) excludedPatterns.get(iter.next());
String name = pattern.getQualifiedPattern(allSet);
String actions = pattern.getMethods();
excludedPermissions.add(new WebResourcePermission(name, actions));
excludedPermissions.add(new WebUserDataPermission(name, actions));
}
iter = rolesPatterns.keySet().iterator();
while (iter.hasNext()) {
URLPattern pattern = (URLPattern) rolesPatterns.get(iter.next());
String name = pattern.getQualifiedPattern(allSet);
String actions = pattern.getMethods();
WebResourcePermission permission = new WebResourcePermission(name, actions);
for (Iterator names = pattern.getRoles().iterator(); names.hasNext();) {
String roleName = (String) names.next();
addPermissionToRole(roleName, permission, rolePermissions);
}
}
iter = uncheckedPatterns.keySet().iterator();
while (iter.hasNext()) {
URLPattern pattern = (URLPattern) uncheckedPatterns.get(iter.next());
String name = pattern.getQualifiedPattern(allSet);
String actions = pattern.getMethods();
addOrUpdatePattern(uncheckedResourcePatterns, name, actions);
}
iter = rolesPatterns.keySet().iterator();
while (iter.hasNext()) {
URLPattern pattern = (URLPattern) rolesPatterns.get(iter.next());
String name = pattern.getQualifiedPattern(allSet);
String actions = pattern.getMethodsWithTransport();
addOrUpdatePattern(uncheckedUserPatterns, name, actions);
}
iter = uncheckedPatterns.keySet().iterator();
while (iter.hasNext()) {
URLPattern pattern = (URLPattern) uncheckedPatterns.get(iter.next());
String name = pattern.getQualifiedPattern(allSet);
String actions = pattern.getMethodsWithTransport();
addOrUpdatePattern(uncheckedUserPatterns, name, actions);
}
/**
* A <code>WebResourcePermission</code> and a <code>WebUserDataPermission</code> must be instantiated for
* each <tt>url-pattern</tt> in the deployment descriptor and the default pattern "/", that is not combined
* by the <tt>web-resource-collection</tt> elements of the deployment descriptor with ever HTTP method
* value. The permission objects must be contructed using the qualified pattern as their name and with
* actions defined by the subset of the HTTP methods that do not occur in combination with the pattern.
* The resulting permissions that must be added to the unchecked policy statements by calling the
* <code>addToUncheckedPolcy</code> method on the <code>PolicyConfiguration</code> object.
*/
iter = allSet.iterator();
while (iter.hasNext()) {
URLPattern pattern = (URLPattern) iter.next();
String name = pattern.getQualifiedPattern(allSet);
String actions = pattern.getComplementedMethods();
if (actions.length() == 0) {
continue;
}
addOrUpdatePattern(uncheckedResourcePatterns, name, actions);
addOrUpdatePattern(uncheckedUserPatterns, name, actions);
}
URLPattern pattern = new URLPattern("/");
if (!allSet.contains(pattern)) {
String name = pattern.getQualifiedPattern(allSet);
String actions = pattern.getComplementedMethods();
addOrUpdatePattern(uncheckedResourcePatterns, name, actions);
addOrUpdatePattern(uncheckedUserPatterns, name, actions);
}
//Create the uncheckedPermissions for WebResourcePermissions
iter = uncheckedResourcePatterns.keySet().iterator();
while (iter.hasNext()) {
UncheckedItem item = (UncheckedItem) iter.next();
String actions = (String) uncheckedResourcePatterns.get(item);
uncheckedPermissions.add(new WebResourcePermission(item.getName(), actions));
}
//Create the uncheckedPermissions for WebUserDataPermissions
iter = uncheckedUserPatterns.keySet().iterator();
while (iter.hasNext()) {
UncheckedItem item = (UncheckedItem) iter.next();
String actions = (String) uncheckedUserPatterns.get(item);
uncheckedPermissions.add(new WebUserDataPermission(item.getName(), actions));
}
ComponentPermissions componentPermissions = new ComponentPermissions(excludedPermissions, uncheckedPermissions, rolePermissions);
return componentPermissions;
}
private void addPermissionToRole(String roleName, Permission permission, Map rolePermissions) {
PermissionCollection permissionsForRole = (PermissionCollection) rolePermissions.get(roleName);
if (permissionsForRole == null) {
permissionsForRole = new Permissions();
rolePermissions.put(roleName, permissionsForRole);
}
permissionsForRole.add(permission);
}
private void addOrUpdatePattern(Map patternMap, String name, String actions) {
UncheckedItem item = new UncheckedItem(name, actions);
String existingActions = (String) patternMap.get(item);
if (existingActions != null) {
patternMap.put(item, actions + "," + existingActions);
return;
}
patternMap.put(item, actions);
}
private static Set collectRoleNames(WebAppType webApp) {
Set roleNames = new HashSet();
SecurityRoleType[] securityRoles = webApp.getSecurityRoleArray();
for (int i = 0; i < securityRoles.length; i++) {
roleNames.add(securityRoles[i].getRoleName().getStringValue().trim());
}
return roleNames;
}
private static void getWebClassPath(EARContext earContext, WebModule webModule) {
File baseDir = earContext.getTargetFile(webModule.getTargetPathURI());
File webInfDir = new File(baseDir, "WEB-INF");
// check for a classes dir
File classesDir = new File(webInfDir, "classes");
if (classesDir.isDirectory()) {
webModule.addToWebClasspath(webModule.getTargetPathURI().resolve(URI.create("WEB-INF/classes/")));
}
// add all of the libs
File libDir = new File(webInfDir, "lib");
if (libDir.isDirectory()) {
File[] libs = libDir.listFiles(new FileFilter() {
public boolean accept(File file) {
return file.isFile() && file.getName().endsWith(".jar");
}
});
if (libs != null) {
for (int i = 0; i < libs.length; i++) {
File lib = libs[i];
webModule.addToWebClasspath(webModule.getTargetPathURI().resolve(URI.create("WEB-INF/lib/" + lib.getName())));
}
}
}
}
private Map buildComponentContext(EARContext earContext, Module webModule, WebAppType webApp, TomcatWebAppType tomcatWebApp, UserTransaction userTransaction, ClassLoader cl) throws DeploymentException {
return ENCConfigBuilder.buildComponentContext(earContext,
earContext,
webModule,
userTransaction,
webApp.getEnvEntryArray(),
webApp.getEjbRefArray(), tomcatWebApp.getEjbRefArray(),
webApp.getEjbLocalRefArray(), tomcatWebApp.getEjbLocalRefArray(),
webApp.getResourceRefArray(), tomcatWebApp.getResourceRefArray(),
webApp.getResourceEnvRefArray(), tomcatWebApp.getResourceEnvRefArray(),
webApp.getMessageDestinationRefArray(),
webApp.getServiceRefArray(), tomcatWebApp.getServiceRefArray(),
cl);
}
private static void check(WebAppType webApp) throws DeploymentException {
checkURLPattern(webApp);
checkMultiplicities(webApp);
}
private static void checkURLPattern(WebAppType webApp) throws DeploymentException {
FilterMappingType[] filterMappings = webApp.getFilterMappingArray();
for (int i = 0; i < filterMappings.length; i++) {
if (filterMappings[i].isSetUrlPattern()) {
checkString(filterMappings[i].getUrlPattern().getStringValue().trim());
}
}
ServletMappingType[] servletMappings = webApp.getServletMappingArray();
for (int i = 0; i < servletMappings.length; i++) {
checkString(servletMappings[i].getUrlPattern().getStringValue().trim());
}
SecurityConstraintType[] constraints = webApp.getSecurityConstraintArray();
for (int i = 0; i < constraints.length; i++) {
WebResourceCollectionType[] collections = constraints[i].getWebResourceCollectionArray();
for (int j = 0; j < collections.length; j++) {
UrlPatternType[] patterns = collections[j].getUrlPatternArray();
for (int k = 0; k < patterns.length; k++) {
checkString(patterns[k].getStringValue().trim());
}
}
}
}
private static void checkString(String pattern) throws DeploymentException {
//j2ee_1_4.xsd explicitly requires preserving all whitespace. Do not trim.
if (pattern.indexOf(0x0D) >= 0) throw new DeploymentException("<url-pattern> must not contain CR(#xD)");
if (pattern.indexOf(0x0A) >= 0) throw new DeploymentException("<url-pattern> must not contain LF(#xA)");
}
private static void checkMultiplicities(WebAppType webApp) throws DeploymentException {
if (webApp.getSessionConfigArray().length > 1) throw new DeploymentException("Multiple <session-config> elements found");
if (webApp.getJspConfigArray().length > 1) throw new DeploymentException("Multiple <jsp-config> elements found");
if (webApp.getLoginConfigArray().length > 1) throw new DeploymentException("Multiple <login-config> elements found");
}
public StoredObject configurePOJO(JarFile moduleFile, Object portInfoObject, String seiClassName, ClassLoader classLoader) throws DeploymentException, IOException {
//the reason to configure a gbeandata rather than just fetch the WebServiceContainer is that fetching the WSContainer ties us to that
//ws implementation. By configuring a servlet gbean, you can provide a different servlet for each combination of
//web container and ws implementation while assuming almost nothing about their relationship.
GBeanData fakeData = new GBeanData();
webServiceBuilder.configurePOJO(fakeData, moduleFile, portInfoObject, seiClassName, classLoader);
return (StoredObject) fakeData.getAttribute("webServiceContainer");
}
class UncheckedItem {
final static int NA = 0x00;
final static int INTEGRAL = 0x01;
final static int CONFIDENTIAL = 0x02;
private int transportType = NA;
private String name;
public UncheckedItem(String name, String actions) {
setName(name);
setTransportType(actions);
}
public boolean equals(Object o) {
UncheckedItem item = (UncheckedItem) o;
return item.getKey().equals(this.getKey());
}
public String getKey() {
return (name + transportType);
}
public int hashCode() {
return getKey().hashCode();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getTransportType() {
return transportType;
}
public void setTransportType(String actions) {
String[] tokens = actions.split(":", 2);
if (tokens.length == 2) {
if (tokens[1].equals("INTEGRAL")) {
this.transportType = INTEGRAL;
} else if (tokens[1].equals("CONFIDENTIAL")) {
this.transportType = CONFIDENTIAL;
}
}
}
}
public static final GBeanInfo GBEAN_INFO;
static {
GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(TomcatModuleBuilder.class, NameFactory.MODULE_BUILDER);
infoBuilder.addAttribute("defaultParentId", URI[].class, true, true);
infoBuilder.addAttribute("defaultContextPriorityClassloader", boolean.class, true, true);
infoBuilder.addAttribute("tomcatContainerObjectName", ObjectName.class, true, true);
infoBuilder.addReference("WebServiceBuilder", WebServiceBuilder.class, NameFactory.MODULE_BUILDER);
infoBuilder.addReference("Repository", Repository.class, NameFactory.GERONIMO_SERVICE);
infoBuilder.addInterface(ModuleBuilder.class);
infoBuilder.setConstructor(new String[]{
"defaultParentId",
"defaultContextPriorityClassloader",
"tomcatContainerObjectName",
"WebServiceBuilder",
"Repository"});
GBEAN_INFO = infoBuilder.getBeanInfo();
}
public static GBeanInfo getGBeanInfo() {
return GBEAN_INFO;
}
}