org.milyn.javabean.dynamic.ModelBuilder Maven / Gradle / Ivy
/*
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 java.io.*;
import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.milyn.FilterSettings;
import org.milyn.Smooks;
import org.milyn.SmooksException;
import org.milyn.assertion.AssertArgument;
import org.milyn.cdr.ParameterAccessor;
import org.milyn.container.ExecutionContext;
import org.milyn.delivery.Fragment;
import org.milyn.event.report.HtmlReportGenerator;
import org.milyn.javabean.BeanInstancePopulator;
import org.milyn.javabean.dynamic.serialize.BeanWriter;
import org.milyn.javabean.dynamic.visitor.NamespaceReaper;
import org.milyn.javabean.dynamic.visitor.UnknownElementDataReaper;
import org.milyn.javabean.lifecycle.BeanContextLifecycleEvent;
import org.milyn.javabean.lifecycle.BeanContextLifecycleObserver;
import org.milyn.javabean.lifecycle.BeanLifecycle;
import org.milyn.payload.JavaResult;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Dynamic Model Builder.
*
* Useful for constructing configuration model etc. Allows you to build a config model
* for a dynamic configuration namespace i.e. a config namespace that is evolving and being
* extended all the time. New namespaces can be easily added or extended. All that's required
* is to define the new config XSD and the Smooks Java Binding config to bind the data in the
* config namespace into the target Java model.
*
* The namespaces all need to be configured in a "descriptor" .properties file located on the classpath.
* Here's an example:
*
* mycomp.namespace=http://www.acme.com/xsd/mycomp.xsd
* mycomp.schemaLocation=/META-INF/xsd/mycomp.xsd
* mycomp.bindingConfigLocation=/META-INF/xsd/mycomp-binding.xml
*
*
* You should use a unique descriptor path for a given configuration model. Of course there can be many instances
* of this file on the classpath i.e. one per module/jar. This allows you to easily add extensions and updates
* to your configuration model, without having to define new Java model for the new namespaces (versions) etc.
*
* @author [email protected]
*/
public class ModelBuilder {
private static Log logger = LogFactory.getLog(ModelBuilder.class);
private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
static {
documentBuilderFactory.setNamespaceAware(true);
}
private Descriptor descriptor;
private boolean validate = true;
private String reportPath;
public ModelBuilder(Descriptor descriptor, boolean validate) throws SAXException, IOException {
AssertArgument.isNotNull(descriptor, "descriptor");
this.descriptor = descriptor;
this.validate = validate;
configure();
}
public ModelBuilder(String descriptorPath, boolean validate) throws SAXException, IOException {
AssertArgument.isNotNullAndNotEmpty(descriptorPath, "descriptorPath");
descriptor = new Descriptor(descriptorPath);
this.validate = validate;
configure();
}
public boolean isValidating() {
return validate;
}
protected Descriptor getDescriptor() {
return descriptor;
}
public void setReportPath(String reportPath) {
this.reportPath = reportPath;
}
public T readObject(InputStream message, Class returnType) throws SAXException, IOException {
return readObject(new InputStreamReader(message), returnType);
}
public T readObject(Reader message, Class returnType) throws SAXException, IOException {
Model model = readModel(message, JavaResult.class);
return model.getModelRoot().getBean(returnType);
}
public Model readModel(InputStream message, Class modelRoot) throws SAXException, IOException {
return readModel(new InputStreamReader(message), modelRoot);
}
public Model readModel(Reader message, Class modelRoot) throws SAXException, IOException {
AssertArgument.isNotNull(message, "message");
AssertArgument.isNotNull(modelRoot, "modelRoot");
JavaResult result = new JavaResult();
ExecutionContext executionContext = descriptor.getSmooks().createExecutionContext();
Map, Map> beanWriters = descriptor.getBeanWriters();
BeanTracker beanTracker = new BeanTracker(beanWriters);
if(reportPath != null) {
executionContext.setEventListener(new HtmlReportGenerator(reportPath));
}
executionContext.getBeanContext().addObserver(beanTracker);
if(validate && descriptor.getSchema() != null) {
// Validate the message against the schemas...
Document messageDoc = toDocument(message);
// Validate the document and then filter it through smooks...
descriptor.getSchema().newValidator().validate(new DOMSource(messageDoc));
descriptor.getSmooks().filterSource(executionContext, new DOMSource(messageDoc), result);
} else {
descriptor.getSmooks().filterSource(executionContext, new StreamSource(message), result);
}
Model model;
if(modelRoot == JavaResult.class) {
model = new Model(modelRoot.cast(result), beanTracker.beans, beanWriters, NamespaceReaper.getNamespacePrefixMappings(executionContext));
} else {
model = new Model(modelRoot.cast(result.getBean(modelRoot)), beanTracker.beans, beanWriters, NamespaceReaper.getNamespacePrefixMappings(executionContext));
}
return model;
}
private Document toDocument(Reader message) {
DocumentBuilder docBuilder;
try {
docBuilder = documentBuilderFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new SmooksException("Unable to parse message and dynamically bind into object model. DOM Parser confguration exception.", e);
}
try {
return docBuilder.parse(new InputSource(message));
} catch (SAXException e) {
throw new SmooksException("Unable to parse message and dynamically bind into object model. Message format exception.", e);
} catch (IOException e) {
throw new SmooksException("Unable to parse message and dynamically bind into object model. IO exception.", e);
} finally {
try {
message.close();
} catch (IOException e) {
logger.debug("Exception closing message reader.", e);
}
}
}
private void configure() {
Smooks smooks = descriptor.getSmooks();
smooks.addVisitor(new NamespaceReaper());
//descriptor.getSmooks().addVisitor(new UnknownElementDataReaper(), "*");
smooks.setFilterSettings(FilterSettings.newDOMSettings());
ParameterAccessor.setParameter(BeanInstancePopulator.NOTIFY_POPULATE, "true", smooks);
// Create the execution context so as to force resolution of the config...
smooks.createExecutionContext();
}
private class BeanTracker implements BeanContextLifecycleObserver {
private List beans = new ArrayList();
private Map, Map> beanWriterMap;
public BeanTracker(Map, Map> beanWriterMap) {
this.beanWriterMap = beanWriterMap;
}
public void onBeanLifecycleEvent(BeanContextLifecycleEvent event) {
if(event.getLifecycle() == BeanLifecycle.ADD || event.getLifecycle() == BeanLifecycle.CHANGE) {
Object bean = event.getBean();
BeanMetadata beanMetadata = new BeanMetadata(bean);
Map beanWriters = beanWriterMap.get(bean.getClass());
Fragment source = event.getSource();
if(source != null) {
String namespaceURI = source.getNamespaceURI();
beanMetadata.setNamespace(namespaceURI);
beanMetadata.setNamespacePrefix(source.getPrefix());
beanMetadata.setCreateSource(source);
beans.add(beanMetadata);
if(source.isDOMElement()) {
beanMetadata.setPreText(UnknownElementDataReaper.getPreText(source.getDOMElement(), beans, event));
} else {
// SAX pretext is gathered by an instance of the UnknownElementDataReaper
}
if(beanWriters != null) {
BeanWriter beanWriter = beanWriters.get(namespaceURI);
if(beanWriter != null) {
beanMetadata.setWriter(beanWriter);
} else if(logger.isDebugEnabled()) {
logger.debug("BeanWriters are configured for Object type '" + bean.getClass() + "', but not for namespace '" + namespaceURI + "'.");
}
} else if(logger.isDebugEnabled()) {
logger.debug("No BeanWriters configured for Object type '" + bean.getClass() + "'.");
}
}
} else if(event.getLifecycle() == BeanLifecycle.POPULATE) {
BeanMetadata beanMetdata = findMetadata(event.getBean());
beanMetdata.getPopulateSources().add(event.getSource());
}
}
private BeanMetadata findMetadata(Object bean) {
for(BeanMetadata metaData : beans) {
if(metaData.getBean() == bean) {
return metaData;
}
}
BeanRegistrationException.throwUnregisteredBeanInstanceException(bean);
return null; // Satisfy compiler
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy