/* * Copyright 2005 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.jsr94.rules; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.rules.Handle; import javax.rules.InvalidHandleException; import javax.rules.InvalidRuleSessionException; import javax.rules.ObjectFilter; import javax.rules.RuleExecutionSetNotFoundException; import javax.rules.RuleRuntime; import javax.rules.RuleSessionCreateException; import javax.rules.StatefulRuleSession; import org.drools.FactHandle; import org.drools.SessionConfiguration; import org.drools.StatefulSession; import org.drools.jsr94.rules.admin.RuleExecutionSetImpl; import org.drools.jsr94.rules.repository.RuleExecutionSetRepository; import org.drools.jsr94.rules.repository.RuleExecutionSetRepositoryException; /** * The Drools implementation of the <code>StatefulRuleSession</code> interface * which is a representation of a stateful rules engine session. A stateful * rules engine session exposes a stateful rule execution API to an underlying * rules engine. The session allows arbitrary objects to be added and removed to * and from the rule session state. Additionally, objects currently part of the * rule session state may be updated. <p/> There are inherently side-effects to * adding objects to the rule session state. The execution of a RuleExecutionSet * can add, remove and update objects in the rule session state. The objects in * the rule session state are therefore dependent on the rules within the * <code>RuleExecutionSet</code> as well as the rule engine vendor's specific * rule engine behavior. <p/> <code>Handle</code> instances are used by the * rule engine vendor to track <code>Object</code>s added to the rule session * state. This allows multiple instances of equivalent <code>Object</code>s * to be added to the session state and identified, even after serialization. * * @see StatefulRuleSession * * @author N. Alex Rupp (n_alex <at>codehaus.org) * @author <a href="mailto:thomas.diesler@softcon-itec.de">thomas diesler </a> */ public class StatefulRuleSessionImpl extends AbstractRuleSessionImpl implements StatefulRuleSession { // ---------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------- /** * */ private static final long serialVersionUID = 510l; private StatefulSession session; /** * Gets the <code>RuleExecutionSet</code> for this URI and associates it * with a RuleBase. * * @param bindUri * the URI the <code>RuleExecutionSet</code> has been bound to * @param properties * additional properties used to create the * <code>RuleSession</code> implementation. * * @throws RuleExecutionSetNotFoundException * if there is no rule set under the given URI * @throws RuleSessionCreateException */ StatefulRuleSessionImpl(final String bindUri, final Map properties, final RuleExecutionSetRepository repository) throws RuleExecutionSetNotFoundException, RuleSessionCreateException { super( repository ); setProperties( properties ); RuleExecutionSetImpl ruleSet = null; try { ruleSet = (RuleExecutionSetImpl) repository.getRuleExecutionSet(bindUri, properties); } catch (RuleExecutionSetRepositoryException e) { String s = "Error while retrieving rule execution set bound to: " + bindUri; throw new RuleSessionCreateException(s, e); } if ( ruleSet == null ) { throw new RuleExecutionSetNotFoundException( "no execution set bound to: " + bindUri ); } this.setRuleExecutionSet( ruleSet ); SessionConfiguration conf = new SessionConfiguration(); conf.setKeepReference( true ); initSession( conf ); } /** * Initialize this <code>RuleSession</code> * with a new <code>WorkingMemory</code>. */ protected void initSession(SessionConfiguration conf) { this.session = this.getRuleExecutionSet().newStatefulSession( conf ); final Map props = this.getProperties(); if ( props != null ) { for ( final Iterator iterator = props.entrySet().iterator(); iterator.hasNext(); ) { final Map.Entry entry = (Map.Entry) iterator.next(); this.session.setGlobal( (String) entry.getKey(), entry.getValue() ); } } } // ---------------------------------------------------------------------- // Instance methods // ---------------------------------------------------------------------- /** * Returns <code>true</code> if the given object is contained within * rulesession state of this rule session. * * @param objectHandle * the handle to the target object. * * @return <code>true</code> if the given object is contained within the * rule session state of this rule session. */ public boolean containsObject(final Handle objectHandle) { if ( objectHandle instanceof FactHandle ) { return this.session.getObject( (FactHandle) objectHandle ) != null; } return false; } /** * Adds a given object to the rule session state of this rule session. The * argument to this method is Object because in the non-managed env. not all * objects should have to implement Serializable. If the * <code>RuleSession</code> is <code>Serializable</code> and it contains * non-serializable fields a runtime exception will be thrown. * * @param object * the object to be added. * * @return the Handle for the newly added Object * * @throws InvalidRuleSessionException * on illegal rule session state. */ public Handle addObject(final Object object) throws InvalidRuleSessionException { checkRuleSessionValidity(); return (Handle) this.session.insert( object ); } /** * Adds a <code>List</code> of <code>Object</code>s to the rule session * state of this rule session. * * @param objList * the objects to be added. * * @return a <code>List</code> of <code>Handle</code>s, one for each * added <code>Object</code>. The <code>List</code> must be * ordered in the same order as the input <code>objList</code>. * * @throws InvalidRuleSessionException * on illegal rule session state. */ public List addObjects(final List objList) throws InvalidRuleSessionException { checkRuleSessionValidity(); final List handles = new ArrayList(); for ( final Iterator objectIter = objList.iterator(); objectIter.hasNext(); ) { handles.add( addObject( objectIter.next() ) ); } return handles; } /** * Notifies the rules engine that a given object in the rule session state * has changed. <p/> The semantics of this call are equivalent to calling * <code>removeObject</code> followed by <code>addObject</code>. The * original <code>Handle</code> is rebound to the new value for the * <code>Object</code> however. * * @param objectHandle * the handle to the original object. * @param newObject * the new object to bind to the handle. * * @throws InvalidRuleSessionException * on illegal rule session state. * @throws InvalidHandleException * if the input <code>Handle</code> is no longer valid */ public void updateObject(final Handle objectHandle, final Object newObject) throws InvalidRuleSessionException, InvalidHandleException { checkRuleSessionValidity(); if ( objectHandle instanceof FactHandle ) { this.session.update( (FactHandle) objectHandle, newObject ); } else { throw new InvalidHandleException( "invalid handle" ); } } /** * Removes a given object from the rule session state of this rule session. * * @param handleObject * the handle to the object to be removed from the rule session * state. * * @throws InvalidRuleSessionException * on illegal rule session state. * @throws InvalidHandleException * if the input <code>Handle</code> is no longer valid */ public void removeObject(final Handle handleObject) throws InvalidRuleSessionException, InvalidHandleException { checkRuleSessionValidity(); if ( handleObject instanceof FactHandle ) { this.session.retract( (FactHandle) handleObject ); } else { throw new InvalidHandleException( "invalid handle" ); } } /** * Executes the rules in the bound rule execution set using the objects * present in the rule session state. This will typically modify the rule * session state - and may add, remove or update <code>Object</code>s * bound to <code>Handle</code>s. * * @throws InvalidRuleSessionException * on illegal rule session state. */ public void executeRules() throws InvalidRuleSessionException { checkRuleSessionValidity(); this.session.fireAllRules(); } /** * @see StatefulRuleSessionImpl */ public Object getObject(final Handle handle) throws InvalidRuleSessionException, InvalidHandleException { checkRuleSessionValidity(); if ( handle instanceof FactHandle ) { return this.session.getObject( (FactHandle) handle ); } else { throw new InvalidHandleException( "invalid handle" ); } } /** * Returns a <code>List</code> of the <code>Handle</code>s being used * for object identity. * * @return a <code>List</code> of <code>Handle</code>s present in the * currect state of the rule session. */ public List getHandles() { return IteratorToList.convert( this.session.iterateFactHandles() ); } /** * Returns a List of all objects in the rule session state of this rule * session. The objects should pass the default filter test of the default * <code>RuleExecutionSet</code> filter (if present). <p/> This may not * neccessarily include all objects added by calls to <code>addObject</code>, * and may include <code>Object</code>s created by side-effects. The * execution of a <code>RuleExecutionSet</code> can add, remove and update * objects as part of the rule session state. Therefore the rule session * state is dependent on the rules that are part of the executed * <code>RuleExecutionSet</code> as well as the rule vendor's specific * rule engine behavior. * * @return a <code>List</code> of all objects part of the rule session * state. * * @throws InvalidRuleSessionException * on illegal rule session state. */ public List getObjects() throws InvalidRuleSessionException { checkRuleSessionValidity(); return getObjects( getRuleExecutionSet().getObjectFilter() ); } /** * Returns a <code>List</code> over the objects in rule session state of * this rule session. The objects should pass the filter test on the * specified <code>ObjectFilter</code>. <p/> This may not neccessarily * include all objects added by calls to <code>addObject</code>, and may * include <code>Object</code>s created by side-effects. The execution of * a <code>RuleExecutionSet</code> can add, remove and update objects as * part of the rule session state. Therefore the rule session state is * dependent on the rules that are part of the executed * <code>RuleExecutionSet</code> as well as the rule vendor's specific * rule engine behavior. * * @param filter * the object filter. * * @return a <code>List</code> of all the objects in the rule session * state of this rule session based upon the given object filter. * * @throws InvalidRuleSessionException * on illegal rule session state. */ public List getObjects(final ObjectFilter filter) throws InvalidRuleSessionException { checkRuleSessionValidity(); return IteratorToList.convert( this.session.iterateObjects( new ObjectFilterAdapter( filter ) ) ); } /** * Resets this rule session. Calling this method will bring the rule session * state to its initial state for this rule session and will reset any other * state associated with this rule session. * <p/> * A reset will not reset the state on the default object filter for a * <code>RuleExecutionSet</code>. */ public void reset() { // stateful rule sessions should not be high load, thus safe to keep references initSession( new SessionConfiguration() ); } public int getType() throws InvalidRuleSessionException { return RuleRuntime.STATEFUL_SESSION_TYPE; } /** * Releases all resources used by this rule session. * This method renders this rule session unusable until * it is reacquired through the <code>RuleRuntime</code>. */ public void release() { if ( this.session != null ) { this.session.dispose(); } this.session = null; super.release(); } /** * Ensures this <code>RuleSession</code> is not * in an illegal rule session state. * * @throws InvalidRuleSessionException on illegal rule session state. */ protected void checkRuleSessionValidity() throws InvalidRuleSessionException { if ( this.session == null ) { throw new InvalidRuleSessionException( "invalid rule session" ); } } }