richardc Posted August 6, 2008 Share Posted August 6, 2008 Hi,I am trying to compile a report using JasperCompileManager.compileReport(...). As I use my own classes I have to set the classpath using: JRProperties.setProperty(JRProperties.COMPILER_CLASSPATH, "..."). This seems to work ok as long as I have the classes in the WEB-INF/lib directory. However, if I have a class outside of WEB-INF/lib and add it to the classpath using JRProperties.setProperty(JRProperties.COMPILER_CLASSPATH, "..."), I get the following exception:net.sf.jasperreports.engine.design.JRValidationException: Report design not valid : 1. java.lang.ClassNotFoundException: com.verticali.vap.reporting.scriptlets.ReportScriptletUtilsat net.sf.jasperreports.engine.design.JRAbstractCompiler.verifyDesign(JRAbstractCompiler.java:260)at net.sf.jasperreports.engine.design.JRAbstractCompiler.compileReport(JRAbstractCompiler.java:144)at net.sf.jasperreports.engine.JasperCompileManager.compileReport(JasperCompileManager.java:220)at net.sf.jasperreports.engine.JasperCompileManager.compileReport(JasperCompileManager.java:206) This looks like the same bug as reported in:http://jasperforge.org/plugins/espforum/view.php?group_id=102&forumid=103&topicid=18750In that bug, Andrey Shuvikov wrote: "Looking in the code I noticed that JRAbstractCompiler calls verifyDesign() _before_ dealing with classpath. But JRVerifier tries to load classes to verify their type, and classpath is not yet set at this point. Is it a bug or did I misunderstood the code?"There is mention of a patch in that bug and I also found a related patch: http://jasperforge.org/tracker/index.php?func=detail&aid=246&group_id=102&atid=369&action=editI tried using the latest version of jasper reports: jasperreports-3.0.0-javaflow.jar, but this did not fix the problem. Is there a patch available to fix this?Regards,Richard Link to comment Share on other sites More sharing options...
richardc Posted August 11, 2008 Author Share Posted August 11, 2008 Here is the source: - class path is not included until after the method verifyDesign is callledClass: net.sf.jasperreports.engine.design.JRAbstractCompilermethod: compileReport: public final JasperReport compileReport(JasperDesign jasperDesign) throws JRException { // check if the language is supported by the compiler checkLanguage(jasperDesign.getLanguage()); // collect all report expressions JRExpressionCollector expressionCollector = JRExpressionCollector.collector(jasperDesign); // verify the report design verifyDesign(jasperDesign, expressionCollector); String nameSuffix = createNameSuffix(); // check if saving source files is required boolean isKeepJavaFile = JRProperties.getBooleanProperty(JRProperties.COMPILER_KEEP_JAVA_FILE); File tempDirFile = null; if (isKeepJavaFile || needsSourceFiles) { String tempDirStr = JRProperties.getProperty(JRProperties.COMPILER_TEMP_DIR); tempDirFile = new File(tempDirStr); if (!tempDirFile.exists() || !tempDirFile.isDirectory()) { throw new JRException("Temporary directory not found : " + tempDirStr); } } List datasets = jasperDesign.getDatasetsList(); List crosstabs = jasperDesign.getCrosstabs(); JRCompilationUnit[] units = new JRCompilationUnit[datasets.size() + crosstabs.size() + 1]; // generating source code for the main report dataset units[0] = createCompileUnit(jasperDesign, jasperDesign.getMainDesignDataset(), expressionCollector, tempDirFile, nameSuffix); int sourcesCount = 1; for (Iterator it = datasets.iterator(); it.hasNext(); ++sourcesCount) { JRDesignDataset dataset = (JRDesignDataset) it.next(); // generating source code for a sub dataset units[sourcesCount] = createCompileUnit(jasperDesign, dataset, expressionCollector, tempDirFile, nameSuffix); } for (Iterator it = crosstabs.iterator(); it.hasNext(); ++sourcesCount) { JRDesignCrosstab crosstab = (JRDesignCrosstab) it.next(); // generating source code for a sub dataset units[sourcesCount] = createCompileUnit(jasperDesign, crosstab, expressionCollector, tempDirFile, nameSuffix); } String classpath = JRProperties.getProperty(JRProperties.COMPILER_CLASSPATH); try { // compiling generated sources String compileErrors = compileUnits(units, classpath, tempDirFile); if (compileErrors != null) { throw new JRException("Errors were encountered when compiling report expressions class file:n" + compileErrors); } // creating the report compile data JRReportCompileData reportCompileData = new JRReportCompileData(); reportCompileData.setMainDatasetCompileData(units[0].getCompileData()); for (ListIterator it = datasets.listIterator(); it.hasNext();) { JRDesignDataset dataset = (JRDesignDataset) it.next(); reportCompileData.setDatasetCompileData(dataset, units[it.nextIndex()].getCompileData()); } for (ListIterator it = crosstabs.listIterator(); it.hasNext();) { JRDesignCrosstab crosstab = (JRDesignCrosstab) it.next(); Integer crosstabId = expressionCollector.getCrosstabId(crosstab); reportCompileData.setCrosstabCompileData(crosstabId.intValue(), units[datasets.size() + it.nextIndex()].getCompileData()); } // creating the report JasperReport jasperReport = new JasperReport( jasperDesign, getCompilerClass(), reportCompileData, expressionCollector, nameSuffix ); return jasperReport; } catch (JRException e) { throw e; } catch (Exception e) { throw new JRException("Error compiling report design.", e); } finally { if (needsSourceFiles && !isKeepJavaFile) { deleteSourceFiles(units); } } } Link to comment Share on other sites More sharing options...
lucianc Posted August 12, 2008 Share Posted August 12, 2008 This is a known problem (see this bug). The COMPILER_CLASSPATH property is only used by javac-based report compilers; the report verifier and the JDT report compiler resolve classes using the context classloader.Hence a workaround for this problem is to make sure that all the classes a report uses/depends on are resolvable by the context class loader. If you need to include arbitrary jars/class folders in a report's classpath, you can instantiate a classloader yourself: ClassLoader originalCL = Thread.currentThread().getContextClassLoader(); URL[] classpath = new URL[]{ new File("classes.jar").toURI().toURL(), new File("folder/classes").toURI().toURL(),}; URLClassLoader loader = new URLClassLoader(classpath, originalCL); try { Thread.currentThread().setContextClassLoader(loader); //compile report } finally { Thread.currentThread().setContextClassLoader(originalCL); } Regards,Lucian Link to comment Share on other sites More sharing options...
richardc Posted August 13, 2008 Author Share Posted August 13, 2008 Thanks Lucian!The custom classloader works fine.Cheers,Richard 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