Bug 724236 (BRMS-180) - Modify raises NPE when using "from"
Summary: Modify raises NPE when using "from"
Keywords:
Status: CLOSED NEXTRELEASE
Alias: BRMS-180
Product: JBoss Enterprise BRMS Platform 5
Classification: JBoss
Component: unspecified
Version: unspecified
Hardware: Unspecified
OS: Unspecified
high
unspecified
Target Milestone: ---
: 5.0.1
Assignee: Edson Tirelli
QA Contact:
URL: http://jira.jboss.org/jira/browse/BRM...
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2009-07-07 15:25 UTC by nwallace
Modified: 2009-10-05 09:21 UTC (History)
0 users

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of:
Environment:
Last Closed: 2009-09-01 12:28:52 UTC
Type: Bug


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Red Hat Issue Tracker BRMS-180 0 None None None Never

Description nwallace 2009-07-07 15:25:18 UTC
Date of First Response: 2009-09-10 01:34:39
securitylevel_name: Public

The attached file should generate everyting into a subdirectory ./movie and compile and execute.

You'll have to modify the classpath, though.

Regards
-W


# Shell this file to create subdir movie with *.java, Movie.drl, database.xml
#
# you'll have to modify the classpath setting - see below
#

mkdir movie || true

cat <<'TheEnd' >movie/Main.java
package movie;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Collection;

import javax.xml.bind.JAXBContext;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.definition.KnowledgePackage;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;

import javax.xml.bind.Unmarshaller;

public class Main {

       private StatefulKnowledgeSession session;

       public Main(){
       }

       public void init() throws Exception {

       KnowledgeBase kBase = KnowledgeBaseFactory.newKnowledgeBase();
       KnowledgeBuilder kBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
       kBuilder.add( ResourceFactory.newFileResource( "movie/Movie.drl" ),
                     ResourceType.DRL );
       if( kBuilder.hasErrors() ){
               for( KnowledgeBuilderError err: kBuilder.getErrors() ){
                       System.out.println( err.toString() );
               }
               throw new IllegalStateException( "DRL errors" );
       }

       kBase.addKnowledgePackages( kBuilder.getKnowledgePackages() );

               File xml = new File( "database.xml" );
               JAXBContext jc = JAXBContext.newInstance( Database.class );
               Unmarshaller u = jc.createUnmarshaller();
               InputStream inputStream = new FileInputStream( xml );
               Database db = (Database)u.unmarshal( inputStream );
               inputStream.close();

       session = kBase.newStatefulKnowledgeSession();

               for( Movie m: db.getMovie() ){
                       session.insert( m );
               }
               for( Actor a: db.getActor() ){
                       session.insert( a );
               }
               for( Role r: db.getRole() ){
                       session.insert( r );
               }
               session.fireAllRules();
       }

       public static void main( String[] args ) throws Exception {
               Main rf = new Main();
               rf.init();
       }

}
TheEnd

cat <<'TheEnd' >movie/Role.java
package movie;

import javax.xml.bind.annotation.XmlAttribute;

public class Role implements Item {

       private String actor;
       private String movie;

       public Role(){
       }

       @XmlAttribute
       public String getActor() {
               return actor;
       }

       public void setActor(String actor) {
               this.actor = actor;
       }

       @XmlAttribute
       public String getMovie() {
               return movie;
       }

       public void setMovie(String movie) {
               this.movie = movie;
       }

       @Override
       public String toString(){
               return actor + " plays in " + movie;
       }

}
TheEnd

cat <<'TheEnd' >movie/Movie.java
package movie;

import java.util.HashSet;
import java.util.Set;
import javax.xml.bind.annotation.XmlAttribute;

public class Movie implements Item {
       private String title;
       private Set<Actor> cast;

       public Movie(){
       }

       @XmlAttribute
       public String getTitle() {
               return title;
       }
       public void setTitle(String title) {
               this.title = title;
       }

       public Set<Actor> getCast(){
               if( cast == null ) cast = new HashSet<Actor>();
               return cast;
       }

       @Override
       public int hashCode(){
               return title.hashCode();
       }

       @Override
       public boolean equals( Object o ){
               if( ! (o instanceof Movie ) ) return false;
               return this.title.equals( ((Movie)o).getTitle() );
       }

       @Override
       public String toString(){
               StringBuilder sb = new StringBuilder();
               sb.append( title ).append( "\n" );
               for( Actor a: getCast() ){
                       sb.append( "  " ).append( a.getName() ).append( "\n" );
               }
               return sb.toString();
       }
}
TheEnd

cat <<'TheEnd' >movie/Item.java
package movie;

/**
 * Marker interface for WMEs
 * @author Wolfgang Laun
 */
public interface Item {
}
TheEnd

cat <<'TheEnd' >movie/Database.java
package movie;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement
@XmlType(propOrder={"movie", "actor" , "role" })
public class Database {

       private List<Movie> movie;
       private List<Actor> actor;
       private List<Role> role;

       public Database(){
       }

       @XmlElement
       public List<Movie> getMovie() {
               if( movie == null ) movie = new ArrayList<Movie>();
               return movie;
       }

       @XmlElement
       public List<Actor> getActor() {
               if( actor == null ) actor = new ArrayList<Actor>();
               return actor;
       }

       @XmlElement
       public List<Role> getRole() {
               if( role == null ) role = new ArrayList<Role>();
               return role;
       }

}
TheEnd

cat <<'TheEnd' >movie/Actor.java
package movie;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAttribute;

public class Actor implements Item {

       private String name;
       private List<Movie> movies;

       public Actor(){
       }

       @XmlAttribute
       public String getName() {
               return name;
       }
       public void setName(String name) {
               this.name = name;
       }

       public List<Movie> getMovies(){
               if( movies == null ) movies = new ArrayList<Movie>();
               return movies;
       }

       @Override
       public String toString(){
               StringBuilder sb = new StringBuilder();
               sb.append( name ).append( "\n" );
               for( Movie m: getMovies() ){
                       sb.append( "  " ).append( m.getTitle() ).append( "\n" );
               }
               return sb.toString();
       }

       @Override
       public int hashCode(){
               return name.hashCode();
       }

       @Override
       public boolean equals( Object o ){
               if( ! (o instanceof Actor ) ) return false;
               return this.name.equals( ((Actor)o).getName() );
       }
}
TheEnd

cat <<'TheEnd' >movie/Movie.drl
package movie;

/***
 - List<?> as fact field type
 - Set<?> as fact field type
 - Match WMEs with elements from field with List<?> type: from
 - inline eval() for determining membership in Collection()
 - Write rules for a set of WME types using a "marker" interface.

*/

#
# Fill the Movies' cast lists with data from Role.
#
rule fillCast
   salience 100
   when
       $r : Role( $actor : actor, $movie : movie )
       $a : Actor( name == $actor )
       $m : Movie( title == $movie )
   then
       modify( $m ){
           getCast().add( $a );
       }
       retract( $r );
end

#
# Fill the Actors' list of movies with data from a Movie's cast list
#
rule fillMovies
   salience 90
   when
       $m : Movie( $cast : cast )
       $a : Actor() from $cast
   then
       System.out.println( "movie " + $m.getTitle() + " actor " + $a.getName() );
       modify( $a ){
           getMovies().add( $m );
       }
end

#
# Find a movie where HB and LB are in together
#
rule findHBLB
   when
       $hb : Actor( name == "Humphrey Bogart" )
       $lb : Actor( name == "Lauren Bacall" )
       $m : Movie( $title : title, $cast : cast, eval( $cast.contains( $hb ) && $cast.contains( $lb ) ) )
   then
       System.out.println( "HB & LB together in " + $title );
end

#
# Display any WME
#
rule showItem
   salience -100
   when
       $i : Item()
   then
       System.out.println( $i.toString() );
end
TheEnd

cat <<'TheEnd' >database.xml
<?xml version="1.0" encoding="UTF-8"?>
<database>
<movie title="Casablanca"/>
<movie title="Key Largo"/>
<movie title="To Have and Have Not"/>
<movie title="The African Queen"/>
<actor name="Humphrey Bogart"/>
<actor name="Katherine Hepburn"/>
<actor name="Ingrid Bergman"/>
<actor name="Lauren Bacall"/>
<role actor="Humphrey Bogart" movie="Casablanca"/>
<role actor="Ingrid Bergman"  movie="Casablanca"/>
<role actor="Humphrey Bogart" movie="The African Queen"/>
<role actor="Humphrey Bogart" movie="Key Largo"/>
<role actor="Humphrey Bogart" movie="To Have and Have Not"/>
<role actor="Lauren Bacall" movie="To Have and Have Not"/>
<role actor="Lauren Bacall" movie="Key Largo"/>
<role actor="Katherine Hepburn" movie="The African Queen"/>
</database>
TheEnd

export CLASSPATH=.:/extra/Drools-5.0.1.SNAPSHOT/drools-core.jar:/extra/Drools-5.0.1.SNAPSHOT/drools-api.jar:/extra/Drools-5.0.1.SNAPSHOT/drools-compiler.jar:/extra/Drools-5.0.1.SNAPSHOT/drools-ant.jar:/extra/Drools-5.0.1.SNAPSHOT/antlr-runtime.jar:/usr/local/eclipse/plugins/org.eclipse.jdt.core_3.4.4.v_894_R34x.jar:/extra/Drools-5.0.0.CR1/bin/lib/mvel2-2.0.8pre1.jar

javac movie/*.java

java movie/Main

cat <<'TheEnd'

#
# This is what I get:
#

movie Casablanca actor Ingrid Bergman
Exception in thread "main" org.drools.runtime.rule.ConsequenceException: java.lang.NullPointerException
       at org.drools.runtime.rule.impl.DefaultConsequenceExceptionHandler.handleException(DefaultConsequenceExceptionHandler.java:23)
       at org.drools.common.DefaultAgenda.fireActivation(DefaultAgenda.java:943)
       at org.drools.common.DefaultAgenda.fireNextItem(DefaultAgenda.java:885)
       at org.drools.common.DefaultAgenda.fireAllRules(DefaultAgenda.java:1086)
       at org.drools.common.AbstractWorkingMemory.fireAllRules(AbstractWorkingMemory.java:682)
       at org.drools.common.AbstractWorkingMemory.fireAllRules(AbstractWorkingMemory.java:649)
       at org.drools.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:183)
       at movie.Main.init(Main.java:65)
       at movie.Main.main(Main.java:70)
Caused by: java.lang.NullPointerException
       at org.drools.base.DefaultKnowledgeHelper.modifyRetract(DefaultKnowledgeHelper.java:190)
       at movie.Rule_fillMovies_0.consequence(Rule_fillMovies_0.java:8)
       at movie.Rule_fillMovies_0ConsequenceInvoker.evaluate(Rule_fillMovies_0ConsequenceInvoker.java:28)
       at org.drools.common.DefaultAgenda.fireActivation(DefaultAgenda.java:934)
       ... 7 more
TheEnd

Comment 1 nwallace 2009-07-07 15:26:29 UTC
Link: Added: This issue is related to JBRULES-2085


Comment 2 nwallace 2009-09-01 12:28:52 UTC
Fix in place.

Comment 3 David Le Sage 2009-09-10 05:34:39 UTC
For documenting this in the Release Notes, can you please confirm the following and fill in the missing information. Dot point explanations are fine:

The CAUSE (what was actually broken)
 * 

CONSEQUENCES of the bug (how it impacts users.)
 * A null pointer exception occured when the keyword "from" was used in conjunction with the Modify command,

The FIX (what was changed to eliminate this bug) and 
 *

RESULTS of the fix (what now happens for users.)
 * 

Comment 4 David Le Sage 2009-09-23 05:41:23 UTC
We are still awaiting the outstanding information for the Release Notes on this one.  Please provide it as soon as possible. Thanks.

Comment 5 Edson Tirelli 2009-09-23 13:32:52 UTC
The CAUSE (what was actually broken)
 * The use of "from" on a pattern causes the engine to create a temporary fact handle to assign the result of the from expression. Modify was using the temporary fact handle to modify the fact in the working memory.

CONSEQUENCES of the bug (how it impacts users.)
 * Being temporary, that fact handle is not stored in the working memory and trying to modify a fact using the temporary fact handle was causing a null pointer exception.

The FIX (what was changed to eliminate this bug) and
 * Modify construct was improved to properly deal with temporary fact handles, resolving the actual fact reference to the permanent fact handle.

RESULTS of the fix (what now happens for users.)
 *  No more Null Pointer Exceptions when using modify on a fact returned by "from".

Comment 6 Dana Mison 2009-10-05 09:21:20 UTC
added to 5.0.CP01 Release Notes as resolved

JBRULES-2085
Calling modify() on the fact returned when using from on a pattern would cause a null pointer exception.  This was due to the fact handle in this situation not being stored in working memory because it was a temporary fact handle.  The code was been updated to properly deal with temporary fact handles.


Note You need to log in before you can comment on or make changes to this bug.