/*
* 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.core.FactHandle;
import org.drools.core.SessionConfiguration;
import org.drools.core.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
*/
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" );
}
}
}