0

I am using a Java application that allows you to add extensions written in Java. I wish to run a JasperReports viewer within an extension. As a test I have this code:

package com.moneydance.modules.features.jasperreports;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JFrame;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.swing.JRViewer;

public class MyJasperReport extends JFrame{
    JasperReport report;
    public MyJasperReport(Main main)  throws JRException, IOException{
      String sourceFileName = "c://users/miker/workspace/JasperReports-6.7.0/test" + 
         "/jasper_report_template.jasper";
    Map<String,Object> parameters = new HashMap<>();
   JasperReport report =(JasperReport)JRLoader.loadObjectFromFile(sourceFileName);
   JRDataSource dataSource = new JREmptyDataSource();
   JasperPrint jasperPrint = null;
   JRViewer viewer = null;
   try {
       jasperPrint = JasperFillManager.fillReport(report, parameters,dataSource);
   }
   catch (JRException e) {
       e.printStackTrace();
   }
   if (jasperPrint != null) {
        viewer = new JRViewer(jasperPrint);
        if (viewer !=null) {
            getContentPane().add(viewer);
        }
    }
}

}

This fails with a null pointer exception on the line:

        btnSave.setIcon(new javax.swing.ImageIcon(getClass().getResource("/net/sf/jasperreports/view/images/save.GIF")));

within the class JRViewerToolBar

If I run this in debug through Eclipse it works. If I include the JRViewer and JRViewerToolBar classes in my code and change the code to use the following snippet:

    public Image getIcon(String action) {
    try {
        loader = getClass().getClassLoader();
        java.io.InputStream in = 
                loader.getResourceAsStream(action);
        if (in != null) {
            ByteArrayOutputStream bout = new ByteArrayOutputStream(1000);
            byte buf[] = new byte[256];
            int n = 0;
            while((n=in.read(buf, 0, buf.length))>=0)
                bout.write(buf, 0, n);
            return Toolkit.getDefaultToolkit().createImage(bout.toByteArray());
        }
    } catch (Throwable e) { }
    return null;
}

where action is "/net/sf/jasperreports/view/images/save.GIF" it also works.

It is obviously a problem with determining the resource path. I suspect the app I am using uses its own class loader to load my extension.

The question is: Is there a way of loading the JasperReports classes so they behave themselves? Alternatively all I can see is having a modified version of JasperReports in my extension, which is going to introduce bugs and be a maintenance nightmare.

EDIT

This is not a duplicate of the File Resolver question. This is about the class loader used to load the Jasper classes from within an application.

  • Your ability to force a classloader or resource loading problem will mostly depend on the application you are extending. You'll have to give us some details about that to continue, because as you've pointed out everything you've done in the extension code so far is fine. – PaulProgrammer Oct 11 '18 at 15:50
  • I have found that the app (Moneydance) overrides getResourceAsStream. If I create a custom class loader, override GetResource and use it to load JasperReports will the jasper code use my class or the default class? –  Oct 11 '18 at 17:21
  • This is connected to a previous question I posted. I should have made the connection. PaulProgrammer I would be interested in your answer about class loaders. –  Oct 11 '18 at 17:32
  • You probably will struggle injecting a custom classloader into the application as a plugin. I would spend some time with the application documentation to understand what it's plugin classloader (and the `getResourceAsStream()` method) does differently and how you can use that within the context of your problem. – PaulProgrammer Oct 11 '18 at 17:44
  • I don't seem to be able to add an answer to this. It has been wrongly declared as a duplicate. The issue is the CLASS-PATH of Moneydance (the app being extended). The path contains the jars of Moneydance and the extension itself because the ClassLoader has been extended. The method getResourceAsStream has been overwritten but he method getResource has not so getResource does not find the Jasper resources. The solution I have come up with is to load an executable jar (using Spring Boot) containing the JasperReports code and build an interface into that jar. –  Oct 14 '18 at 09:22
  • BTW, you can use *JRParameter.REPORT_CLASS_LOADER* parameter for passing classloader. – Alex K Oct 15 '18 at 18:17

1 Answers1

0

The issue is the CLASS-PATH of Moneydance (the app being extended). The path contains the jars of Moneydance and the extension itself because the ClassLoader has been extended. The method getResourceAsStream has been overwritten but he method getResource has not so getResource does not find the Jasper resources. The solution I have come up with is to load an executable jar (using Spring Boot) containing the JasperReports code and build an interface into that jar.

I am using as custom class loader to load the background task that contains the Jasper classes. I can intercept the getResources method and find the resource in the jar.