/**
*
* Copyright 2003-2005 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.deployment.service;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.xml.namespace.QName;
import org.apache.geronimo.common.DeploymentException;
import org.apache.geronimo.deployment.ConfigurationBuilder;
import org.apache.geronimo.deployment.DeploymentContext;
import org.apache.geronimo.deployment.xbeans.AttributeType;
import org.apache.geronimo.deployment.xbeans.ClassFilterType;
import org.apache.geronimo.deployment.xbeans.ConfigurationDocument;
import org.apache.geronimo.deployment.xbeans.ConfigurationType;
import org.apache.geronimo.deployment.xbeans.DependencyType;
import org.apache.geronimo.deployment.xbeans.GbeanType;
import org.apache.geronimo.deployment.xbeans.ReferenceType;
import org.apache.geronimo.deployment.xbeans.ReferencesType;
import org.apache.geronimo.deployment.xbeans.ServiceDocument;
import org.apache.geronimo.deployment.xbeans.XmlAttributeType;
import org.apache.geronimo.deployment.xbeans.PatternType;
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.gbean.ReferenceMap;
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.config.ConfigurationData;
import org.apache.geronimo.kernel.config.ConfigurationModuleType;
import org.apache.geronimo.kernel.repository.MissingDependencyException;
import org.apache.geronimo.kernel.repository.Repository;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
/**
* @version $Rev$ $Date$
*/
public class ServiceConfigBuilder implements ConfigurationBuilder {
private final List defaultParentId;
private final Repository repository;
private final Kernel kernel;
//TODO this being static is a really good argument that all other builders should have a reference to this gbean, not use static methods on it.
private static final Map xmlAttributeBuilderMap = new HashMap();
private static final Map xmlReferenceBuilderMap = new HashMap();
private Map attrRefMap;
private Map refRefMap;
private static final QName SERVICE_QNAME = new QName("http://geronimo.apache.org/xml/ns/deployment-1.0", "configuration");
public ServiceConfigBuilder(URI[] defaultParentId, Repository repository) {
this(defaultParentId, repository, null, null, null);
}
public ServiceConfigBuilder(URI[] defaultParentId, Repository repository, Collection xmlAttributeBuilders, Collection xmlReferenceBuilders, Kernel kernel) {
this.defaultParentId = defaultParentId == null? Collections.EMPTY_LIST: Arrays.asList(defaultParentId);
this.repository = repository;
this.kernel = kernel;
if (xmlAttributeBuilders != null) {
ReferenceMap.Key key = new ReferenceMap.Key() {
public Object getKey(Object object) {
return ((XmlAttributeBuilder) object).getNamespace();
}
};
attrRefMap = new ReferenceMap(xmlAttributeBuilders, xmlAttributeBuilderMap, key);
}
if (xmlReferenceBuilders != null) {
ReferenceMap.Key key = new ReferenceMap.Key() {
public Object getKey(Object object) {
return ((XmlReferenceBuilder) object).getNamespace();
}
};
refRefMap = new ReferenceMap(xmlReferenceBuilders, xmlReferenceBuilderMap, key);
}
}
public Object getDeploymentPlan(File planFile, JarFile module) throws DeploymentException {
if (planFile == null) {
return null;
}
try {
XmlObject xmlObject = XmlBeansUtil.parse(planFile);
XmlCursor cursor = xmlObject.newCursor();
try {
cursor.toFirstChild();
if (!SERVICE_QNAME.equals(cursor.getName())) {
return null;
}
} finally {
cursor.dispose();
}
ConfigurationDocument configurationDoc;
if (xmlObject instanceof ConfigurationDocument) {
configurationDoc = (ConfigurationDocument) xmlObject;
} else {
configurationDoc = (ConfigurationDocument) xmlObject.changeType(ConfigurationDocument.type);
}
Collection errors = new ArrayList();
if (!configurationDoc.validate(XmlBeansUtil.createXmlOptions(errors))) {
throw new DeploymentException("Invalid deployment descriptor: " + errors + "\nDescriptor: " + configurationDoc.toString());
}
return configurationDoc.getConfiguration();
} catch (XmlException e) {
throw new DeploymentException("Could not parse xml in plan", e);
} catch (IOException e) {
throw new DeploymentException("no plan at " + planFile, e);
}
}
public URI getConfigurationID(Object plan, JarFile module) throws IOException, DeploymentException {
ConfigurationType configType = (ConfigurationType) plan;
try {
return new URI(configType.getConfigId());
} catch (URISyntaxException e) {
throw new DeploymentException("Invalid configId " + configType.getConfigId(), e);
}
}
public ConfigurationData buildConfiguration(Object plan, JarFile unused, File outfile) throws IOException, DeploymentException {
ConfigurationType configType = (ConfigurationType) plan;
String domain = null;
String server = null;
return buildConfiguration(configType, domain, server, outfile);
}
public ConfigurationData buildConfiguration(ConfigurationType configType, String domain, String server, File outfile) throws DeploymentException, IOException {
List parentID = getParentID(configType.getParentId(), configType.getImportArray());
if (parentID == null || parentID.size() == 0) {
if (configType.isSetDomain()) {
if (!configType.isSetServer()) {
throw new DeploymentException("You must set both domain and server");
}
domain = configType.getDomain();
server = configType.getServer();
} else {
parentID = defaultParentId;
}
}
if (domain == null) {
//get from parent id
if (kernel == null) {
throw new DeploymentException("You must supply a kernel or the domain and server names");
}
}
URI configID;
try {
configID = new URI(configType.getConfigId());
} catch (URISyntaxException e) {
throw new DeploymentException("Invalid configId " + configType.getConfigId(), e);
}
DeploymentContext context = null;
context = new DeploymentContext(outfile, configID, ConfigurationModuleType.SERVICE, parentID, domain, server, kernel);
J2eeContext j2eeContext = new J2eeContextImpl(context.getDomain(), context.getServer(), NameFactory.NULL, NameFactory.J2EE_MODULE, configID.toString(), null, null);
DependencyType[] includes = configType.getIncludeArray();
addIncludes(context, includes, repository);
addDependencies(context, configType.getDependencyArray(), repository);
ClassLoader cl = context.getClassLoader(repository);
GbeanType[] gbeans = configType.getGbeanArray();
addGBeans(gbeans, cl, j2eeContext, context);
if (configType.isSetInverseClassloading()) {
context.setInverseClassloading(configType.getInverseClassloading());
}
ClassFilterType[] filters = configType.getHiddenClassesArray();
addHiddenClasses(context, filters);
filters = configType.getNonOverridableClassesArray();
addNonOverridableClasses(context, filters);
context.close();
return context.getConfigurationData();
}
public static void addHiddenClasses(DeploymentContext context, ClassFilterType[] filters) throws DeploymentException {
Set tmpFilters = new HashSet();
for (int i = 0; i < filters.length; i++) {
tmpFilters.add(filters[i].getFilter());
}
context.addHiddenClasses(tmpFilters);
}
public static void addNonOverridableClasses(DeploymentContext context, ClassFilterType[] filters) throws DeploymentException {
Set tmpFilters = new HashSet();
for (int i = 0; i < filters.length; i++) {
tmpFilters.add(filters[i].getFilter());
}
context.addNonOverridableClasses(tmpFilters);
}
public static List getParentID(String parentIDString, DependencyType[] imports) throws DeploymentException {
List uris = new ArrayList();
if (parentIDString != null) {
try {
uris.add(new URI(parentIDString));
} catch (URISyntaxException e) {
throw new DeploymentException("Invalid parentId " + parentIDString, e);
}
} else if (imports.length == 0) {
return new ArrayList();
}
for (int i = 0; i < imports.length; i++) {
DependencyType anImport = imports[i];
URI parentURI = getDependencyURI(anImport);
uris.add(parentURI);
}
return uris;
}
public static void addIncludes(DeploymentContext context, DependencyType[] includes, Repository repository) throws DeploymentException {
for (int i = 0; i < includes.length; i++) {
DependencyType include = includes[i];
URI uri = getDependencyURI(include, repository);
String name = getDependencyFileName(include);
URI path;
try {
path = new URI(name);
} catch (URISyntaxException e) {
throw new DeploymentException("Unable to generate path for include: " + uri, e);
}
try {
URL url = repository.getURL(uri);
context.addInclude(path, url);
} catch (IOException e) {
throw new DeploymentException("Unable to add include: " + uri, e);
}
}
}
public static void addDependencies(DeploymentContext context, DependencyType[] deps, Repository repository) throws DeploymentException {
for (int i = 0; i < deps.length; i++) {
URI dependencyURI = getDependencyURI(deps[i], repository);
context.addDependency(dependencyURI);
URL url;
try {
url = repository.getURL(dependencyURI);
} catch (MalformedURLException e) {
throw new DeploymentException("Unable to get URL for dependency " + dependencyURI, e);
}
ClassLoader depCL = new URLClassLoader(new URL[]{url}, ClassLoader.getSystemClassLoader());
InputStream is = depCL.getResourceAsStream("META-INF/geronimo-service.xml");
if (is != null) {
// it has a geronimo-service.xml file
ServiceDocument serviceDoc = null;
try {
Collection errors = new ArrayList();
serviceDoc = ServiceDocument.Factory.parse(is, XmlBeansUtil.createXmlOptions(errors));
if (errors.size() > 0) {
throw new DeploymentException("Invalid service doc: " + errors);
}
} catch (XmlException e) {
throw new DeploymentException("Invalid geronimo-service.xml file in " + url, e);
} catch (IOException e) {
throw new DeploymentException("Unable to parse geronimo-service.xml file in " + url, e);
}
DependencyType[] dependencyDeps = serviceDoc.getService().getDependencyArray();
if (dependencyDeps != null) {
addDependencies(context, dependencyDeps, repository);
}
}
}
}
public static void addGBeans(GbeanType[] gbeans, ClassLoader cl, J2eeContext j2eeContext, DeploymentContext context) throws DeploymentException {
for (int i = 0; i < gbeans.length; i++) {
addGBeanData(gbeans[i], j2eeContext, cl, context);
}
}
public static ObjectName addGBeanData(GbeanType gbean, J2eeContext j2eeContext, ClassLoader cl, DeploymentContext context) throws DeploymentException {
GBeanInfo gBeanInfo = GBeanInfo.getGBeanInfo(gbean.getClass1(), cl);
ObjectName objectName;
if (gbean.isSetGbeanName()) {
try {
objectName = ObjectName.getInstance(gbean.getGbeanName());
} catch (MalformedObjectNameException e) {
throw new DeploymentException("Invalid ObjectName: " + gbean.getName(), e);
}
} else {
String namePart = gbean.getName();
try {
String j2eeType = gBeanInfo.getJ2eeType();
//todo investigate using the module type from the j2eecontext.
objectName = NameFactory.getComponentName(null, null, null, null, namePart, j2eeType, j2eeContext);
} catch (MalformedObjectNameException e) {
throw new DeploymentException("Invalid ObjectName: " + namePart, e);
}
}
GBeanBuilder builder = new GBeanBuilder(objectName, gBeanInfo, cl, context, j2eeContext, xmlAttributeBuilderMap, xmlReferenceBuilderMap);
// set up attributes
AttributeType[] attributeArray = gbean.getAttributeArray();
if (attributeArray != null) {
for (int j = 0; j < attributeArray.length; j++) {
builder.setAttribute(attributeArray[j].getName().trim(), attributeArray[j].getType(), attributeArray[j].getStringValue());
}
}
XmlAttributeType[] xmlAttributeArray = gbean.getXmlAttributeArray();
if (xmlAttributeArray != null) {
for (int i = 0; i < xmlAttributeArray.length; i++) {
XmlAttributeType xmlAttributeType = xmlAttributeArray[i];
String name = xmlAttributeType.getName().trim();
XmlObject[] anys = xmlAttributeType.selectChildren(XmlAttributeType.type.qnameSetForWildcardElements());
if (anys.length != 1) {
throw new DeploymentException("Unexpected count of xs:any elements in xml-attribute " + anys.length + " qnameset: " + XmlAttributeType.type.qnameSetForWildcardElements());
}
builder.setXmlAttribute(name, anys[0]);
}
}
// set up all single pattern references
ReferenceType[] referenceArray = gbean.getReferenceArray();
if (referenceArray != null) {
for (int j = 0; j < referenceArray.length; j++) {
builder.setReference(referenceArray[j].getName2(), referenceArray[j], j2eeContext);
}
}
// set up app multi-patterned references
ReferencesType[] referencesArray = gbean.getReferencesArray();
if (referencesArray != null) {
for (int j = 0; j < referencesArray.length; j++) {
builder.setReference(referencesArray[j].getName(), referencesArray[j].getPatternArray(), j2eeContext);
}
}
XmlAttributeType[] xmlReferenceArray = gbean.getXmlReferenceArray();
if (xmlReferenceArray != null) {
for (int i = 0; i < xmlReferenceArray.length; i++) {
XmlAttributeType xmlAttributeType = xmlReferenceArray[i];
String name = xmlAttributeType.getName().trim();
XmlObject[] anys = xmlAttributeType.selectChildren(XmlAttributeType.type.qnameSetForWildcardElements());
if (anys.length != 1) {
throw new DeploymentException("Unexpected count of xs:any elements in xml-attribute " + anys.length + " qnameset: " + XmlAttributeType.type.qnameSetForWildcardElements());
}
builder.setXmlReference(name, anys[0]);
}
}
PatternType[] dependencyArray = gbean.getDependencyArray();
if (dependencyArray != null) {
for (int i = 0; i < dependencyArray.length; i++) {
PatternType patternType = dependencyArray[i];
builder.addDependency(patternType, j2eeContext);
}
}
GBeanData gBeanData = builder.getGBeanData();
context.addGBean(gBeanData);
return objectName;
}
private static URI getDependencyURI(DependencyType dep, Repository repository) throws DeploymentException {
URI uri = getDependencyURI(dep);
if (!repository.hasURI(uri)) {
throw new DeploymentException(new MissingDependencyException("uri " + uri + " not found in repository"));
}
return uri;
}
private static URI getDependencyURI(DependencyType dep) throws DeploymentException {
URI uri;
if (dep.isSetUri()) {
try {
uri = new URI(dep.getUri().trim());
} catch (URISyntaxException e) {
throw new DeploymentException("Invalid dependency URI " + dep.getUri().trim(), e);
}
} else {
String groupId = dep.getGroupId().trim();
String type = dep.isSetType() ? dep.getType().trim() : "jar";
String artifactId = dep.getArtifactId().trim();
String version = dep.getVersion().trim();
String id = groupId + "/" + artifactId + "/" + version + "/" + type;
try {
uri = new URI(id);
} catch (URISyntaxException e) {
throw new DeploymentException("Unable to construct URI for groupId=" + groupId + ", artifactId=" + artifactId + ", version=" + version + ", type=" + type, e);
}
}
return uri;
}
private static String getDependencyFileName(DependencyType dep) throws DeploymentException {
String name;
if (dep.isSetUri()) {
name = dep.getUri().trim();
} else {
String groupId = dep.getGroupId().trim();
String type = dep.isSetType() ? dep.getType().trim() : "jar";
String artifactId = dep.getArtifactId().trim();
String version = dep.getVersion().trim();
name = artifactId + "-" + version + "." + type;
}
return name;
}
public static final GBeanInfo GBEAN_INFO;
static {
GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(ServiceConfigBuilder.class, NameFactory.CONFIG_BUILDER);
infoFactory.addInterface(ConfigurationBuilder.class);
infoFactory.addAttribute("defaultParentId", URI[].class, true);
infoFactory.addReference("Repository", Repository.class, NameFactory.GERONIMO_SERVICE);
infoFactory.addReference("XmlAttributeBuilders", XmlAttributeBuilder.class, "XmlAttributeBuilder");
infoFactory.addReference("XmlReferenceBuilders", XmlReferenceBuilder.class, "XmlReferenceBuilder");
infoFactory.addAttribute("kernel", Kernel.class, false);
infoFactory.setConstructor(new String[]{"defaultParentId", "Repository", "XmlAttributeBuilders", "XmlReferenceBuilders", "kernel"});
GBEAN_INFO = infoFactory.getBeanInfo();
}
public static GBeanInfo getGBeanInfo() {
return GBEAN_INFO;
}
}