org.drools.runtime.pipeline.PipelineFactory Maven / Gradle / Ivy
package org.drools.runtime.pipeline;
import java.util.Properties;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import net.sf.jxls.reader.ReaderBuilder;
import net.sf.jxls.reader.XLSReader;
import org.drools.ProviderInitializationException;
import org.drools.builder.help.KnowledgeBuilderHelper;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.StatelessKnowledgeSession;
import org.milyn.Smooks;
import com.thoughtworks.xstream.XStream;
/**
*
* The PipelineFactory and associated classes are there to help with the automation of getting information
* into and out of Drools, especially when using services, such as JMS, and non pojo data sources. Transformers for
* Smooks, JAXB, Xstream and Jxls are povided. Smooks is an ETL tooling and can work with a variety of data sources,
* JAXB is a Java standard aimed at working with XSDs, while XStream is a simple and fast xml serialisation framework and finally
* Jxls allows for loading of pojos from an excel decision table. minimal information on these technologies will be provided here
* and it is expected for the user to consult the relevant user guide for each.
*
*
*
* Pipeline is not meant as a replacement for products like the more powerful Camel, but is aimed as a complimentary
* framework that ultimately can be integrated into more powerful pipeline frameworks. Instead it is a simple framework aimed at
* the specific Drools use cases.
*
*
*
* In Drools a pipeline is a series of stages that operate on and propagate a given payload. Typically this starts with a Pipeline instance which is
* responsible for taking the payload, creating a PipelineContext for it and propagating that to the first Receiver stage. Two types of Pipelines
* are provided, both requiring a different PipelineContexts. StatefulKnowledgeSessionPipeline and StatelessKnowledgeSessionPipeline. Notice that both
* factory methods take the relevant session as an argument.
*
*
* Pipeline pipeline = PipelineFactory.newStatefulKnowledgeSessionPipeline( ksession );
* pipeline.setReceiver( receiver );
*
*
*
* A pipeline is then made up of a chain of Stages that can implement both the Emitter and the Receiver interfaces. The Emitter interface means the stage
* can propagate a payload and the Receiver interface means it can receive a payload. This is why the Pipeline interface only implements Emitter and Stage
* and not Receiver, as it is the first instance in the chain. The Stage interface allows a custom exception handler to be set on the stage.
*
*
* Transformer transformer = PipelineFactory.newXStreamFromXmlTransformer( xstream );
* transformer.setStageExceptionHandler( new StageExceptionHandler() { .... } );
*
*
*
* The Transformer interface above extends both Stage, Emitter and Receiver, other than providing those interface methods as a single type, it's other role
* is that of a marker interface that indicates the role of the instance that implements it. We have several other marker interfaces such as Expression and
* Action, both of which also extend Stage, Emitter and Receiver. One of the stages should be responsible for setting a result value on the PipelineContext. It is the role of the ResultHandler interface, that
* the user implements that is responsible for executing on these results or simply setting them an object that the user can retrieve them from.
*
*
* ResultHandler resultHandler = new ResultHandlerImpl();
* pipeline.insert( factHandle, resultHandler );
* System.out.println( resultHandler );
* ...
* public class ResultHandlerImpl implements ResultHandler {
* Object result;
*
* public void handleResult(Object result) {
* this.result = result;
* }
*
* public Object getResult() {
* return this.result;
* }
* }
*
*
*
* while the above example shows a simple handler that simply assigns the result to a field that the user can access, it could do more complex work
* like sending the object as a message.
*
*
*
* Pipeline is provides an adapter to insert the payload and internal create the correct PipelineContext. Two types of Pipelines
* are provided, both requiring a different PipelineContext. StatefulKnowledgeSessionPipeline and StatelessKnowledgeSessionPipeline.
* Pipeline itself implements both Stage and Emitter, this means it's a Stage in a pipeline and emits the payload to a receiver. It does
* not implement Receiver itself, as it the start adapter for the pipeline. PipelineFactory provides methods to create both of the two
* Pipeline. StatefulKnowledgeSessionPipeline is constructed as below, with the receiver set
*
*
*
* In general it easier to construct the pipelines in reverse, for example the following one handles loading xml data from disk,
* transforming it with xstream and then inserting the object:
*
* // Make the results, in this case the FactHandles, available to the user
* Action executeResultHandler = PipelineFactory.newExecuteResultHandler();
*
* // Insert the transformed object into the session associated with the PipelineContext
* KnowledgeRuntimeCommand insertStage = PipelineFactory.newStatefulKnowledgeSessionInsert();
* insertStage.setReceiver( executeResultHandler );
*
* // Create the transformer instance and create the Transformer stage, where we are going from Xml to Pojo.
* XStream xstream = new XStream();
* Transformer transformer = PipelineFactory.newXStreamFromXmlTransformer( xstream );
* transformer.setReceiver( insertStage );
*
* // Create the start adapter Pipeline for StatefulKnowledgeSessions
* Pipeline pipeline = PipelineFactory.newStatefulKnowledgeSessionPipeline( ksession );
* pipeline.setReceiver( transformer );
*
* // Instantiate a simple result handler and load and insert the XML
* ResultHandlerImpl resultHandler = new ResultHandlerImpl();
* pipeline.insert( ResourceFactory.newClassPathResource( "path/facts.xml", getClass() ),
* resultHandler );
*
*
* * See StatefullKnowledgeSessionPipeline, StatelessKnowledgeSessionPipeline for more specific information and capabilities on these pipelines. *
* ** While the above example is for loading a resource from disk it is also possible to work from a running messaging service. Drools currently * provides a single Service for JMS, called JmsMessenger. Other Services will be added later. Look at the factory method for JmsMessenger for more details, but below shows * part of a unit test: *
* ** // as this is a service, it's more likely the results will be logged or sent as a return message * Action resultHandlerStage = PipelineFactory.newExecuteResultHandler(); * * // Insert the transformed object into the session associated with the PipelineContext * KnowledgeRuntimeCommand insertStage = PipelineFactory.newStatefulKnowledgeSessionInsert(); * insertStage.setReceiver( resultHandlerStage ); * * // Create the transformer instance and create the Transformer stage, where we are going from Xml to Pojo. Jaxb needs an array of the available classes * JAXBContext jaxbCtx = KnowledgeBuilderHelper.newJAXBContext( classNames, * kbase ); * Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller(); * Transformer transformer = PipelineFactory.newJaxbFromXmlTransformer( unmarshaller ); * transformer.setReceiver( insertStage ); * * // payloads for JMS arrive in a Message wrapper, we need to unwrap this object * Action unwrapObjectStage = PipelineFactory.newJmsUnwrapMessageObject(); * unwrapObjectStage.setReceiver( transformer ); * * // Create the start adapter Pipeline for StatefulKnowledgeSessions * Pipeline pipeline = PipelineFactory.newStatefulKnowledgeSessionPipeline( ksession ); * pipeline.setReceiver( unwrapObjectStage ); * * // Services, like JmsMessenger take a ResultHandlerFactory implementation, this is because a result handler must be created for each incoming message. * ResultHandleFactoryImpl factory = new ResultHandleFactoryImpl(); * Service messenger = PipelineFactory.newJmsMessenger( pipeline, * props, * destinationName, * factory ); * messenger.start(); **/ public class PipelineFactory { private static CorePipelineProvider corePipelineProvider; private static JaxbTransformerProvider jaxbPipelineProvider; private static SmooksTransformerProvider smooksPipelineProvider; private static XStreamTransformerProvider xstreamPipelineProvider; private static JxlsTransformerProvider jxlsPipelineProvider; private static JmsMessengerProvider jmsMessengerProvider; /** * Construct a new Pipeline to be used when interacting with a StatefulKnowledgeSession. * It assumes that the default entry point will be used for any insertions. * * @param ksession * @return */ public static Pipeline newStatefulKnowledgeSessionPipeline(StatefulKnowledgeSession ksession) { return getCorePipelineProvider().newStatefulKnowledgeSessionPipeline( ksession ); } /** * Construct a new Pipeline to be used when interacting with a StatefulKnowledgeSession. The entry point * to be used is specified as s String. * * @param ksession * @param entryPointName * @return */ public static Pipeline newStatefulKnowledgeSessionPipeline(StatefulKnowledgeSession ksession, String entryPointName) { return getCorePipelineProvider().newStatefulKnowledgeSessionPipeline( ksession, entryPointName ); } /** * Construct a new Pipeline to be used when interacting with StatelessKnowledgeSessions. * @param ksession * @return */ public static Pipeline newStatelessKnowledgeSessionPipeline(StatelessKnowledgeSession ksession) { return getCorePipelineProvider().newStatelessKnowledgeSessionPipelineImpl( ksession ); } /** * Insert the payload into the StatefulKnowledgeSesssion referenced in the context. This stage * expects the returned FactHandles to be stored in a HashMap of the PipelineContext result property. * @return */ public static KnowledgeRuntimeCommand newStatefulKnowledgeSessionInsert() { return getCorePipelineProvider().newStatefulKnowledgeSessionInsert(); } /** * Executes a StatelessKnowledgeSession. StatelessKnowledgeSessions sessions have four possible execution. Execute against an object * and execute against an Iterable and both with and without Paramters. See StatelessKnowledgeSession for more details on what those * mean. To control which execution method is used the StatelessKnowledgeSessionPipelineContext has three properties; object, iterable * and parameters. These can be assigned using an MVEL action stage, which has access to that context, the "this" object is assumed * to be the propagating payload object. If no properties are set it will call executeObject against the propagating payload. The same * is true if just the object property is assigned. If the Iterable property is assigned it all call executeIterable. Finally the WithParamaters * method will be called for each of those if the parameters property is set. * *
* Action executeResultHandler = PipelineFactory.newExecuteResultHandler(); * * KnowledgeRuntimeCommand execute = PipelineFactory.newStatelessKnowledgeSessionExecute(); * execute.setReceiver( executeResultHandler ); * * Action assignParameters = PipelineFactory.newMvelAction( "context.parameters.globalParams.setInOut( ['list' : new java.util.ArrayList()] )"); * assignParameters.setReceiver( execute ); * * Action assignIterable = PipelineFactory.newMvelAction( "context.setIterable( this )"); * assignIterable.setReceiver( assignParameters ); * * Pipeline pipeline = PipelineFactory.newStatelessKnowledgeSessionPipeline(ksession); * pipeline.setReceiver( assignIterable ); * * ResultHandlerImpl handler = new ResultHandlerImpl(); * * pipeline.insert( object, handler ); ** @return */ public static KnowledgeRuntimeCommand newStatelessKnowledgeSessionExecute() { return getCorePipelineProvider().newStatelessKnowledgeSessionExecute(); } /** * The payload here is expected to be a String and the global will be set on the PipelineContext result property. The propagating * object will also be switched to the results. * @return */ public static KnowledgeRuntimeCommand newStatefulKnowledgeSessionGetGlobal() { return getCorePipelineProvider().newStatefulKnowledgeSessionGetGlobal(); } /** * Expects the payload to be a Map
* // as this is a service, it's more likely the results will be logged or sent as a return message * Action resultHandlerStage = PipelineFactory.newExecuteResultHandler(); * * // Insert the transformed object into the session associated with the PipelineContext * KnowledgeRuntimeCommand insertStage = PipelineFactory.newStatefulKnowledgeSessionInsert(); * insertStage.setReceiver( resultHandlerStage ); * * // Create the transformer instance and create the Transformer stage, where we are going from Xml to Pojo. Jaxb needs an array of the available classes * JAXBContext jaxbCtx = KnowledgeBuilderHelper.newJAXBContext( classNames, * kbase ); * Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller(); * Transformer transformer = PipelineFactory.newJaxbFromXmlTransformer( unmarshaller ); * transformer.setReceiver( insertStage ); * * // payloads for JMS arrive in a Message wrapper, we need to unwrap this object * Action unwrapObjectStage = PipelineFactory.newJmsUnwrapMessageObject(); * unwrapObjectStage.setReceiver( transformer ); * * // Create the start adapter Pipeline for StatefulKnowledgeSessions * Pipeline pipeline = PipelineFactory.newStatefulKnowledgeSessionPipeline( ksession ); * pipeline.setReceiver( unwrapObjectStage ); * * // Services, like JmsMessenger take a ResultHandlerFactory implementation, this is because a result handler must be created for each incoming message. * ResultHandleFactoryImpl factory = new ResultHandleFactoryImpl(); * Service messenger = PipelineFactory.newJmsMessenger( pipeline, * props, * destinationName, * factory ); * messenger.start(); ** @param pipeline * @param properties * @param destinationName * @param resultHandlerFactory * @return */ public static Service newJmsMessenger(Pipeline pipeline, Properties properties, String destinationName, ResultHandlerFactory resultHandlerFactory) { return getJmsMessengerProvider().newJmsMessenger( pipeline, properties, destinationName, resultHandlerFactory ); } /** * Unwrap the payload from the JMS Message and propagate it as the payload object. * @return */ public static Action newJmsUnwrapMessageObject() { return getJmsMessengerProvider().newJmsUnwrapMessageObject(); } /** * Transforms from Source to Pojo using Smooks, the resulting pojo is set as the propagating object. * *
* // Instantiate Smooks with the config... * Smooks smooks = new Smooks( getClass().getResourceAsStream( "smooks-config.xml" ) ); * * Transformer transformer = PipelineFactory.newSmooksFromSourceTransformer( smooks, * "orderItem" ); * transformer.setReceiver( insertStage ); ** * @param smooks * @param rootId * @return */ public static Transformer newSmooksFromSourceTransformer(Smooks smooks, String rootId) { return getSmooksPipelineProvider().newSmooksFromSourceTransformer( smooks, rootId ); } /** * Transforms from Pojo to Source using Smooks, the resulting Source is set as the propagating object * *
* // Instantiate Smooks with the config... * Smooks smooks = new Smooks( getClass().getResourceAsStream( "smooks-config.xml" ) ); * * Transformer transformer = PipelineFactory.newSmooksToSourceTransformer( smooks ); * transformer.setReceiver( receiver ); ** * @param smooks * @param rootId * @return */ public static Transformer newSmooksToSourceTransformer(Smooks smooks) { return getSmooksPipelineProvider().newSmooksToSourceTransformer( smooks ); } /** * Transforms from XML to Pojo using JAXB, the resulting pojo is set as the propagating object. * *
* JAXBContext jaxbCtx = KnowledgeBuilderHelper.newJAXBContext( classNames, * kbase ); * Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller(); * Transformer transformer = PipelineFactory.newJaxbFromXmlTransformer( unmarshaller ); * transformer.setReceiver( receiver ); ** * Don't forget the XSD model must be generated, using XJC at runtime into the KnowledgeBase first, * Using KnowledgeBuilderHelper.addXsdModel: *
* Options xjcOpts = new Options(); * xjcOpts.setSchemaLanguage( Language.XMLSCHEMA ); * KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); * * String[] classNames = KnowledgeBuilderHelper.addXsdModel( ResourceFactory.newClassPathResource( "order.xsd", * getClass() ), * kbuilder, * xjcOpts, * "xsd" ); ** @param unmarshaller * @return */ public static Transformer newJaxbFromXmlTransformer(Unmarshaller unmarshaller) { return getJaxbPipelineProvider().newJaxbFromXmlTransformer( unmarshaller ); } /** * Transforms from Pojo to XML using JAXB, the resulting XML is set as the propagating object. *
* JAXBContext jaxbCtx = KnowledgeBuilderHelper.newJAXBContext( classNames, * kbase ); * Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller(); * Transformer transformer = PipelineFactory.newJaxbFromXmlTransformer( unmarshaller ); * transformer.setReceiver( receiver ); * * * @param marshaller * @return */ public static Transformer newJaxbToXmlTransformer(Marshaller marshaller) { return getJaxbPipelineProvider().newJaxbToXmlTransformer( marshaller ); } /** * Transforms from XML to Pojo using XStream, the resulting Pojo is set as the propagating object. * ** XStream xstream = new XStream(); * Transformer transformer = PipelineFactory.newXStreamFromXmlTransformer( xstream ); * transformer.setReceiver( receiver ); ** @param xstream * @return */ public static Transformer newXStreamFromXmlTransformer(XStream xstream) { return getXStreamTransformerProvider().newXStreamFromXmlTransformer( xstream ); } /** * Transforms from Pojo to XML using XStream, the resulting XML is set as the propagating object. * ** XStream xstream = new XStream(); * Transformer transformer = PipelineFactory.newXStreamToXmlTransformer( xstream ); * transformer.setReceiver( receiver ); ** @param xstream * @return */ public static Transformer newXStreamToXmlTransformer(XStream xstream) { return getXStreamTransformerProvider().newXStreamToXmlTransformer( xstream ); } /** * Transforms from an Excel spread to a Map of pojos pojos using jXLS, the resulting map is set as the propagating object. * You may need to use splitters and MVEL expressions to split up the transformation to insert individual pojos. * * Note you must provde an XLSReader, which references the mapping file and also an MVEL string which will instantiate the map. * The mvel expression is pre-compiled but executedon each usage of the transformation. * ** XLSReader mainReader = ReaderBuilder.buildFromXML( ResourceFactory.newClassPathResource( "departments.xml", getClass() ).getInputStream() ); * Transformer transformer = PipelineFactory.newJxlsTransformer(mainReader, "[ 'departments' : new java.util.ArrayList(), 'company' : new org.drools.runtime.pipeline.impl.Company() ]"); ** * @param xlsReader * @param text * @return */ public static Transformer newJxlsTransformer(XLSReader xlsReader, String text) { return getJxlsTransformerProvider().newJxlsTransformer( xlsReader, text ); } private static synchronized void setCorePipelineProvider(CorePipelineProvider provider) { PipelineFactory.corePipelineProvider = provider; } private static synchronized CorePipelineProvider getCorePipelineProvider() { if ( corePipelineProvider == null ) { loadCorePipelineProvider(); } return corePipelineProvider; } private static void loadCorePipelineProvider() { try { Classcls = (Class ) Class.forName( "org.drools.runtime.pipeline.impl.CorePipelineProviderImpl" ); setCorePipelineProvider( cls.newInstance() ); } catch ( Exception e2 ) { throw new ProviderInitializationException( "org.drools.runtime.pipeline.impl.CorePipelineProviderImpl could not be set.", e2 ); } } private static synchronized void setJaxbTransformerProvider(JaxbTransformerProvider provider) { PipelineFactory.jaxbPipelineProvider = provider; } private static synchronized JaxbTransformerProvider getJaxbPipelineProvider() { if ( jaxbPipelineProvider == null ) { loadJaxbTransformerProvider(); } return jaxbPipelineProvider; } private static void loadJaxbTransformerProvider() { try { Class cls = (Class ) Class.forName( "org.drools.runtime.pipeline.impl.JaxbTransformerProviderImpl" ); setJaxbTransformerProvider( cls.newInstance() ); } catch ( Exception e2 ) { throw new ProviderInitializationException( "Provider org.drools.runtime.pipeline.impl.JaxbTransformerProviderImpl could not be set.", e2 ); } } private static synchronized void setSmooksTransformerProvider(SmooksTransformerProvider provider) { PipelineFactory.smooksPipelineProvider = provider; } private static synchronized SmooksTransformerProvider getSmooksPipelineProvider() { if ( smooksPipelineProvider == null ) { loadSmooksTransformerProvider(); } return smooksPipelineProvider; } private static void loadSmooksTransformerProvider() { try { Class cls = (Class ) Class.forName( "org.drools.runtime.pipeline.impl.SmooksTransformerProviderImpl" ); setSmooksTransformerProvider( cls.newInstance() ); } catch ( Exception e2 ) { throw new ProviderInitializationException( "Provider org.drools.runtime.pipeline.impl.SmooksTransformerProviderImpl could not be set.", e2 ); } } private static synchronized void setXStreamTransformerProvider(XStreamTransformerProvider provider) { PipelineFactory.xstreamPipelineProvider = provider; } private static synchronized XStreamTransformerProvider getXStreamTransformerProvider() { if ( xstreamPipelineProvider == null ) { loadXStreamTransformerProvider(); } return xstreamPipelineProvider; } private static void loadXStreamTransformerProvider() { try { Class cls = (Class ) Class.forName( "org.drools.runtime.pipeline.impl.XStreamTransformerProviderImpl" ); setXStreamTransformerProvider( cls.newInstance() ); } catch ( Exception e2 ) { throw new ProviderInitializationException( "Provider org.drools.runtime.pipeline.impl.XStreamTransformerProviderImpl could not be set.", e2 ); } } private static synchronized void setJxlsTransformerProvider(JxlsTransformerProvider provider) { PipelineFactory.jxlsPipelineProvider = provider; } private static synchronized JxlsTransformerProvider getJxlsTransformerProvider() { if ( jxlsPipelineProvider == null ) { loadJxlsTransformerProvider(); } return jxlsPipelineProvider; } private static void loadJxlsTransformerProvider() { try { Class cls = (Class ) Class.forName( "org.drools.runtime.pipeline.impl.JxlsTransformer$JxlsTransformerProviderImpl" ); setJxlsTransformerProvider( cls.newInstance() ); } catch ( Exception e2 ) { throw new ProviderInitializationException( "Provider org.drools.runtime.pipeline.impl.JxlsTransformer$JxlsTransformerProviderImpl could not be set.", e2 ); } } private static synchronized void setJmsMessengerProvider(JmsMessengerProvider provider) { PipelineFactory.jmsMessengerProvider = provider; } private static synchronized JmsMessengerProvider getJmsMessengerProvider() { if ( jmsMessengerProvider == null ) { loadJmsMessengerProvider(); } return jmsMessengerProvider; } private static void loadJmsMessengerProvider() { try { Class cls = (Class ) Class.forName( "org.drools.runtime.pipeline.impl.JmsMessengerProviderImpl" ); setJmsMessengerProvider( cls.newInstance() ); } catch ( Exception e2 ) { throw new ProviderInitializationException( "Provider org.drools.runtime.pipeline.impl.JmsMessengerProviderImpl could not be set.", e2 ); } } }