简体   繁体   中英

How to load a Jasper customizer class during runtime?

I need to render Jasper reports with charts and require individual ChartCustomizer classes for them. My application is running as a Java web-application.

Current state is, that the templates (.jasper files) are packaged with their required resources in a separate jar-file. These jar files themselves are stored as BLOBs in the Database. I load them with an own FileResolver, which I provide as a parameter to the Jasper Engine.

So far this works great for me, except I cannot load my Customizer classes. I tried to put them in another jar file and load them with an own ClassLoader and also provide that to the Jasper Engine:

URL customizersUrl = classLoader.findResource("customizers.jar");
if (customizersUrl != null) {
    URI jarUri = customizersUrl.toURI();

    JarFile jarFile = new JarFile(new File(jarUri));
    Enumeration e = jarFile.entries();

    URL[] jarContentUrls = {new URL("jar:file:" + jarUri.getPath() + "!/")};
    customizerClassLoader = URLClassLoader.newInstance(jarContentUrls);

    while (e.hasMoreElements()) {
        JarEntry je = (JarEntry) e.nextElement();
        if (je.isDirectory() || !je.getName().endsWith(".class")) {
            continue;
        }
        // -6 because of .class
        String className = je.getName().substring(0, je.getName().length() - 6);
        className = className.replace('/', '.');
        Class c = customizerClassLoader.loadClass(className);

    }
}
parameters.put(JRParameter.REPORT_CLASS_LOADER, customizerClassLoader);

but I am still getting a java.lang.ClassNotFoundException, although I can see in the Debugger, that the classloading from jar works.

Any help is appreciated!

Ok, I figured out that I need to put the class loader into the current thread's context. I am also using an anonymous class loader now, so that only requested classes get loaded (also improves debugging).

// check if customizer classes are present and load them
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
final URL customizersUrl = classLoader.findResource("customizers.jar");
if (customizersUrl != null) {

    ClassLoader cl = new ClassLoader() {

        @Override
        public Class loadClass(String className) throws ClassNotFoundException {
            try {
                return contextClassLoader.loadClass(className);
            } catch (ClassNotFoundException ex) {

                if (customizersUrl != null) {
                    try {
                        URI jarUri = customizersUrl.toURI();
                        URL[] jarContentUrls = {new URL("jar:file:" + jarUri.getPath() + "!/")};
                        URLClassLoader customizerInnerClassLoader = URLClassLoader.newInstance(jarContentUrls);
                        return customizerInnerClassLoader.loadClass(className);

                    } catch (URISyntaxException ex1) {
                        logger.debug("Exception during customizer class loading", ex1);
                    } catch (IOException ex1) {
                        logger.debug("Exception during customizer class loading", ex1);
                    } catch (ClassNotFoundException ex1) {
                        throw new ClassNotFoundException("Exception during customizer class loading", ex1);
                    }
                }
            }
            return null;
        }
    };

    // squeeze our own class loader in
    Thread.currentThread().setContextClassLoader(cl);

}
byte[] result = generate(jasperReport, parameters);
// changing the class loader back to its origin... just to be safe
Thread.currentThread().setContextClassLoader(contextClassLoader);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM