/** * 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.camel.component; import java.io.ByteArrayInputStream; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.camel.Exchange; import org.apache.camel.ExchangePattern; import org.apache.camel.Message; import org.apache.camel.Processor; import org.apache.camel.component.cxf.CxfConstants; import org.apache.camel.component.cxf.CxfSpringEndpoint; import org.apache.camel.model.BeanDefinition; import org.apache.camel.model.DataFormatDefinition; import org.apache.camel.model.MarshalDefinition; import org.apache.camel.model.ProcessorDefinition; import org.apache.camel.model.RouteDefinition; import org.apache.camel.model.ToDefinition; import org.apache.camel.model.UnmarshalDefinition; import org.apache.camel.model.dataformat.JaxbDataFormat; import org.apache.camel.model.dataformat.XStreamDataFormat; import org.apache.camel.spi.Policy; import org.apache.camel.spi.RouteContext; import org.drools.command.runtime.BatchExecutionCommandImpl; import org.drools.command.runtime.GetGlobalCommand; import org.drools.command.runtime.SetGlobalCommand; import org.drools.command.runtime.process.AbortWorkItemCommand; import org.drools.command.runtime.process.CompleteWorkItemCommand; import org.drools.command.runtime.process.SignalEventCommand; import org.drools.command.runtime.process.StartProcessCommand; import org.drools.command.runtime.rule.FireAllRulesCommand; import org.drools.command.runtime.rule.GetObjectsCommand; import org.drools.command.runtime.rule.InsertElementsCommand; import org.drools.command.runtime.rule.InsertObjectCommand; import org.drools.command.runtime.rule.ModifyCommand; import org.drools.command.runtime.rule.ModifyCommand.SetterImpl; import org.drools.command.runtime.rule.QueryCommand; import org.drools.command.runtime.rule.RetractCommand; import org.drools.common.DefaultFactHandle; import org.drools.core.util.StringUtils; import org.drools.jax.soap.PostCxfSoapProcessor; import org.drools.jax.soap.PostCxfTransportSoapProcessor; import org.drools.jax.soap.PreCxfSoapProcessor; import org.drools.jax.soap.PreCxfTransportSoapProcessor; import org.drools.runtime.CommandExecutor; import org.drools.runtime.impl.ExecutionResultImpl; import org.drools.runtime.rule.impl.FlatQueryResults; import org.drools.xml.jaxb.util.JaxbListWrapper; public class DroolsPolicy implements Policy { private static boolean augmented; public void beforeWrap(RouteContext routeContext, ProcessorDefinition<?> processorDefinition) { augmentNodes( routeContext, processorDefinition, new HashSet<Object>() ); } public Processor wrap(RouteContext routeContext, Processor processor) { RouteDefinition routeDef = routeContext.getRoute(); ToDefinition toDrools = getDroolsNode( routeDef ); Processor returnedProcessor; if ( toDrools != null ) { returnedProcessor = new DroolsProcess( toDrools.getUri(), processor ); } else { returnedProcessor = processor;//new DroolsClientProcessor( processor ); } return returnedProcessor; } private ToDefinition getDroolsNode(RouteDefinition routeDef) { ToDefinition toDrools = null; for ( ProcessorDefinition<?> child : routeDef.getOutputs() ) { toDrools = getDroolsNode( child ); if ( toDrools != null ) { break; } } return toDrools; } public static void augmentNodes(RouteContext routeContext, ProcessorDefinition<?> nav, Set visited) { if ( !nav.getOutputs().isEmpty() ) { List<ProcessorDefinition> outputs = nav.getOutputs(); for ( int i = 0; i < outputs.size(); i++ ) { ProcessorDefinition child = outputs.get( i );//it.next(); if ( child instanceof ToDefinition ) { ToDefinition to = (ToDefinition) child; if ( to.getUri().startsWith( "cxfrs" ) && !visited.contains( to ) ) { BeanDefinition beanDef = new BeanDefinition(); beanDef.setBeanType( PreCxfrs.class ); outputs.add( i, beanDef ); // insert before cxfrs beanDef = new BeanDefinition(); beanDef.setBeanType( PostCxfrs.class ); outputs.add( i + 2, beanDef ); // insert after cxfrs i = i + 2;// adjust for the two inserts } else if ( to.getUri().startsWith( "cxf" ) && !visited.contains( to ) ) { BeanDefinition beanDef = new BeanDefinition(); beanDef.setBeanType( PreCxfSoapProcessor.class ); outputs.add( i, beanDef ); // insert before cxf beanDef = new BeanDefinition(); beanDef.setBeanType( PostCxfSoapProcessor.class ); outputs.add( i + 2, beanDef ); // insert after cxf i = i + 2;// adjust for the two inserts augmented = true; } } else if ( child instanceof MarshalDefinition ) { MarshalDefinition m = (MarshalDefinition) child; DataFormatDefinition dformatDefinition = m.getDataFormatType(); dformatDefinition = processDataFormatType( routeContext, m.getRef(), dformatDefinition ); m.setDataFormatType( dformatDefinition ); // repoint the marshaller, if it was cloned } else if ( child instanceof UnmarshalDefinition ) { UnmarshalDefinition m = (UnmarshalDefinition) child; DataFormatDefinition dformatDefinition = m.getDataFormatType(); dformatDefinition = processDataFormatType( routeContext, m.getRef(), dformatDefinition ); m.setDataFormatType( dformatDefinition ); // repoint the marshaller, if it was cloned } } for ( Iterator<ProcessorDefinition> it = nav.getOutputs().iterator(); it.hasNext(); ) { ProcessorDefinition child = it.next(); augmentNodes( routeContext, child, visited ); } } } private static DataFormatDefinition processDataFormatType(RouteContext routeContext, String ref, DataFormatDefinition dformatDefinition) { if ( dformatDefinition == null ) { if ( "json".equals( ref ) ) { dformatDefinition = new XStreamDataFormat(); ((XStreamDataFormat) dformatDefinition).setDriver( "json" ); } else if ( "xstream".equals( ref ) ) { dformatDefinition = new XStreamDataFormat(); } else if ( "jaxb".equals( ref ) ) { dformatDefinition = new JaxbDataFormat(); } else { dformatDefinition = routeContext.getCamelContext().resolveDataFormatDefinition( ref ); } } // always clone before changing dformatDefinition = new FastCloner().deepClone( dformatDefinition ); if ( dformatDefinition instanceof JaxbDataFormat ) { dformatDefinition = augmentJaxbDataFormatDefinition( (JaxbDataFormat) dformatDefinition ); } else if ( dformatDefinition instanceof XStreamDataFormat ) { XStreamDataFormat xstreamDataFormat = (XStreamDataFormat) dformatDefinition; if ( "json".equals( xstreamDataFormat.getDriver() ) ) { dformatDefinition = XStreamJson.newJSonMarshaller( xstreamDataFormat );; } else { dformatDefinition = XStreamXml.newXStreamMarshaller( (XStreamDataFormat) dformatDefinition ); } } return dformatDefinition; } private ToDefinition getDroolsNode(ProcessorDefinition nav) { if ( !nav.getOutputs().isEmpty() ) { List<ProcessorDefinition> children = nav.getOutputs(); for ( ProcessorDefinition child : children ) { if ( child instanceof ToDefinition ) { ToDefinition to = (ToDefinition) child; if ( to.getUri().trim().startsWith( "drools:" ) ) { return to; } } getDroolsNode( child ); } } return null; } /** * Clones the passed JaxbDataFormat and then augments it with with Drools related namespaces * * @param jaxbDataFormat * @return */ public static JaxbDataFormat augmentJaxbDataFormatDefinition(JaxbDataFormat jaxbDataFormat) { Set<String> set = new HashSet<String>(); for ( String clsName : JAXB_ANNOTATED_CMD ) { set.add( clsName.substring( 0, clsName.lastIndexOf( '.' ) ) ); } StringBuilder sb = new StringBuilder(); sb.append( jaxbDataFormat.getContextPath() ); sb.append( ":" ); for ( String pkgName : set ) { sb.append( pkgName ); sb.append( ':' ); } jaxbDataFormat.setContextPath( sb.toString() ); return jaxbDataFormat; } public static class DroolsClientProcessor implements Processor { private Processor processor; public DroolsClientProcessor(Processor processor) { this.processor = processor; } public void process(Exchange exchange) throws Exception { exchange.setPattern( ExchangePattern.InOut ); Message inMessage = exchange.getIn(); inMessage.setHeader( CxfConstants.CAMEL_CXF_RS_USING_HTTP_API, Boolean.TRUE ); inMessage.setHeader( Exchange.HTTP_METHOD, "POST" ); inMessage.setHeader( Exchange.HTTP_PATH, "/execute" ); inMessage.setHeader( Exchange.ACCEPT_CONTENT_TYPE, "text/plain" ); inMessage.setHeader( Exchange.CONTENT_TYPE, "text/plain" ); this.processor.process( exchange ); } } public static class DroolsProcess implements Processor { private String droolsUri; private DroolsEndpoint dep; private Processor processor; public DroolsProcess(String droolsUri, Processor processor) { this.droolsUri = droolsUri; this.processor = processor; } public void process(Exchange exchange) throws Exception { //Bad Hack - Need to remote it and fix it in Camel (if it's a camel problem) //I need to copy the body of the exachange because for some reason // the getContext().getEndpoint() erase the content/or loose the reference String body = exchange.getIn().getBody( String.class ); if ( dep == null ) { this.dep = exchange.getContext().getEndpoint( this.droolsUri, DroolsEndpoint.class ); } if ( dep == null ) { throw new RuntimeException( "Could not find DroolsEndPoint for uri=" + this.droolsUri ); } ClassLoader originalClassLoader = null; try { originalClassLoader = Thread.currentThread().getContextClassLoader(); CommandExecutor exec = dep.executor; if ( exec == null ) { String lookup = exchange.getIn().getHeader( DroolsComponent.DROOLS_LOOKUP, String.class ); if ( StringUtils.isEmpty( lookup ) ) { //Bad Hack - Need to remote it and fix it in Camel (if it's a camel problem) lookup = dep.getLookup( body ); //lookup = dep.getLookup( exchange.getIn().getBody( String.class ) ); } if ( StringUtils.isEmpty( lookup ) ) { throw new RuntimeException( "No Executor defined and no lookup information available for uri " + this.dep.getEndpointUri() ); } exec = dep.getCommandExecutor( lookup ); } if ( exec == null ) { throw new RuntimeException( "CommandExecutor cannot be found for uri " + this.dep.getEndpointUri() ); } ClassLoader localClassLoader = dep.getClassLoader( exec ); if ( localClassLoader == null ) { throw new RuntimeException( "CommandExecutor Classloader cannot be null for uri " + this.dep.getEndpointUri() ); } // Set the classloader to the one used by the CommandExecutor Thread.currentThread().setContextClassLoader( localClassLoader ); ExecutionNodePipelineContextImpl context = new ExecutionNodePipelineContextImpl( dep.node, localClassLoader ); context.setCommandExecutor( exec ); exchange.setProperty( "drools-context", context ); //Bad Hack - Need to remote it and fix it in Camel (if it's a camel problem) // I need to re set the Body because the exchange loose the content at // the begining of the method exchange.getIn().setBody(new ByteArrayInputStream(body.getBytes("UTF-8"))); boolean soap = false; if ( !augmented && exchange.getFromEndpoint() instanceof CxfSpringEndpoint ) { new PreCxfTransportSoapProcessor().process( exchange ); soap = true; } processor.process( exchange ); if ( soap ) { new PostCxfTransportSoapProcessor().process( exchange ); } } finally { Thread.currentThread().setContextClassLoader( originalClassLoader ); } } } public static final String[] JAXB_ANNOTATED_CMD = {BatchExecutionCommandImpl.class.getName(), SetGlobalCommand.class.getName(), GetGlobalCommand.class.getName(), FireAllRulesCommand.class.getName(), InsertElementsCommand.class.getName(), InsertObjectCommand.class.getName(), ModifyCommand.class.getName(), SetterImpl.class.getName(), QueryCommand.class.getName(), RetractCommand.class.getName(), AbortWorkItemCommand.class.getName(), SignalEventCommand.class.getName(), StartProcessCommand.class.getName(), BatchExecutionCommandImpl.class.getName(), ExecutionResultImpl.class.getName(), DefaultFactHandle.class.getName(), JaxbListWrapper.class.getName(), FlatQueryResults.class.getName(), CompleteWorkItemCommand.class.getName(), GetObjectsCommand.class.getName()}; }