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

JRProperties.COMPILER_CLASSPATH Bug


richardc

Recommended Posts

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.ReportScriptletUtils
at 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=18750

In 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=edit

I 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

  • Replies 3
  • Created
  • Last Reply

Top Posters In This Topic

Here is the source: - class path is not included until after the method verifyDesign is callled

Class: net.sf.jasperreports.engine.design.JRAbstractCompiler

method: 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

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

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...