/** * Copyright 2010 JBoss Inc * * 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. */ package org.drools.runtime.help; import com.thoughtworks.xstream.XStream; /** * <p> * Provides a configured XStream instance to support the marshalling of BatchExecutions, where the resulting * xml can be used as a message format. Configured converters only exist for the commands supported via the * CommandFactory. The user may add other converters for their user objects. * </p> * * <p> * This is very useful for scripting stateless of stateful knowledge sessions, especially when services are involved. * </p> * * <p> * There is current no xsd for schema validation, however we will try to outline the basic format here and the drools-transformer-xstream module * has an illustrative unit test in the XStreamBatchExecutionTest unit test. The root element is <batch-execution> and it can contain zero or more * commands elements. * </p> * <pre> * <batch-execution> * ... * </batch-execution> * </pre> * * <p> * This contains a list of elements that represent commands, the supported commands is limited to those Commands provided by the CommandFactory. The * most basic of these is the <insert> element, which inserts objects. The contents of the insert element is the user object, as dictated by XStream. * </p> * <pre> * <batch-execution> * <insert> * .... * </insert> * </batch-execution> * </pre> * <p> * The insert element supports an 'out-identifier' attribute, this means the inserted object's FactHandle will be returned * and optionally the object itself as part of the <batch-execution-results> payload. To return the object use the attribute 'return-object' which takes a boolean * 'true'|'false' value, by default this is true. * The * </p> * <pre> * <batch-execution > * <insert out-identifier='userVar' > * .... * </insert> * </batch-execution> * </pre> * * <p> * It's also possible to insert a collection of objects using the <insert-elements> element, here each element is inserted in turn. This command also support's an * 'out-identifier' attribute which returns the FactHandle's in a Collection, of the same order that the objects where inserted. 'return-object' is also supported to optionally * return the inserted objects, again they will be in a collection of the same order they where inserted. * The org.domain.UserClass is just an illustrative user object that xstream would serialise. * </p> * <pre> * <batch-execution> * <insert-elements> * <org.domain.UserClass> * ... * </org.domain.UserClass> * <org.domain.UserClass> * ... * </org.domain.UserClass> * <org.domain.UserClass> * ... * </org.domain.UserClass> * </insert-elements> * </batch-execution> * </pre> * * <p> * Next there is the <set-global> element, which sets a global for the session. * </p> * <pre> * <batch-execution> * <set-global identifier='userVar'> * <org.domain.UserClass> * ... * </org.domain.UserClass> * </set-global> * </batch-execution> * </pre> * <p> * <set-global> also supports two other optional attributes 'out' and 'out-identifier'. 'out' is a boolean and when set the global will be added to the * <batch-execution-results&g; payload using the name from the 'identifier' attribute. 'out-identifier' works like 'out' but additionally allows you to * override the identifier used in the <batch-execution-results&g; payload. * </p> * <pre> * <batch-execution> * <set-global identifier='userVar1' out='true'> * <org.domain.UserClass> * ... * </org.domain.UserClass> * </set-global> * <set-global identifier='userVar2' out-identifier='alternativeUserVar2'> * <org.domain.UserClass> * ... * </org.domain.UserClass> * </set-global> * </batch-execution> * </pre> * <p> * There is also a <get-global> element, which has no contents but does support an 'out-identifier' attribute, there is no need for an 'out' attribute * as we assume that a <get-global> is always an 'out'. * </p> * <pre> * <batch-execution> * <get-global identifier='userVar1' /> * <get-global identifier='userVar2' out-identifier='alternativeUserVar2'/> * </batch-execution> * </pre> * * <p>Specific objects can be retrieved using the FactHandle:</p> * <pre> * <batch-execution> * <get-object out-identifier='outStilton' factHandle='" + factHandle.toExternalForm() + "' /> * </batch-execution> * </pre> * * <p> * While the 'out' attribute is useful in returning specific instances as a result payload, we often wish to run actual querries. Both parameter * and parameterless querries are supported. The 'name' attribute is the name of the query to be called, and the 'out-identifier' is the identifier * to be used for the query results in the <batch-execution-results> payload. * </p> * <pre> * <batch-execution> * <query out-identifier='cheeses' name='cheeses'/> * <query out-identifier='cheeses2' name='cheesesWithParams'> * <string>stilton</string> * <string>cheddar</string> * </query>; * </batch-execution> * </pre> * * <p> * The <start-process> command is also supported and accepts optional parameters: * </p> * <pre> * <batch-execution> * <startProcess processId='org.drools.actions'> * <parameter identifier='person'> * <org.drools.TestVariable> * <name>John Doe</name> * </org.drools.TestVariable> * </parameter> * </startProcess> * </batch-execution> * </pre> * * <p>SignelEvent</p> * <pre> * <signal-event process-instance-id='1' event-type='MyEvent'> * <string>MyValue</string> * </signal-event> * </pre> * * <p>CompleteWorkItem</p> * <pre> * <complete-work-item id='" + workItem.getId() + "' > * <result identifier='Result'> * <string>SomeOtherString</string> * </result> * </complete-work-item> * </pre> * * <p>AbortWorkItem</p> * <pre> * <abort-work-item id='21' /> * </pre> * * <p> * Support for more commands will be added over time. * </p> * * <p> * The following is a simple insert batch-execution command: * </p> * <pre> * <batch-execution> * <insert out-identifier='outStilton'> * <org.drools.Cheese> * <type>stilton</type> * <price>25</price> * <oldPrice>0</oldPrice> * </org.drools.Cheese> * </insert> * </batch-execution> * </pre> * * <p> * The pipeline can be used to handle this end to end, notice the part where the configured XStream instance is passed "BatchExecutionHelper.newXStreamMarshaller()". * This will take a given xml, transform it and then execute it as a BatchExecution Command. Notice the Pipeline also handles the marshalling * of the ExecutionResults back out to XML. * </p> * <pre> * Action executeResultHandler = PipelineFactory.newExecuteResultHandler(); * * Action assignResult = PipelineFactory.newAssignObjectAsResult(); * assignResult.setReceiver( executeResultHandler ); * * Transformer outTransformer = PipelineFactory.newXStreamToXmlTransformer( BatchExecutionHelper.newXStreamMarshaller() ); * outTransformer.setReceiver( assignResult ); * * KnowledgeRuntimeCommand batchExecution = PipelineFactory.newBatchExecutor(); * batchExecution.setReceiver( outTransformer ); * * * Transformer inTransformer = PipelineFactory.newXStreamFromXmlTransformer( BatchExecutionHelper.newXStreamMarshaller() ); * inTransformer.setReceiver( batchExecution ); * * Pipeline pipeline = PipelineFactory.newStatelessKnowledgeSessionPipeline( ksession ); * pipeline.setReceiver( inTransformer ); * </pre> * * <p> * The results would look like following xml: * </p> * <pre> * <execution-results> * <result identifier='outStilton'> * <org.drools.Cheese> * <type>stilton</type> * <oldPrice>0</oldPrice> * <price>30</price> * </org.drools.Cheese> * </result> * </execution-results> * </pre> * * <p>This api is experimental and thus the classes and the interfaces returned are subject to change.</p> * */ public class BatchExecutionHelper { private static volatile BatchExecutionHelperProvider provider; public static XStream newXStreamMarshaller() { return getBatchExecutionHelperProvider().newXStreamMarshaller(); } public static XStream newJSonMarshaller() { return getBatchExecutionHelperProvider().newJSonMarshaller(); } private static synchronized void setBatchExecutionHelperProvider(BatchExecutionHelperProvider provider) { BatchExecutionHelper.provider = provider; } private static synchronized BatchExecutionHelperProvider getBatchExecutionHelperProvider() { if ( provider == null ) { loadProvider(); } return provider; } private static void loadProvider() { try { Class<BatchExecutionHelperProvider> cls = (Class<BatchExecutionHelperProvider>) Class.forName( "org.drools.runtime.help.impl.BatchExecutionHelperProviderImpl" ); setBatchExecutionHelperProvider( cls.newInstance() ); } catch ( Exception e2 ) { throw new RuntimeException( "Provider org.drools.runtime.help.impl.BatchExecutionHelperProviderImpl could not be set.", e2 ); } } }