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

org.smooks.cartridges.javabean.dynamic.ModelBuilder Maven / Gradle / Ivy

The newest version!
/*-
 * ========================LICENSE_START=================================
 * smooks-javabean-cartridge
 * %%
 * Copyright (C) 2020 Smooks
 * %%
 * Licensed under the terms of the Apache License Version 2.0, or
 * the GNU Lesser General Public License version 3.0 or later.
 *
 * SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-or-later
 *
 * ======================================================================
 *
 * 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.
 *
 * ======================================================================
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program 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.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * =========================LICENSE_END==================================
 */
package org.smooks.cartridges.javabean.dynamic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smooks.FilterSettings;
import org.smooks.Smooks;
import org.smooks.api.ExecutionContext;
import org.smooks.api.SmooksException;
import org.smooks.api.bean.lifecycle.BeanContextLifecycleEvent;
import org.smooks.api.bean.lifecycle.BeanContextLifecycleObserver;
import org.smooks.api.bean.lifecycle.BeanLifecycle;
import org.smooks.api.delivery.fragment.Fragment;
import org.smooks.assertion.AssertArgument;
import org.smooks.cartridges.javabean.BeanInstancePopulator;
import org.smooks.cartridges.javabean.dynamic.serialize.BeanWriter;
import org.smooks.cartridges.javabean.dynamic.visitor.NamespaceReaper;
import org.smooks.cartridges.javabean.dynamic.visitor.UnknownElementDataReaper;
import org.smooks.engine.delivery.fragment.NodeFragment;
import org.smooks.engine.report.HtmlReportGenerator;
import org.smooks.engine.resource.config.ParameterAccessor;
import org.smooks.io.sink.JavaSink;
import org.smooks.io.source.DOMSource;
import org.smooks.io.source.ReaderSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 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 final Logger LOGGER = LoggerFactory.getLogger(ModelBuilder.class); private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); static { DOCUMENT_BUILDER_FACTORY.setNamespaceAware(true); } private final Descriptor descriptor; private boolean validate; 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, JavaSink.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"); JavaSink sink = new JavaSink(); ExecutionContext executionContext = descriptor.getSmooks().createExecutionContext(); Map, Map> beanWriters = descriptor.getBeanWriters(); BeanTracker beanTracker = new BeanTracker(beanWriters); if (reportPath != null) { executionContext.getContentDeliveryRuntime().getExecutionEventListeners().add(new HtmlReportGenerator(reportPath, descriptor.getSmooks().getApplicationContext())); } 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 javax.xml.transform.dom.DOMSource(messageDoc)); descriptor.getSmooks().filterSource(executionContext, new DOMSource(messageDoc), sink); } else { descriptor.getSmooks().filterSource(executionContext, new ReaderSource<>(message), sink); } Model model; if (modelRoot == JavaSink.class) { model = new Model<>(modelRoot.cast(sink), beanTracker.beans, beanWriters, NamespaceReaper.getNamespacePrefixMappings(executionContext)); } else { model = new Model<>(modelRoot.cast(sink.getBean(modelRoot)), beanTracker.beans, beanWriters, NamespaceReaper.getNamespacePrefixMappings(executionContext)); } return model; } private Document toDocument(Reader message) { DocumentBuilder docBuilder; try { docBuilder = DOCUMENT_BUILDER_FACTORY.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.newSaxNgSettings().setMaxNodeDepth(Integer.MAX_VALUE)); ParameterAccessor.setParameter(BeanInstancePopulator.NOTIFY_POPULATE, "true", smooks); // Create the execution context so as to force resolution of the config... smooks.createExecutionContext(); } private static class BeanTracker implements BeanContextLifecycleObserver { private final List beans = new ArrayList<>(); private final 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 = null; if (source instanceof NodeFragment) { Node node = (Node) source.unwrap(); namespaceURI = node.getNamespaceURI(); beanMetadata.setNamespace(namespaceURI); beanMetadata.setNamespacePrefix(node.getPrefix()); beanMetadata.setCreateSource(source); beans.add(beanMetadata); beanMetadata.setPreText(UnknownElementDataReaper.getPreText((Element) node, beans)); } 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