Jump to content
We've recently updated our Privacy Statement, available here ×

Report compilation & Custom Class Loader


hubeny

Recommended Posts

Hello,

I have an compilation problem of .jrxml report source files to the binary .jasper format.

The problem description:

Simple jrxml report design (testReport.jrxml) is readed (JRXmlLoader) and compiled with the built-in jasper compiler (e.g. the JRJdtCompiler).The report uses a scriptlet. The scriptlet class (PerformanceScriptlet) file is stored in the blob in a database. Therefore I use my class loader in order to load the scriptlet class. The class is successfully read from the database, however the compilation fails with following error:

net.sf.jasperreports.engine.JRException: Errors were encountered when compiling report expressions class file:
1. PerformanceScriptlet cannot be resolved to a type
value = (java.lang.String)(((PerformanceScriptlet)parameter_REPORT_SCRIPTLET.getValue()).toString());//$JR_EXPR_ID=8$
<------------------>
2. PerformanceScriptlet cannot be resolved to a type
value = (java.lang.String)(((PerformanceScriptlet)parameter_REPORT_SCRIPTLET.getValue()).toString());//$JR_EXPR_ID=8$
<------------------>
3. PerformanceScriptlet cannot be resolved to a type
value = (java.lang.String)(((PerformanceScriptlet)parameter_REPORT_SCRIPTLET.getValue()).toString());//$JR_EXPR_ID=8$
<------------------>
3 errors

at net.sf.jasperreports.engine.design.JRAbstractCompiler.compileReport(JRAbstractCompiler.java:193)
at compilationerror.Main.main(Main.java:28)

Thank you in advance for any help.

Honza Hubeny

I made an example NetBeans project in order to simulate this error. It is attached to this post in a zip file.
The class loader in my simulation read the class file from the disk, from path which is not in the CLASSPATH variable.

the simulation program expects that the files and directories will have following structure

/ --- testReport.jrxml
|
--- hidden/classes/PerformanceScriptlet.class

Code listing is as follows:
The Main class:

Code:
package compilationerror;import java.util.logging.Level;import java.util.logging.Logger;import net.sf.jasperreports.engine.JRException;import net.sf.jasperreports.engine.JasperReport;import net.sf.jasperreports.engine.design.JRCompiler;import net.sf.jasperreports.engine.design.JRJdtCompiler;import net.sf.jasperreports.engine.design.JasperDesign;import net.sf.jasperreports.engine.xml.JRXmlLoader;/** * * @author hubeny */public class Main {    public static void main(String[] args) {        try {            MyClassLoader cl = MyClassLoader.getInstance();            JasperDesign design = JRXmlLoader.load("testReport.jrxml"«»);            JRCompiler compiler = new JRJdtCompiler();            Thread.currentThread().setContextClassLoader(cl);            JasperReport report = compiler.compileReport(design);        } catch (JRException ex) {            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);        }    }}[/code]			

My class loader

Code:
package compilationerror;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.util.logging.Level;import java.util.logging.Logger;/** * * @author hubeny */public class MyClassLoader extends ClassLoader {    private final static MyClassLoader instance = new MyClassLoader();    private static final String MY_CLASS_PATH = "hidden/classes/";    protected MyClassLoader() {        super(MyClassLoader.class.getClassLoader());    }    public static MyClassLoader getInstance() {        return instance;    }    private byte[] getClassImplFromDataBase(String className) {        System.out.println("        >>>>>> Fetching scriptlet class implementation..." + className);        byte[] retVal = null;        File classFile = new File(MY_CLASS_PATH + className + ".class"«»);        try {            retVal = getBytesFromFile(classFile);        } catch (IOException ex) {            Logger.getLogger(MyClassLoader.class.getName()).log(Level.SEVERE, null, ex);        }        return retVal;    }    @Override    protected synchronized Class<?> findClass(String name)            throws ClassNotFoundException {        Class result;        byte classData[];        classData = getClassImplFromDataBase(name);        if (classData == null) {            throw new ClassNotFoundException();        }        /* Define it (parse the class file) */        result = defineClass(name, classData, 0, classData.length);        if (result == null) {            throw new ClassFormatError();        }        return result;   }    public static byte[] getBytesFromFile(File file) throws IOException {        InputStream is = new FileInputStream(file);        // Get the size of the file        long length = file.length();        if (length > Integer.MAX_VALUE) {        // File is too large        }        // Create the byte array to hold the data        byte[] bytes = new byte[(int) length];        // Read in the bytes        int offset = 0;        int numRead = 0;        while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0)         {            offset += numRead;        }        // Ensure all the bytes have been read in        if (offset < bytes.length) {            throw new IOException("Could not completely read file " + file.getName()); //NOI18N        }        // Close the input stream and return bytes        is.close();        return bytes;    }}[/code]			

[file name=CompilationError.gz size=10857]

Link to comment
Share on other sites

  • Replies 5
  • Created
  • Last Reply

Top Posters In This Topic

You need to change two things:

  • Switch the order of the following two lines in Main.java (JRJdtCompiler initializes its classloader in the constructor):
    Code:
                JRCompiler compiler = new JRJdtCompiler();            Thread.currentThread().setContextClassLoader(cl);[/code]					

  • Implement findResource in MyClassLoader, as the JDT compiler needs to access class files as resources. If you want to be safe, also implement findResources.
  • [/ol]

    HTH,
    Lucian
    Post edited by: lucianc, at: 2008/03/13 16:26
Link to comment
Share on other sites

Hello,

thank you for your advice. It helps a lot.
I implement the findResource() method in the class loader, add two new classes -- URL StreamHandler and URLConnection and now I can compile reports with scriptlet classes dynamicaly loaded from database.

Thanks a lot!

Honza Hubeny

added code:...

Code:
@Override    protected URL findResource(String name) {        URL retVal = null;        if (name.endsWith(".class"«»)) {            try {                name = name.replace(".class", ""«»);                if (SRptUtils.isScriptletInDB(name)) {                    retVal = new URL("isdb", SRptDBURLStreamHandler.SCRIPTLET_CLASS, -1                                           , name, new SRptDBURLStreamHandler());                }            }            catch (MalformedURLException ex) {                System.out.println(ex.getMessage());                Exceptions.printStackTrace(ex);            }            catch (SQLException ex) {                System.out.println(ex.getMessage());                Exceptions.printStackTrace(ex);            }        }        return retVal;    }[/code]			
Code:
public class SRptDBURLStreamHandler extends URLStreamHandler {public static final String SCRIPTLET_CLASS="ScriptletClass";    @Override    protected URLConnection openConnection(URL u) throws IOException {        return new SRptDBURLConnection(u);    }}[/code]			

and

Code:
public class SRptDBURLConnection extends URLConnection {    public SRptDBURLConnection(URL url) {        super(url);    }    @Override    public void connect() throws IOException {    }    @Override    public InputStream getInputStream() throws IOException {        InputStream retVal = null;        if (SDatabase.checkNullAndActive(false)) {            if (url.getHost().equals(SRptDBURLStreamHandler.SCRIPTLET_CLASS)) {                retVal = SRptReportLoader.readReportScriptletToISFromDB(url.getFile()                                                    , SRptConst.REPORT_TYPES.SCRIPTLET_BIN);            }        }        return retVal;    }}[/code]			
Link to comment
Share on other sites

Hello,

 

I have a question: Wouldn't the report template (.jrxml) compile if PerformanceScriptlet were in your classpath?

I'm working on a project that might need to use custom classes. My intention is to package all the required classes in a jar and add it to the classpath.

I'm getting ready to put an example together and will follow this message up with the results.

Thanks.

Post edited by: nasamad, at: 2008/03/16 05:19

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...