blob: d59540145f88a618a2e95d5af2da8df96101782a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.servicemix.jbi.deployer.descriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* Factory to read a JBI descriptor from a file, url or stream.
*/
public class DescriptorFactory {
/**
* JAXP attribute value indicating the XSD schema language.
*/
private static final String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";
/**
* The location of the JBI descriptor in a JBI artifact.
*/
public static final String DESCRIPTOR_FILE = "META-INF/jbi.xml";
/**
* Build a jbi descriptor from a file archive.
*
* @param descriptorFile
* path to the jbi descriptor, or to the root directory
* @return the Descriptor object
*/
public static Descriptor buildDescriptor(File descriptorFile) {
if (descriptorFile.isDirectory()) {
descriptorFile = new File(descriptorFile, DESCRIPTOR_FILE);
}
if (descriptorFile.isFile()) {
try {
return buildDescriptor(descriptorFile.toURL());
} catch (MalformedURLException e) {
throw new RuntimeException("There is a bug here...", e);
}
}
return null;
}
/**
* Build a jbi descriptor from the specified URL
*
* @param url
* url to the jbi descriptor
* @return the Descriptor object
*/
public static Descriptor buildDescriptor(final URL url) {
try {
return buildDescriptor(url.openStream());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Build a jbi descriptor from the specified stream
*
* @param stream
* input stream to the jbi descriptor
* @return the Descriptor object
*/
public static Descriptor buildDescriptor(final InputStream stream) {
try {
// Read descriptor
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copyInputStream(stream, baos);
return buildDescriptor(baos.toByteArray());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Build a jbi descriptor from the specified binary data.
* The descriptor is validated against the schema, but no
* semantic validation is performed.
*
* @param bytes hold the content of the JBI descriptor xml document
* @return the Descriptor object
*/
public static Descriptor buildDescriptor(final byte[] bytes) {
try {
// Validate descriptor
SchemaFactory schemaFactory = SchemaFactory.newInstance(XSD_SCHEMA_LANGUAGE);
Schema schema = schemaFactory.newSchema(DescriptorFactory.class.getResource("jbi-descriptor.xsd"));
Validator validator = schema.newValidator();
validator.setErrorHandler(new ErrorHandler() {
public void warning(SAXParseException exception) throws SAXException {
//log.debug("Validation warning on " + url + ": " + exception);
}
public void error(SAXParseException exception) throws SAXException {
//log.info("Validation error on " + url + ": " + exception);
}
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
});
validator.validate(new StreamSource(new ByteArrayInputStream(bytes)));
// Parse descriptor
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(new ByteArrayInputStream(bytes));
Element jbi = doc.getDocumentElement();
Descriptor desc = new Descriptor();
desc.setVersion(Double.parseDouble(getAttribute(jbi, "version")));
Element child = getFirstChildElement(jbi);
if ("component".equals(child.getLocalName())) {
ComponentDesc component = new ComponentDesc();
component.setType(child.getAttribute("type"));
component.setComponentClassLoaderDelegation(getAttribute(child, "component-class-loader-delegation"));
component.setBootstrapClassLoaderDelegation(getAttribute(child, "bootstrap-class-loader-delegation"));
List<SharedLibraryList> sls = new ArrayList<SharedLibraryList>();
DocumentFragment ext = null;
for (Element e = getFirstChildElement(child); e != null; e = getNextSiblingElement(e)) {
if ("identification".equals(e.getLocalName())) {
component.setIdentification(readIdentification(e));
} else if ("component-class-name".equals(e.getLocalName())) {
component.setComponentClassName(getText(e));
component.setDescription(getAttribute(e, "description"));
} else if ("component-class-path".equals(e.getLocalName())) {
ClassPath componentClassPath = new ClassPath();
ArrayList<String> l = new ArrayList<String>();
for (Element e2 = getFirstChildElement(e); e2 != null; e2 = getNextSiblingElement(e2)) {
if ("path-element".equals(e2.getLocalName())) {
l.add(getText(e2));
}
}
componentClassPath.setPathList(l);
component.setComponentClassPath(componentClassPath);
} else if ("bootstrap-class-name".equals(e.getLocalName())) {
component.setBootstrapClassName(getText(e));
} else if ("bootstrap-class-path".equals(e.getLocalName())) {
ClassPath bootstrapClassPath = new ClassPath();
ArrayList<String> l = new ArrayList<String>();
for (Element e2 = getFirstChildElement(e); e2 != null; e2 = getNextSiblingElement(e2)) {
if ("path-element".equals(e2.getLocalName())) {
l.add(getText(e2));
}
}
bootstrapClassPath.setPathList(l);
component.setBootstrapClassPath(bootstrapClassPath);
} else if ("shared-library".equals(e.getLocalName())) {
SharedLibraryList sl = new SharedLibraryList();
sl.setName(getText(e));
sl.setVersion(getAttribute(e, "version"));
sls.add(sl);
} else {
if (ext == null) {
ext = doc.createDocumentFragment();
}
ext.appendChild(e);
}
}
component.setSharedLibraries(sls.toArray(new SharedLibraryList[sls.size()]));
if (ext != null) {
InstallationDescriptorExtension descriptorExtension = new InstallationDescriptorExtension();
descriptorExtension.setDescriptorExtension(ext);
component.setDescriptorExtension(descriptorExtension);
}
desc.setComponent(component);
} else if ("shared-library".equals(child.getLocalName())) {
SharedLibraryDesc sharedLibrary = new SharedLibraryDesc();
sharedLibrary.setClassLoaderDelegation(getAttribute(child, "class-loader-delegation"));
sharedLibrary.setVersion(getAttribute(child, "version"));
for (Element e = getFirstChildElement(child); e != null; e = getNextSiblingElement(e)) {
if ("identification".equals(e.getLocalName())) {
sharedLibrary.setIdentification(readIdentification(e));
} else if ("shared-library-class-path".equals(e.getLocalName())) {
ClassPath sharedLibraryClassPath = new ClassPath();
ArrayList<String> l = new ArrayList<String>();
for (Element e2 = getFirstChildElement(e); e2 != null; e2 = getNextSiblingElement(e2)) {
if ("path-element".equals(e2.getLocalName())) {
l.add(getText(e2));
}
}
sharedLibraryClassPath.setPathList(l);
sharedLibrary.setSharedLibraryClassPath(sharedLibraryClassPath);
}
}
desc.setSharedLibrary(sharedLibrary);
} else if ("service-assembly".equals(child.getLocalName())) {
ServiceAssemblyDesc serviceAssembly = new ServiceAssemblyDesc();
ArrayList<ServiceUnitDesc> sus = new ArrayList<ServiceUnitDesc>();
for (Element e = getFirstChildElement(child); e != null; e = getNextSiblingElement(e)) {
if ("identification".equals(e.getLocalName())) {
serviceAssembly.setIdentification(readIdentification(e));
} else if ("service-unit".equals(e.getLocalName())) {
ServiceUnitDesc su = new ServiceUnitDesc();
for (Element e2 = getFirstChildElement(e); e2 != null; e2 = getNextSiblingElement(e2)) {
if ("identification".equals(e2.getLocalName())) {
su.setIdentification(readIdentification(e2));
} else if ("target".equals(e2.getLocalName())) {
Target target = new Target();
for (Element e3 = getFirstChildElement(e2); e3 != null; e3 = getNextSiblingElement(e3)) {
if ("artifacts-zip".equals(e3.getLocalName())) {
target.setArtifactsZip(getText(e3));
} else if ("component-name".equals(e3.getLocalName())) {
target.setComponentName(getText(e3));
}
}
su.setTarget(target);
}
}
sus.add(su);
} else if ("connections".equals(e.getLocalName())) {
Connections connections = new Connections();
ArrayList<Connection> cns = new ArrayList<Connection>();
for (Element e2 = getFirstChildElement(e); e2 != null; e2 = getNextSiblingElement(e2)) {
if ("connection".equals(e2.getLocalName())) {
Connection cn = new Connection();
for (Element e3 = getFirstChildElement(e2); e3 != null; e3 = getNextSiblingElement(e3)) {
if ("consumer".equals(e3.getLocalName())) {
Consumer consumer = new Consumer();
consumer.setInterfaceName(readAttributeQName(e3, "interface-name"));
consumer.setServiceName(readAttributeQName(e3, "service-name"));
consumer.setEndpointName(getAttribute(e3, "endpoint-name"));
cn.setConsumer(consumer);
} else if ("provider".equals(e3.getLocalName())) {
Provider provider = new Provider();
provider.setServiceName(readAttributeQName(e3, "service-name"));
provider.setEndpointName(getAttribute(e3, "endpoint-name"));
cn.setProvider(provider);
}
}
cns.add(cn);
}
}
connections.setConnections(cns.toArray(new Connection[cns.size()]));
serviceAssembly.setConnections(connections);
}
}
serviceAssembly.setServiceUnits(sus.toArray(new ServiceUnitDesc[sus.size()]));
desc.setServiceAssembly(serviceAssembly);
} else if ("services".equals(child.getLocalName())) {
Services services = new Services();
services.setBindingComponent(Boolean.valueOf(getAttribute(child, "binding-component")).booleanValue());
ArrayList<Provides> provides = new ArrayList<Provides>();
ArrayList<Consumes> consumes = new ArrayList<Consumes>();
for (Element e = getFirstChildElement(child); e != null; e = getNextSiblingElement(e)) {
if ("provides".equals(e.getLocalName())) {
Provides p = new Provides();
p.setInterfaceName(readAttributeQName(e, "interface-name"));
p.setServiceName(readAttributeQName(e, "service-name"));
p.setEndpointName(getAttribute(e, "endpoint-name"));
provides.add(p);
} else if ("consumes".equals(e.getLocalName())) {
Consumes c = new Consumes();
c.setInterfaceName(readAttributeQName(e, "interface-name"));
c.setServiceName(readAttributeQName(e, "service-name"));
c.setEndpointName(getAttribute(e, "endpoint-name"));
c.setLinkType(getAttribute(e, "link-type"));
consumes.add(c);
}
}
services.setProvides(provides.toArray(new Provides[provides.size()]));
services.setConsumes(consumes.toArray(new Consumes[consumes.size()]));
desc.setServices(services);
}
checkDescriptor(desc);
return desc;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String getAttribute(Element e, String name) {
if (e.hasAttribute(name)) {
return e.getAttribute(name);
} else {
return null;
}
}
private static QName readAttributeQName(Element e, String name) {
String attr = getAttribute(e, name);
if (attr != null) {
return createQName(e, attr);
} else {
return null;
}
}
private static String getText(Element e) {
return getElementText(e).trim();
}
private static Identification readIdentification(Element e) {
Identification ident = new Identification();
for (Element e2 = getFirstChildElement(e); e2 != null; e2 = getNextSiblingElement(e2)) {
if ("name".equals(e2.getLocalName())) {
ident.setName(getElementText(e2));
} else if ("description".equals(e2.getLocalName())) {
ident.setDescription(getElementText(e2));
}
}
return ident;
}
/**
* Check validity of the JBI descriptor.
*
* @param descriptor
* the descriptor to check
* @throws Exception
* if the descriptor is not valid
*/
public static void checkDescriptor(Descriptor descriptor) {
List<String> violations = new ArrayList<String>();
if (descriptor.getVersion() != 1.0) {
violations.add("JBI descriptor version should be set to '1.0' but is " + descriptor.getVersion());
}
if (descriptor.getComponent() != null) {
checkComponent(violations, descriptor.getComponent());
} else if (descriptor.getServiceAssembly() != null) {
checkServiceAssembly(violations, descriptor.getServiceAssembly());
} else if (descriptor.getServices() != null) {
checkServiceUnit(violations, descriptor.getServices());
} else if (descriptor.getSharedLibrary() != null) {
checkSharedLibrary(violations, descriptor.getSharedLibrary());
} else {
violations.add("The jbi descriptor does not contain any informations");
}
if (violations.size() > 0) {
throw new RuntimeException("The JBI descriptor is not valid, please correct these violations "
+ violations.toString());
}
}
/**
* Checks that the component is valid
*
* @param violations
* A list of violations that the check can add to
*
* @param component
* The component descriptor that is being checked
*/
private static void checkComponent(List<String> violations, ComponentDesc component) {
if (component.getIdentification() == null) {
violations.add("The component has not identification");
} else {
if (isBlank(component.getIdentification().getName())) {
violations.add("The component name is not set");
}
}
if (component.getBootstrapClassName() == null) {
violations.add("The component has not defined a boot-strap class name");
}
if (component.getBootstrapClassPath() == null || component.getBootstrapClassPath().getPathElements() == null) {
violations.add("The component has not defined any boot-strap class path elements");
}
}
/**
* Checks that the service assembly is valid
*
* @param violations
* A list of violations that the check can add to
*
* @param serviceAssembly
* The service assembly descriptor that is being checked
*/
private static void checkServiceAssembly(List<String> violations, ServiceAssemblyDesc serviceAssembly) {
if (serviceAssembly.getIdentification() == null) {
violations.add("The service assembly has not identification");
} else {
if (isBlank(serviceAssembly.getIdentification().getName())) {
violations.add("The service assembly name is not set");
}
}
}
/**
* Checks that the service unit is valid
*
* @param violations
* A list of violations that the check can add to
*
* @param services
* The service unit descriptor that is being checked
*/
private static void checkServiceUnit(List<String> violations, Services services) {
// TODO Auto-generated method stub
}
/**
* Checks that the shared library is valid
*
* @param violations
* A list of violations that the check can add to
*
* @param sharedLibrary
* The shared library descriptor that is being checked
*/
private static void checkSharedLibrary(List<String> violations, SharedLibraryDesc sharedLibrary) {
if (sharedLibrary.getIdentification() == null) {
violations.add("The shared library has not identification");
} else {
if (isBlank(sharedLibrary.getIdentification().getName())) {
violations.add("The shared library name is not set");
}
}
}
/**
* Retrieves the jbi descriptor as a string
*
* @param descriptorFile
* path to the jbi descriptor, or to the root directory
* @return the contents of the jbi descriptor
*/
public static String getDescriptorAsText(File descriptorFile) {
if (descriptorFile.isDirectory()) {
descriptorFile = new File(descriptorFile, DESCRIPTOR_FILE);
}
if (descriptorFile.isFile()) {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
InputStream is = new FileInputStream(descriptorFile);
copyInputStream(is, os);
return os.toString();
} catch (Exception e) {
//log.debug("Error reading jbi descritor: " + descriptorFile, e);
}
}
return null;
}
/**
* <p>Checks if a String is whitespace, empty ("") or null.</p>
*
* <pre>
* StringUtils.isBlank(null) = true
* StringUtils.isBlank("") = true
* StringUtils.isBlank(" ") = true
* StringUtils.isBlank("bob") = false
* StringUtils.isBlank(" bob ") = false
* </pre>
*
* @param str the String to check, may be null
* @return <code>true</code> if the String is null, empty or whitespace
*
* Copied from org.apache.commons.lang.StringUtils#isBlanck
*/
private static boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
return true;
}
/**
* Copy in stream to an out stream
*
* @param in
* @param out
* @throws IOException
*/
public static void copyInputStream(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[4096];
int len;
while ((len = in.read(buffer)) >= 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
/**
* Creates a QName instance from the given namespace context for the given qualifiedName
*
* @param element the element to use as the namespace context
* @param qualifiedName the fully qualified name
* @return the QName which matches the qualifiedName
*/
public static QName createQName(Element element, String qualifiedName) {
int index = qualifiedName.indexOf(':');
if (index >= 0) {
String prefix = qualifiedName.substring(0, index);
String localName = qualifiedName.substring(index + 1);
String uri = recursiveGetAttributeValue(element, "xmlns:" + prefix);
return new QName(uri, localName, prefix);
}
else {
String uri = recursiveGetAttributeValue(element, "xmlns");
if (uri != null) {
return new QName(uri, qualifiedName);
}
return new QName(qualifiedName);
}
}
/**
* Recursive method to find a given attribute value
*/
public static String recursiveGetAttributeValue(Element element, String attributeName) {
String answer = null;
try {
answer = element.getAttribute(attributeName);
}
catch (Exception e) {
//if (log.isTraceEnabled()) {
// log.trace("Caught exception looking up attribute: " + attributeName + " on element: " + element + ". Cause: " + e, e);
//}
}
if (answer == null || answer.length() == 0) {
Node parentNode = element.getParentNode();
if (parentNode instanceof Element) {
return recursiveGetAttributeValue((Element) parentNode, attributeName);
}
}
return answer;
}
/**
* Returns the text of the element
*/
public static String getElementText(Element element) {
StringBuffer buffer = new StringBuffer();
NodeList nodeList = element.getChildNodes();
for (int i = 0, size = nodeList.getLength(); i < size; i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) {
buffer.append(node.getNodeValue());
}
}
return buffer.toString();
}
/**
* Get the first child element
* @param parent
* @return
*/
public static Element getFirstChildElement(Node parent) {
NodeList childs = parent.getChildNodes();
for (int i = 0; i < childs.getLength(); i++) {
Node child = childs.item(i);
if (child instanceof Element) {
return (Element) child;
}
}
return null;
}
/**
* Get the next sibling element
* @param el
* @return
*/
public static Element getNextSiblingElement(Element el) {
for (Node n = el.getNextSibling(); n != null; n = n.getNextSibling()) {
if (n instanceof Element) {
return (Element) n;
}
}
return null;
}
}