Janino

ExtendsType using ClassBodyEvaluator throws java.lang.NullPointerException

Details

  • Type: Bug Bug
  • Status: Resolved Resolved
  • Priority: Major Major
  • Resolution: Won't Fix
  • Affects Version/s: None
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None
  • Number of attachments :
    0

Description

As soon as you have a ScriptEvaluator that extends a class it breaks - this was something that definitely worked in 2.1.0:
java.lang.NullPointerException
at org.codehaus.janino.Java$ClassDeclaration.compile(Java.java:850)
at org.codehaus.janino.Java$CompilationUnit.compile(Java.java:378)
at org.codehaus.janino.Java$CompilationUnit.compile(Java.java:367)
at org.codehaus.janino.EvaluatorBase.compileAndLoad(EvaluatorBase.java:259)
at org.codehaus.janino.EvaluatorBase.compileAndLoad(EvaluatorBase.java:293)
at org.codehaus.janino.ScriptEvaluator.<init>(ScriptEvaluator.java:323)
at org.codehaus.janino.ScriptEvaluator.createFastScriptEvaluator(ScriptEvaluator.java:416)
at org.drools.semantics.java.TestJanino.test1(TestJanino.java:47)

I have made a junit test script to prove this:
package org.drools.semantics.java;

import java.util.HashMap;

import org.codehaus.janino.ByteArrayClassLoader;
import org.codehaus.janino.ClassBodyEvaluator;
import org.codehaus.janino.Scanner;
import org.codehaus.janino.ScriptEvaluator;

import junit.framework.TestCase;

public class TestJanino extends TestCase
{

public void test1() throws Exception
{
String func = "public void helloWorld(java.lang.String hello)";
func += "{"; func += " System.err.println(hello + \" World\");"; func += "}";

ClassLoader classLoader = new ByteArrayClassLoader( new HashMap( ),
Thread.currentThread( ).getContextClassLoader( ) );

ClassBodyEvaluator classBody = new ClassBodyEvaluator( new Scanner( null,
new java.io.StringReader( func ) ),
"func0",
null,
new Class[]{},
classLoader );
Class funcClass = classBody.evaluate( );

String text = "return true;";

try
{
Script script = (Script) ScriptEvaluator.createFastScriptEvaluator( new Scanner( null,
new java.io.StringReader( text ) ),
"condition0",
null,
Script.class,
new String[]{},
classLoader );
}
catch ( Exception e )

{ fail( "this should pass" ); }

try
{
Script script = (Script) ScriptEvaluator.createFastScriptEvaluator( new Scanner( null,
new java.io.StringReader( text ) ),
"condition0",
funcClass,
Script.class,
new String[]{},
classLoader );
fail( "this should fail" );
}
catch ( Exception e )

{ e.printStackTrace( ); }

}

public static interface Script

{ public boolean invoke() throws Exception; }

}

Activity

Hide
Arno Unkrig added a comment -

Hi Mark,

your problem is caused by a subtle incompatible change that I introduced in version 2.2.0 and didn't document clearly enough. I'll make good for that in the next version (JAVADOC and change log).
<p>
The description of the incompatible change is:

<pre>

  • Notice that from version 2.1.0 to 2.2.0, the parameter was renamed from
  • <code>optionalClassLoader</code> to <code>optionalparentClassLoader</code>
  • together with an incompatible semantic change:
  • <dl>
  • <dt>2.1.0
  • <dd>If the <code>optionalClassLoader</code> was a
  • {@link ByteArrayClassLoader}, the generated classes were loaded into it,
    * otherwise a new {@link ByteArrayClassLoader} was created with the
  • <code>optionalClassLoader</code> as the parent {@link ClassLoader}.
    * <dt>2.2.0
    * <dd>A new {@link ByteArrayClassLoader} is always created with the
    * <code>optionalClassLoader</code> as the parent {@link ClassLoader}.
  • </dl>
  • The old behavior was regarded as ugly because it depends on an argument's
  • type.
    </pre>

As a consequence, the "old trick":
<pre>
ClassLoader cl = new ByteArrayClassLoader();
Class class1 = new ClassBodyEvaluator(... , cl);
new XyzEvaluator(... , class1, ... , cl);
</pre>
doesn't work any more, instead you have to write:
<pre>
Class class1 = new ClassBodyEvaluator(...);
new XyzEvaluator(... , class1, ... , class1.getClassLoader());
</pre>
Notice that the new code works both for 2.1.0 AND 2.2.0.
<p>
I also added code that avoids the NullPointerException and throws a verbose
RuntimeException instead:
<p>
java.lang.RuntimeException: Cannot load class "func0" through the given ClassLoader
<p>
Sorry for the inconvenience!

Show
Arno Unkrig added a comment - Hi Mark, your problem is caused by a subtle incompatible change that I introduced in version 2.2.0 and didn't document clearly enough. I'll make good for that in the next version (JAVADOC and change log). <p> The description of the incompatible change is: <pre>
  • Notice that from version 2.1.0 to 2.2.0, the parameter was renamed from
  • <code>optionalClassLoader</code> to <code>optionalparentClassLoader</code>
  • together with an incompatible semantic change:
  • <dl>
  • <dt>2.1.0
  • <dd>If the <code>optionalClassLoader</code> was a
  • {@link ByteArrayClassLoader}, the generated classes were loaded into it, * otherwise a new {@link ByteArrayClassLoader} was created with the
  • <code>optionalClassLoader</code> as the parent {@link ClassLoader}. * <dt>2.2.0 * <dd>A new {@link ByteArrayClassLoader} is always created with the * <code>optionalClassLoader</code> as the parent {@link ClassLoader}.
  • </dl>
  • The old behavior was regarded as ugly because it depends on an argument's
  • type. </pre>
As a consequence, the "old trick": <pre> ClassLoader cl = new ByteArrayClassLoader(); Class class1 = new ClassBodyEvaluator(... , cl); new XyzEvaluator(... , class1, ... , cl); </pre> doesn't work any more, instead you have to write: <pre> Class class1 = new ClassBodyEvaluator(...); new XyzEvaluator(... , class1, ... , class1.getClassLoader()); </pre> Notice that the new code works both for 2.1.0 AND 2.2.0. <p> I also added code that avoids the NullPointerException and throws a verbose RuntimeException instead: <p> java.lang.RuntimeException: Cannot load class "func0" through the given ClassLoader <p> Sorry for the inconvenience!

People

Vote (0)
Watch (0)

Dates

  • Created:
    Updated:
    Resolved: