All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.milyn.javabean.dynamic.Descriptor Maven / Gradle / Ivy

The newest version!
/*
 * Milyn - Copyright (C) 2006 - 2010
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License (version 2.1) as published by the Free Software
 *  Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 *  See the GNU Lesser General Public License for more details:
 *  http://www.gnu.org/licenses/lgpl.txt
 */

package org.milyn.javabean.dynamic;

import org.milyn.Smooks;
import org.milyn.SmooksException;
import org.milyn.assertion.AssertArgument;
import org.milyn.cdr.SmooksConfigurationException;
import org.milyn.cdr.SmooksResourceConfiguration;
import org.milyn.cdr.SmooksResourceConfigurationList;
import org.milyn.cdr.XMLConfigDigester;
import org.milyn.cdr.xpath.SelectorStep;
import org.milyn.javabean.dynamic.ext.BeanWriterFactory;
import org.milyn.javabean.dynamic.resolvers.AbstractResolver;
import org.milyn.javabean.dynamic.resolvers.DefaultBindingConfigResolver;
import org.milyn.javabean.dynamic.resolvers.DefaultSchemaResolver;
import org.milyn.javabean.dynamic.serialize.BeanWriter;
import org.milyn.util.ClassUtil;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;

/**
 * Model Descriptor.
 * 
 * @author [email protected]
 */
public class Descriptor {

    public static final String DESCRIPTOR_NAMESPACE_POSTFIX = ".namespace";
    public static final String DESCRIPTOR_SCHEMA_LOCATION_POSTFIX = ".schemaLocation";
    public static final String DESCRIPTOR_BINDING_CONFIG_LOCATION_POSTFIX = ".bindingConfigLocation";
    public static final String DESCRIPTOR_ORDER_POSTFIX = ".order";

    private Smooks smooks;
    private Schema schema;
    private ClassLoader classloader = Descriptor.class.getClassLoader();

    public Descriptor(List descriptors) throws SAXException, IOException {
        AssertArgument.isNotNullAndNotEmpty(descriptors, "descriptors");

        intialize(descriptors, new DefaultSchemaResolver(descriptors), new DefaultBindingConfigResolver(descriptors));
    }

    public Descriptor(String descriptorPath) throws SAXException, IOException {
		AssertArgument.isNotNullAndNotEmpty(descriptorPath, "descriptorPath");

		List descriptors = loadDescriptors(descriptorPath, getClass().getClassLoader());
		intialize(descriptors, new DefaultSchemaResolver(descriptors), new DefaultBindingConfigResolver(descriptors));
	}

	public Descriptor(String descriptorPath, EntityResolver schemaResolver, EntityResolver bindingResolver, ClassLoader classloader) throws SAXException, IOException {
		AssertArgument.isNotNullAndNotEmpty(descriptorPath, "descriptorPath");
		AssertArgument.isNotNull(bindingResolver, "bindingResolver");
        AssertArgument.isNotNull(classloader, "classloader");

        this.classloader = classloader;

		List descriptors = loadDescriptors(descriptorPath, classloader);
		intialize(descriptors, schemaResolver, bindingResolver);
	}

    public Descriptor(List descriptors, EntityResolver schemaResolver, EntityResolver bindingResolver, ClassLoader classloader) throws SAXException, IOException {
        AssertArgument.isNotNullAndNotEmpty(descriptors, "descriptors");
        AssertArgument.isNotNull(bindingResolver, "bindingResolver");
        AssertArgument.isNotNull(classloader, "classloader");

        this.classloader = classloader;

        intialize(descriptors, schemaResolver, bindingResolver);
    }

    public Smooks getSmooks() {
        return smooks;
    }

    public Schema getSchema() {
        return schema;
    }

    public Map, Map> getBeanWriters() {
        return BeanWriterFactory.getBeanWriters(smooks.getApplicationContext());
    }

    public static List loadDescriptors(String descriptorPath, ClassLoader classLoader) {
        List descriptorFiles = new ArrayList();

        try {
            List resources = ClassUtil.getResources(descriptorPath, classLoader);

            if(resources.isEmpty()) {
                throw new IllegalStateException("Failed to locate any model descriptor file by the name '" + descriptorPath + "' on the classpath.");
            }

            for(URL resource : resources) {
                InputStream resStream = resource.openStream();
                descriptorFiles.add(loadDescriptor(resStream));
            }
        } catch (IOException e) {
            throw new IllegalStateException("Unexpected IO Exception when reading Dynamic Namespace Descriptor files from classpath.", e);
        }

        return descriptorFiles;
    }

    public static Properties loadDescriptor(InputStream descriptorStream) throws IOException {
        AssertArgument.isNotNull(descriptorStream, "descriptorStream");
        try {
            Properties descriptor = new Properties();
            descriptor.load(descriptorStream);
            return descriptor;
        } finally {
            descriptorStream.close();
        }
    }

    private void intialize(List descriptors, EntityResolver schemaResolver, EntityResolver bindingResolver) throws SAXException, IOException {

        if(schemaResolver instanceof AbstractResolver) {
            if(((AbstractResolver)schemaResolver).getClassLoader() != classloader) {
                throw new SmooksException("Schema EntityResolver '" + schemaResolver.getClass().getName() + "' not using the same ClassLoader as this Descriptor instance.");
            }
        }
        if(bindingResolver instanceof AbstractResolver) {
            if(((AbstractResolver)bindingResolver).getClassLoader() != classloader) {
                throw new SmooksException("Binding EntityResolver '" + bindingResolver.getClass().getName() + "' not using the same ClassLoader as this Descriptor instance.");
            }
        }

        if(schemaResolver != null) {
            this.schema = newSchemaInstance(descriptors, schemaResolver);
        }
		this.smooks = newSmooksInstance(descriptors, bindingResolver);
	}

    private Schema newSchemaInstance(List descriptors, EntityResolver schemaResolver) throws SAXException, IOException {
        List schemas = getSchemas(descriptors, schemaResolver);

        try {
            // Create the merged Schema instance and from that, create the Validator instance...
            SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            return schemaFactory.newSchema(schemas.toArray(new Source[schemas.size()]));
        } finally {
            for(Source schemaSource : schemas) {
                if(schemaSource instanceof StreamSource) {
                    StreamSource streamSource = (StreamSource)schemaSource;
                    if(streamSource.getInputStream() != null) {
                        streamSource.getInputStream().close();
                    } else if(streamSource.getReader() != null) {
                        streamSource.getReader().close();
                    }
                }
            }
        }
    }

    private List getSchemas(List descriptors, EntityResolver schemaResolver) throws SAXException, IOException {
        Set namespaces = resolveNamespaces(descriptors);
        List xsdSources = new ArrayList();

        for (Namespace namespace : namespaces) {
            InputSource schemaSource = schemaResolver.resolveEntity(namespace.uri, namespace.uri);

            if(schemaSource != null) {
                if(schemaSource.getByteStream() != null) {
                    xsdSources.add(new StreamSource(schemaSource.getByteStream()));
                } else if(schemaSource.getCharacterStream() != null) {
                    xsdSources.add(new StreamSource(schemaSource.getCharacterStream()));
                } else {
                    throw new SAXException("Schema resolver '" + schemaResolver.getClass().getName() + "' failed to resolve schema for namespace '" + namespace + "'.  Resolver must return a Reader or InputStream in the InputSource.");
                }
            }
        }

        return xsdSources;
    }

    private Smooks newSmooksInstance(List descriptors, EntityResolver bindingResolver) throws SAXException, IOException, SmooksConfigurationException {
        AssertArgument.isNotNullAndNotEmpty(descriptors, "descriptors");
        AssertArgument.isNotNull(bindingResolver, "bindingResolver");

        Set namespaces = resolveNamespaces(descriptors);
        Map extendedConfigDigesters = new HashMap();

        // Now create a Smooks instance for processing configurations for these namespaces...
        Smooks smooks = new Smooks();

        smooks.setClassLoader(classloader);
        
        for (Namespace namespace : namespaces) {
            InputSource bindingSource = bindingResolver.resolveEntity(namespace.uri, namespace.uri);

            if(bindingSource != null) {
                if(bindingSource.getByteStream() != null) {
                    SmooksResourceConfigurationList configList;

                    try {
                        configList = XMLConfigDigester.digestConfig(bindingSource.getByteStream(), "./", extendedConfigDigesters, classloader);
                        for(int i = 0; i < configList.size(); i++) {
                            SmooksResourceConfiguration config = configList.get(i);
                            
                            if(config.getSelectorNamespaceURI() == null) {
                                SelectorStep selectorStep = config.getSelectorStep();

                                // And if there isn't a namespace prefix specified on the element (unresolved at this point),
                                // then assign the binding config namespace...
                                if(selectorStep.getTargetElement().getPrefix().equals(XMLConstants.DEFAULT_NS_PREFIX)) {
                                    config.setSelectorNamespaceURI(namespace.uri);
                                }
                            }
                        }
                    } catch (URISyntaxException e) {
                        throw new SmooksConfigurationException("Unexpected configuration digest exception.", e);
                    }

                    smooks.getApplicationContext().getStore().addSmooksResourceConfigurationList(configList);
                } else {
                    throw new SAXException("Binding configuration resolver '" + bindingResolver.getClass().getName() + "' failed to resolve binding configuration for namespace '" + namespace + "'.  Resolver must return an InputStream in the InputSource.");
                }
            }
        }

        return smooks;
    }

    private static Set resolveNamespaces(List descriptors) {
        List namespaces = new ArrayList();

        for(Properties descriptor : descriptors) {
            extractNamespaceDecls(descriptor, namespaces);
        }

        Comparator namspaceSorter = new Comparator() {
            public int compare(Namespace o1, Namespace o2) {
                return o1.order - o2.order;
            }
        };

        Namespace[] namespaceArray = new Namespace[namespaces.size()];
        namespaces.toArray(namespaceArray);
        Arrays.sort(namespaceArray, namspaceSorter);

        Set orderedNamespaceSet = new LinkedHashSet();
        orderedNamespaceSet.addAll(Arrays.asList(namespaceArray));

        return orderedNamespaceSet;
    }

    private static List extractNamespaceDecls(Properties descriptor, List namespaces) {
        Set> properties = descriptor.entrySet();
        for(Map.Entry property: properties) {
            String key = ((String) property.getKey()).trim();
            if(key.endsWith(DESCRIPTOR_NAMESPACE_POSTFIX)) {
                Namespace namespace = new Namespace();
                String namespaceUri = (String) property.getValue();
                String namespaceId = getNamespaceId(namespaceUri, descriptor);

                if(namespaceId == null) {
                    throw new SmooksConfigurationException("Unable to resolve namespace ID for namespace URI '" + namespaceUri + "'.");
                }

                String namespaceOrder = descriptor.getProperty(namespaceId + DESCRIPTOR_ORDER_POSTFIX, Integer.toString(Integer.MAX_VALUE)).trim();

                namespace.uri = namespaceUri;
                try {
                    namespace.order = Integer.parseInt(namespaceOrder);
                } catch(NumberFormatException e) {
                    throw new SmooksConfigurationException("Invalid value for descriptor config value '" + namespaceId + DESCRIPTOR_ORDER_POSTFIX + "'.  Must be a valid Integer value.");
                }

                namespaces.add(namespace);
            }
        }

        return namespaces;
    }

    public static String getNamespaceId(String namespaceURI, List descriptors) {
        for(Properties descriptor : descriptors) {
            String id = getNamespaceId(namespaceURI, descriptor);
            if(id != null) {
                return id;
            }
        }
        return null;
    }

    private static String getNamespaceId(String namespaceURI, Properties descriptor) {
        Set> properties = descriptor.entrySet();
        for(Map.Entry property: properties) {
            String key = ((String) property.getKey()).trim();
            String value = ((String) property.getValue()).trim();
            if(key.endsWith(DESCRIPTOR_NAMESPACE_POSTFIX) && value.equals(namespaceURI)) {
                return key.substring(0, (key.length() - DESCRIPTOR_NAMESPACE_POSTFIX.length()));
            }
        }
        return null;
    }

    public static String getSchemaLocation(String namespaceId, List descriptors) {
        return getDescriptorValue(namespaceId + DESCRIPTOR_SCHEMA_LOCATION_POSTFIX, descriptors);
    }

    public static String getBindingConfigLocation(String namespaceId, List descriptors) {
        return getDescriptorValue(namespaceId + DESCRIPTOR_BINDING_CONFIG_LOCATION_POSTFIX, descriptors);
    }

    private static String getDescriptorValue(String name, List descriptors) {
        for(Properties descriptor : descriptors) {
            String value = descriptor.getProperty(name);
            if(value != null) {
                return value;
            }
        }

        return null;
    }

    private static class Namespace {
        private String uri;
        private int order;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy