简体   繁体   中英

loading jar at runtime and using its class

i have one problem in which i need some help.

Problem statement: I'm using one jar to generate reports in excel sheet format. This jar is required only if the user wants to generate report in excel format. Other formats of report available are html and txt which don't require this jar.

The current user generates the reports in html format so he says, why should I download this jar and export it in the classpath when I don't need report in the excel format.

Now the problem is if this jar is removed, this build will fail/as all the imports to the classes which are being used will give error. Class.forName can load the class at run-time and doesn't give me error but with this I will not be able to use the method of that class since I cannot have the reference of the class.

Is there any way out or this is not possible?

The problem is that you are hard wiring your dependencies. So your code needs to do some import s for the third party libs. What you need is to loosely couple the third party libs so that the core of you application does not need to import anything related to 3rd party libs. Use an interface which defines a method or the set of methods needed to generate reports in any format. Make this interface part of your core application. Format specific implementation goes then in separate modules which are dependent on your core application and on the 3rd party libs. Use a factory in the core application to load the specific implementation at runtime using refelction. If a format is requested from which the relevant module jars are not present in the classpath, a ClassNotFoundException will be thrown, catch it and handle accordingly.

Here a sample structure for your application

Core application

class ReportData {

}   

interface ReportGenerator {
    byte[] generate(ReportData data);
}


class ReportGeneratorFactory {
    public ReportGenerator getInstance(String format) 
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        ReportGenerator reportGenerator = null;
        if("txt".equals(format)) {
            reportGenerator = (ReportGenerator) 
                    Class.forName("com.foo.TxtReportGenerator").newInstance();
        } else if("html".equals(format)) {
            reportGenerator = (ReportGenerator) 
                    Class.forName("com.foo.HtmlReportGenerator").newInstance();
        } else if("xl".equals(format)) {
            reportGenerator = (ReportGenerator) 
                    Class.forName("com.foo.XlReportGenerator").newInstance();
        } else {
            throw new UnsupportedOperationException(
                 String.format("Unsupport format %s", format));
        }
        return reportGenerator;
    }
}

Txt / Html Export (Could be part of the core application if no 3rd party lib are needed)

class TxtReportGenerator implements ReportGenerator {
    public byte[] generate(ReportData data) {
        // TODO Auto-generated method stub
        return null;
    }
}

class HtmlReportGenerator implements ReportGenerator {
    public byte[] generate(ReportData data) {
        // TODO Auto-generated method stub
        return null;
    }
}

Module (own jar) for XL report (depends on your core application and on the 3rd party lib)

class XlReportGenerator implements ReportGenerator {
    public byte[] generate(ReportData data) {
        // TODO Auto-generated method stub
        return null;
    }
}

Usage:

public static void main(String[] args) 
        throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    byte[] report = new ReportGeneratorFactory()
        .getInstance("xl")
        .generate(new ReportData());
}

Did you try to compile it with the jar as a dependency for the compile.

Then at runtime, you will have a part where you check if the jar is needed and if so you can dynamically get the jar and load it like so (Code does not work like this of course ;) ):

import java.lang.reflect.Method;
import java.net.URLClassLoader;

Method addURL = null;
try {
    addURL = URLClassLoader.class.getDeclaredMethod("addURL",
            new Class[]{URL.class});
} catch (Exception e1) {
    //Log error
}
addURL.setAccessible(true);
//Maybe download the file or check if file exist else give out error and end processing
File yourJar = new File(filePath+"/"+fileName+".jar");
//Replace Your.Main.Class with your main class
addURL.invoke(Your.Main.Class.class
                        .getClassLoader(), yourJar.toURI().toURL());
// Your class should now be loaded and no more ClassNotFound exception should occur when it is accessed, but not if it is accessed before!

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