Java tutorial
/* * 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.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"; private static final String VERSION = "version"; private static final String TYPE = "type"; private static final String COMPONENT = "component"; private static final String COMPONENT_CLASS_LOADER_DELEGATION = "component-class-loader-delegation"; private static final String BOOTSTRAP_CLASS_LOADER_DELEGATION = "bootstrap-class-loader-delegation"; private static final String DESCRIPTION = "description"; private static final String IDENTIFICATION = "identification"; private static final String COMPONENT_CLASS_NAME = "component-class-name"; private static final String COMPONENT_CLASS_PATH = "component-class-path"; private static final String PATH_ELEMENT = "path-element"; private static final String SHARED_LIBRARY = "shared-library"; private static final String BOOTSTRAP_CLASS_PATH = "bootstrap-class-path"; private static final String BOOTSTRAP_CLASS_NAME = "bootstrap-class-name"; private static final String CLASS_LOADER_DELEGATION = "class-loader-delegation"; private static final String SERVICE_ASSEMBLY = "service-assembly"; private static final String SERVICE_UNIT = "service-unit"; private static final String TARGET = "target"; private static final String ARTIFACTS_ZIP = "artifacts-zip"; private static final String COMPONENT_NAME = "component-name"; private static final String CONNECTIONS = "connections"; private static final String CONNECTION = "connection"; private static final String CONSUMER = "consumer"; private static final String PROVIDER = "provider"; private static final String INTERFACE_NAME = "interface-name"; private static final String SERVICE_NAME = "service-name"; private static final String ENDPOINT_NAME = "endpoint-name"; private static final String BINDING_COMPONENT = "binding-component"; private static final String SERVICES = "services"; private static final String PROVIDES = "provides"; private static final String CONSUMES = "consumes"; private static final String LINK_TYPE = "link-type"; private static final String NAME = "name"; private static final String JBI_DESCRIPTOR_XSD = "jbi-descriptor.xsd"; private static final String SHARED_LIBRARY_CLASS_PATH = "shared-library-class-path"; /** * 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 validate service unit } /** * 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 { return getDescriptorAsText(descriptorFile.toURL()); } catch (MalformedURLException e) { //log.debug("Error reading jbi descritor: " + descriptorFile, e); } } return null; } /** * Retrieves the jbi descriptor as a string * * @param descriptorURL URL pointing to the JBI descriptor * @return the contents of the jbi descriptor */ public static String getDescriptorAsText(URL descriptorURL) { try { ByteArrayOutputStream os = new ByteArrayOutputStream(); InputStream is = descriptorURL.openStream(); 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> * <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 * <p/> * 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; } }