/**
* 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.container.spring.namespace;
import static org.drools.container.spring.namespace.DefinitionParserHelper.emptyAttributeCheck;
import java.util.List;
import org.drools.ClockType;
import org.drools.SessionConfiguration;
import org.drools.command.runtime.SetGlobalCommand;
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.FireUntilHaltCommand;
import org.drools.command.runtime.rule.InsertObjectCommand;
import org.drools.container.spring.beans.KnowledgeAgentBeanFactory;
import org.drools.container.spring.beans.StatefulKnowledgeSessionBeanFactory;
import org.drools.container.spring.beans.StatefulKnowledgeSessionBeanFactory.JpaConfiguration;
import org.drools.container.spring.beans.StatelessKnowledgeSessionBeanFactory;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class KnowledgeSessionDefinitionParser extends AbstractBeanDefinitionParser {
private static final String KBASE_ATTRIBUTE = "kbase";
private static final String EMF_ATTRIBUTE = "entity-manager-factory";
private static final String TX_MANAGER_ATTRIBUTE = "transaction-manager";
private static final String FORCLASS_ATTRIBUTE = "for-class";
private static final String IMPLEMENTATION_ATTRIBUTE = "implementation";
private static final String NAME_ATTRIBUTE = "name";
private static final String GRID_NODE_ATTRIBUTE = "node";
private static final String TYPE_ATTRIBUTE = "type";
private static final String KEEP_REFERENCE = "keep-reference";
private static final String CLOCK_TYPE = "clock-type";
private static final String WORK_ITEMS = "work-item-handlers";
private static final String WORK_ITEM = "work-item-handler";
@SuppressWarnings("unchecked")
protected AbstractBeanDefinition parseInternal(Element element,
ParserContext parserContext) {
String id = element.getAttribute( "id" );
emptyAttributeCheck( element.getLocalName(),
"id",
id );
String kbase = element.getAttribute( KBASE_ATTRIBUTE );
emptyAttributeCheck( element.getLocalName(),
KBASE_ATTRIBUTE,
kbase );
String sessionType = element.getAttribute( TYPE_ATTRIBUTE );
BeanDefinitionBuilder factory;
if ( "stateful".equals( sessionType ) ) {
factory = BeanDefinitionBuilder.rootBeanDefinition( StatefulKnowledgeSessionBeanFactory.class );
} else if ( "stateless".equals( sessionType ) ) {
factory = BeanDefinitionBuilder.rootBeanDefinition( StatelessKnowledgeSessionBeanFactory.class );
} else {
throw new IllegalArgumentException( "Invalid value for " + TYPE_ATTRIBUTE + " attribute: " + sessionType );
}
factory.addPropertyReference( "kbase",
kbase );
String node = element.getAttribute( GRID_NODE_ATTRIBUTE );
if ( node != null && node.length() > 0 ) {
factory.addPropertyReference( "node",
node );
}
String name = element.getAttribute( NAME_ATTRIBUTE );
if ( StringUtils.hasText( name ) ) {
factory.addPropertyValue( "name",
name );
} else {
factory.addPropertyValue( "name",
id );
}
Element ksessionConf = DomUtils.getChildElementByTagName( element,
"configuration" );
if ( ksessionConf != null ) {
Element persistenceElm = DomUtils.getChildElementByTagName( ksessionConf,
"jpa-persistence" );
if ( persistenceElm != null ) {
BeanDefinitionBuilder beanBuilder = BeanDefinitionBuilder.genericBeanDefinition( JpaConfiguration.class );
String loadId = persistenceElm.getAttribute( "load" );
if ( StringUtils.hasText( loadId ) ) {
beanBuilder.addPropertyValue( "id",
Long.parseLong( loadId ) );
}
Element tnxMng = DomUtils.getChildElementByTagName( persistenceElm,
TX_MANAGER_ATTRIBUTE );
String ref = tnxMng.getAttribute( "ref" );
beanBuilder.addPropertyReference( "platformTransactionManager",
ref );
Element emf = DomUtils.getChildElementByTagName( persistenceElm,
EMF_ATTRIBUTE );
ref = emf.getAttribute( "ref" );
beanBuilder.addPropertyReference( "entityManagerFactory",
ref );
Element variablePersisters = DomUtils.getChildElementByTagName( persistenceElm,
"variable-persisters" );
if ( variablePersisters != null && variablePersisters.hasChildNodes() ) {
List<Element> childPersisterElems = DomUtils.getChildElementsByTagName( variablePersisters,
"persister" );
ManagedMap persistors = new ManagedMap( childPersisterElems.size() );
for ( Element persisterElem : childPersisterElems ) {
String forClass = persisterElem.getAttribute( FORCLASS_ATTRIBUTE );
String implementation = persisterElem.getAttribute( IMPLEMENTATION_ATTRIBUTE );
if ( !StringUtils.hasText( forClass ) ) {
throw new RuntimeException( "persister element must have valid for-class attribute" );
}
if ( !StringUtils.hasText( implementation ) ) {
throw new RuntimeException( "persister element must have valid implementation attribute" );
}
persistors.put( forClass,
implementation );
}
beanBuilder.addPropertyValue( "variablePersisters",
persistors );
}
factory.addPropertyValue( "jpaConfiguration",
beanBuilder.getBeanDefinition() );
}
BeanDefinitionBuilder rbaseConfBuilder = BeanDefinitionBuilder.rootBeanDefinition( SessionConfiguration.class );
Element e = DomUtils.getChildElementByTagName( ksessionConf,
KEEP_REFERENCE );
if ( e != null && StringUtils.hasText( e.getAttribute( "enabled" ) ) ) {
rbaseConfBuilder.addPropertyValue( "keepReference",
Boolean.parseBoolean( e.getAttribute( "enabled" ) ) );
}
e = DomUtils.getChildElementByTagName( ksessionConf,
CLOCK_TYPE );
if ( e != null && StringUtils.hasText( e.getAttribute( "type" ) ) ) {
rbaseConfBuilder.addPropertyValue( "clockType",
ClockType.resolveClockType( e.getAttribute( "type" ) ) );
}
factory.addPropertyValue( "conf",
rbaseConfBuilder.getBeanDefinition() );
e = DomUtils.getChildElementByTagName( ksessionConf,
WORK_ITEMS );
if ( e != null ) {
List<Element> children = DomUtils.getChildElementsByTagName( e,
WORK_ITEM );
if ( children != null && !children.isEmpty() ) {
ManagedMap workDefs = new ManagedMap();
for ( Element child : children ) {
workDefs.put( child.getAttribute( "name" ),
new RuntimeBeanReference( child.getAttribute( "ref" ) ) );
}
factory.addPropertyValue( "workItems",
workDefs );
}
}
}
Element batch = DomUtils.getChildElementByTagName( element,
"batch" );
if ( batch == null ) {
// just temporary legacy suppport
batch = DomUtils.getChildElementByTagName( element,
"script" );
}
if ( batch != null ) {
// we know there can only ever be one
ManagedList children = new ManagedList();
for ( int i = 0, length = batch.getChildNodes().getLength(); i < length; i++ ) {
Node n = batch.getChildNodes().item( i );
if ( n instanceof Element ) {
Element e = (Element) n;
BeanDefinitionBuilder beanBuilder = null;
if ( "insert-object".equals( e.getLocalName() ) ) {
String ref = e.getAttribute( "ref" );
Element nestedElm = getFirstElement( e.getChildNodes() );
beanBuilder = BeanDefinitionBuilder.genericBeanDefinition( InsertObjectCommand.class );
if ( StringUtils.hasText( ref ) ) {
beanBuilder.addConstructorArgReference( ref );
} else if ( nestedElm != null ) {
beanBuilder.addConstructorArgValue( parserContext.getDelegate().parsePropertySubElement( nestedElm,
null,
null ) );
} else {
throw new IllegalArgumentException( "insert-object must either specify a 'ref' attribute or have a nested bean" );
}
} else if ( "set-global".equals( e.getLocalName() ) ) {
String ref = e.getAttribute( "ref" );
Element nestedElm = getFirstElement( e.getChildNodes() );
beanBuilder = BeanDefinitionBuilder.genericBeanDefinition( SetGlobalCommand.class );
beanBuilder.addConstructorArgValue( e.getAttribute( "identifier" ) );
if ( StringUtils.hasText( ref ) ) {
beanBuilder.addConstructorArgReference( ref );
} else if ( nestedElm != null ) {
beanBuilder.addConstructorArgValue( parserContext.getDelegate().parsePropertySubElement( nestedElm,
null,
null ) );
} else {
throw new IllegalArgumentException( "set-global must either specify a 'ref' attribute or have a nested bean" );
}
} else if ( "fire-until-halt".equals( e.getLocalName() ) ) {
beanBuilder = BeanDefinitionBuilder.genericBeanDefinition( FireUntilHaltCommand.class );
} else if ( "fire-all-rules".equals( e.getLocalName() ) ) {
beanBuilder = BeanDefinitionBuilder.genericBeanDefinition( FireAllRulesCommand.class );
String max = e.getAttribute( "max" );
if ( StringUtils.hasText( max ) ) {
beanBuilder.addPropertyValue( "max",
max );
}
} else if ( "start-process".equals( e.getLocalName() ) ) {
beanBuilder = BeanDefinitionBuilder.genericBeanDefinition( StartProcessCommand.class );
String processId = e.getAttribute( "process-id" );
if ( !StringUtils.hasText( processId ) ) {
throw new IllegalArgumentException( "start-process must specify a process-id" );
}
beanBuilder.addConstructorArgValue( processId );
List<Element> params = DomUtils.getChildElementsByTagName( e,
"parameter" );
if ( !params.isEmpty() ) {
ManagedMap map = new ManagedMap();
for ( Element param : params ) {
String identifier = param.getAttribute( "identifier" );
if ( !StringUtils.hasText( identifier ) ) {
throw new IllegalArgumentException( "start-process paramaters must specify an identifier" );
}
String ref = param.getAttribute( "ref" );
Element nestedElm = getFirstElement( param.getChildNodes() );
if ( StringUtils.hasText( ref ) ) {
map.put( identifier,
new RuntimeBeanReference( ref ) );
} else if ( nestedElm != null ) {
map.put( identifier,
parserContext.getDelegate().parsePropertySubElement( nestedElm,
null,
null ) );
} else {
throw new IllegalArgumentException( "start-process paramaters must either specify a 'ref' attribute or have a nested bean" );
}
}
beanBuilder.addPropertyValue( "parameters",
map );
}
} else if ( "signal-event".equals( e.getLocalName() ) ) {
beanBuilder = BeanDefinitionBuilder.genericBeanDefinition( SignalEventCommand.class );
String processInstanceId = e.getAttribute( "process-instance-id" );
if ( StringUtils.hasText( processInstanceId ) ) {
beanBuilder.addConstructorArgValue( processInstanceId );
}
beanBuilder.addConstructorArgValue( e.getAttribute( "event-type" ) );
String ref = e.getAttribute( "ref" );
Element nestedElm = getFirstElement( e.getChildNodes() );
if ( StringUtils.hasText( ref ) ) {
beanBuilder.addConstructorArgReference( ref );
} else if ( nestedElm != null ) {
beanBuilder.addConstructorArgValue( parserContext.getDelegate().parsePropertySubElement( nestedElm,
null,
null ) );
} else {
throw new IllegalArgumentException( "signal-event must either specify a 'ref' attribute or have a nested bean" );
}
}
if ( beanBuilder == null ) {
throw new IllegalStateException( "Unknow element: " + e.getLocalName() );
}
children.add( beanBuilder.getBeanDefinition() );
}
}
factory.addPropertyValue( "batch",
children );
}
// find any kagent's for the current kbase and assign
for ( String beanName : parserContext.getRegistry().getBeanDefinitionNames() ) {
BeanDefinition def = parserContext.getRegistry().getBeanDefinition( beanName );
if ( def.getBeanClassName().equals( KnowledgeAgentBeanFactory.class.getName() ) ) {
PropertyValue pvalue = def.getPropertyValues().getPropertyValue( "kbase" );
RuntimeBeanReference tbf = (RuntimeBeanReference) pvalue.getValue();
if ( kbase.equals( tbf.getBeanName() ) ) {
factory.addPropertyValue( "knowledgeAgent",
new RuntimeBeanReference( beanName ) );
}
}
}
return factory.getBeanDefinition();
}
private Element getFirstElement(NodeList list) {
for ( int j = 0, lengthj = list.getLength(); j < lengthj; j++ ) {
if ( list.item( j ) instanceof Element ) {
return (Element) list.item( j );
}
}
return null;
}
}