hubeny Posted March 13, 2008 Share Posted March 13, 2008 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 More sharing options...
lucianc Posted March 13, 2008 Share Posted March 13, 2008 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 More sharing options...
hubeny Posted March 14, 2008 Author Share Posted March 14, 2008 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 More sharing options...
C-Box Posted March 14, 2008 Share Posted March 14, 2008 As I did something similiar some days ago.... I would call super.findResource(name) if it's not a class, as you just check for "class" files. Otherwise your ClassLoader can't find other resources!??! just my two cents.C-Box Link to comment Share on other sites More sharing options...
nasamad Posted March 16, 2008 Share Posted March 16, 2008 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 More sharing options...
lucianc Posted March 17, 2008 Share Posted March 17, 2008 nasamad wrote:Wouldn't the report template (.jrxml) compile if PerformanceScriptlet were in your classpath? Yes, it would. Also see this FAQ. Regards,Lucian Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now