How to load JAR files dynamically at Runtime?
Asked Answered
A

21

383

Why is it so hard to do this in Java? If you want to have any kind of module system you need to be able to load JAR files dynamically. I'm told there's a way of doing it by writing your own ClassLoader, but that's a lot of work for something that should (in my mind at least) be as easy as calling a method with a JAR file as its argument.

Any suggestions for simple code that does this?

Anguish answered 13/9, 2008 at 18:48 Comment(3)
I want to do the same but run the loaded jar in a more sandboxed environment (for security reasons obviously). For example, I want to block all network and filesystem access.Moguel
@francogrex if it is so simple, then why did you leave that comment instead of answering the question?Newmint
@TylerMarshall Allain Lalonde below showed how and it isn't hard, also on other systems it is rather simple. I didn't need to duplicate the answers it's a bad attitude to do that, one good is enough, but I wanted to highlight to the OP that what he qualifies as "hard" is not and his statement is false.Enroot
L
312

The reason it's hard is security. Classloaders are meant to be immutable; you shouldn't be able to willy-nilly add classes to it at runtime. I'm actually very surprised that works with the system classloader. Here's how you do it making your own child classloader:

URLClassLoader child = new URLClassLoader(
        new URL[] {myJar.toURI().toURL()},
        this.getClass().getClassLoader()
);
Class classToLoad = Class.forName("com.MyClass", true, child);
Method method = classToLoad.getDeclaredMethod("myMethod");
Object instance = classToLoad.newInstance();
Object result = method.invoke(instance);

Painful, but there it is.

Linville answered 13/9, 2008 at 19:4 Comment(11)
Only problem with this approach is that you need to know what classes are in what jars. As opposed to just loading a directory of jars and then instantiating classes. I am misunderstanding it?Anguish
This method works great when running in my IDE, but when I build my JAR I get a ClassNotFoundException when calling Class.forName().Harless
Using this approach you need to make sure you won't call this load method more than once for each class. Since you're creating a new class loader for every load operation, it can not know whether the class was already loaded previously. This can have bad consequences. For example singletons not working because the class was loaded several times and so the static fields exist several times.Orthography
Works. Even with dependencies to other classes inside the jar. The first line was incomplete. I used URLClassLoader child = new URLClassLoader (new URL[] {new URL("file://./my.jar")}, Main.class.getClassLoader()); assuming that the jar file is called my.jar and is located in the same directory.Nazler
What is the class type of the myJar variable? Would be useful to have the instantiation of that variable added to the example so that the sample is entirely standalone.Seagirt
Don't forget to URL url = file.toURI().toURL();Lesley
@Allain and other people, you can load a directory of jars like this: <code>ClassLoader loader = URLClassLoader.newInstance( new URL[] { yourURL }, getClass().getClassLoader() );</code>Lesley
Maybe it's a security problem. However, if a hacker already has access to the system, that one leak is the least of the problem.Pulsatory
I've two jars (jar1 and jar2). I wish to load the class Abc that resides in the default package of jar1 and jar2. With this approach the class Abc from the jar1 is always loaded, so in my case this snippet code is not workingUlphiah
If we use Java SPI probably we even don't need to know about the class name as well, SPI can scan and load the implemented classes.Kalpa
This solution didn't work for me but this did https://mcmap.net/q/88146/-how-to-load-classes-at-runtime-from-a-folder-or-jarFrayda
A
151

The following solution is hackish, as it uses reflection to bypass encapsulation, but it works flawlessly:

File file = ...
URL url = file.toURI().toURL();

URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoader, url);
Anguish answered 13/9, 2008 at 18:50 Comment(12)
All the activity on this response makes me wonder how much hacks we are running in production in different systems. I'm not sure I want to know the answerTeresita
Doesn't work so well if the system class loader happens to be something other than a URLClassLoader...Pair
Does this also work when replacing a jar file (which is in the classpath already) ?Shotton
I'm afraid I don't know how it behaves when replacing jars. I imagine there's some caching going on keeping it from hitting the disk every time. Try it and let us know.Anguish
Java 9+ warns that URLClassLoader.class.getDeclaredMethod("addURL", URL.class) is an illegal use of reflection, and will fail in the future.Definition
Any idea how to update this code to work with Java 9+?Necrose
@Necrose Yes!!Wayzgoose
This is the only solution that actually worked for me. I am using the externally loaded JAR in an ExecutorService tasks and it was mysteriously hanging with all other approaches..Scythe
lol the use of the phrase, 'works flawlessly' in this answer is quite amusing...considering not only is it abusive to the API and violates security, but as of Java9 the systemClassLoader is no longer a URLClassloader, as it was never promised it would be via the API.Pehlevi
lol. I'm certainly not going back and editing and answer I gave almost 14 years ago.Anguish
With openjdk 11 this approach fails with exception java.lang.IllegalArgumentException: object is not an instance of declaring class at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566)Unconscionable
Wild Hyrum's law spotted!Savanna
W
52

While most solutions listed here are either hacks (pre JDK 9) hard to configure (agents) or just don't work anymore (post JDK 9) I find it really surprising that nobody mentioned a clearly documented method.

You can create a custom system class loader and then you're free to do whatever you wish. No reflection required and all classes share the same classloader.

When starting the JVM add this flag:

java -Djava.system.class.loader=com.example.MyCustomClassLoader

The classloader must have a constructor accepting a classloader, which must be set as its parent. The constructor will be called on JVM startup and the real system classloader will be passed, the main class will be loaded by the custom loader.

To add jars just call ClassLoader.getSystemClassLoader() and cast it to your class.

Check out this implementation for a carefully crafted classloader. Please note, you can change the add() method to public.

Wayzgoose answered 15/1, 2020 at 1:14 Comment(9)
Thank you - this is really helpful! All other references on web use methods for JDK 8 or before - which has multiple problems.Arthur
-Djava.system.class.loader=com.example.MyCustomClassLoader seems not reflected for the compile time dependency jars in the application jar manifest. Please see: #68381468Eudoxia
After startup with the Custom Classloader, can the jar files be loaded exactly the same as in Allain answer? Still get "jdk.internal.loader.ClassLoaders$AppClassLoader is in module java.base of loader 'bootstrap'; org.myapp.CustomClassLoader is in unnamed module of loader 'app'"Tysontyumen
@Tysontyumen I'm not sure what you might be doing wrong, but you should be able to access your classloader just like with Allain's answerWayzgoose
I tried it like this: DynamicClassLoader classLoader = (DynamicClassLoader)ClassLoader.getSystemClassLoader(); Method method = DynamicClassLoader.class.getDeclaredMethod("addURL", URL.class); method.setAccessible(true); method.invoke(classLoader, url);Tysontyumen
btw I also checked the update4j code and there they do it like this ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); DynamicClassLoader dynamic = DynamicClassLoader.findAncestor(contextClassLoader); dynamic.add(url); But this seems like another use case.Tysontyumen
Ah, no. Just modify your class loader to expose the protected addUrl and you don't need reflection. It's actually my last sentenceWayzgoose
Maybe I am missing something or is it specific to Spring Boot. Well I created a separate question for it "#70067451"Tysontyumen
As mentioned in the answer, the original system class loader becomes the parent loader of the custom loader, which means, classes loaded through the original class loader don’t see classes defined by the custom loader, which is important, as the class path is still handled through the original loader, i.e. getURLs() of the custom loader does not return the class path, and when the custom loader follows the standard delegation model, the request for the main class will be forwarded to the old standard loader which will load it the same way as without the custom loader.Liquate
M
51

You should take a look at OSGi, e.g. implemented in the Eclipse Platform. It does exactly that. You can install, uninstall, start and stop so called bundles, which are effectively JAR files. But it does a little more, as it offers e.g. services that can be dynamically discovered in JAR files at runtime.

Or see the specification for the Java Module System.

Maxantia answered 13/9, 2008 at 18:55 Comment(0)
E
44

How about the JCL class loader framework? I have to admit, I haven't used it, but it looks promising.

Usage example:

JarClassLoader jcl = new JarClassLoader();
jcl.add("myjar.jar"); // Load jar file  
jcl.add(new URL("http://myserver.com/myjar.jar")); // Load jar from a URL
jcl.add(new FileInputStream("myotherjar.jar")); // Load jar file from stream
jcl.add("myclassfolder/"); // Load class folder  
jcl.add("myjarlib/"); // Recursively load all jar files in the folder/sub-folder(s)

JclObjectFactory factory = JclObjectFactory.getInstance();
// Create object of loaded class  
Object obj = factory.create(jcl, "mypackage.MyClass");
Eugine answered 20/9, 2009 at 11:35 Comment(5)
It's also buggy and missing some important implementations i.e. findResources(...). Be ready to spend wonderful nights investigating why certain things don't work =)Inhume
I'm still wondering @SergeyKarpushin's claims are still present since the project has been updated over time to second major version. Would like to hear experience.Glasswort
@ErdinEray, it is a very good question that I ask myself too since we were "forced" to switch to OpenJDK. I still work on java projects, and I don't have any evidences that Open JDK will fail you these days (I had issue back then though). I guess I withdraw my claim until I bump into something else.Inhume
@ErayErdin Seems like it does now.Prelacy
Not working with java 11Enclave
A
19

Here is a version that is not deprecated. I modified the original to remove the deprecated functionality.

/**************************************************************************************************
 * Copyright (c) 2004, Federal University of So Carlos                                           *
 *                                                                                                *
 * All rights reserved.                                                                           *
 *                                                                                                *
 * Redistribution and use in source and binary forms, with or without modification, are permitted *
 * provided that the following conditions are met:                                                *
 *                                                                                                *
 *     * Redistributions of source code must retain the above copyright notice, this list of      *
 *       conditions and the following disclaimer.                                                 *
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of   *
 *     * conditions and the following disclaimer in the documentation and/or other materials      *
 *     * provided with the distribution.                                                          *
 *     * Neither the name of the Federal University of So Carlos nor the names of its            *
 *     * contributors may be used to endorse or promote products derived from this software       *
 *     * without specific prior written permission.                                               *
 *                                                                                                *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS                            *
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT                              *
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR                          *
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR                  *
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,                          *
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,                            *
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR                             *
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF                         *
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING                           *
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS                             *
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                   *
 **************************************************************************************************/
/*
 * Created on Oct 6, 2004
 */
package tools;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * Useful class for dynamically changing the classpath, adding classes during runtime. 
 */
public class ClasspathHacker {
    /**
     * Parameters of the method to add an URL to the System classes. 
     */
    private static final Class<?>[] parameters = new Class[]{URL.class};

    /**
     * Adds a file to the classpath.
     * @param s a String pointing to the file
     * @throws IOException
     */
    public static void addFile(String s) throws IOException {
        File f = new File(s);
        addFile(f);
    }

    /**
     * Adds a file to the classpath
     * @param f the file to be added
     * @throws IOException
     */
    public static void addFile(File f) throws IOException {
        addURL(f.toURI().toURL());
    }

    /**
     * Adds the content pointed by the URL to the classpath.
     * @param u the URL pointing to the content to be added
     * @throws IOException
     */
    public static void addURL(URL u) throws IOException {
        URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
        Class<?> sysclass = URLClassLoader.class;
        try {
            Method method = sysclass.getDeclaredMethod("addURL",parameters);
            method.setAccessible(true);
            method.invoke(sysloader,new Object[]{ u }); 
        } catch (Throwable t) {
            t.printStackTrace();
            throw new IOException("Error, could not add URL to system classloader");
        }        
    }

    public static void main(String args[]) throws IOException, SecurityException, ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
        addFile("C:\\dynamicloading.jar");
        Constructor<?> cs = ClassLoader.getSystemClassLoader().loadClass("test.DymamicLoadingTest").getConstructor(String.class);
        DymamicLoadingTest instance = (DymamicLoadingTest)cs.newInstance();
        instance.test();
    }
}
Amberambergris answered 7/4, 2010 at 15:33 Comment(3)
I hate to bump an old thread, but I would like to point out that all content on stackoverflow is CC licensed. Your copyright statement is effectively ineffective. stackoverflow.com/faq#editingBawdy
Um. Technically, original content is CC licensed, but if you post copyrighted content here, it doesn't remove the fact that the content is copyrighted. If I post a picture of Mickey Mouse, it doesn't make it CC-licensed. So I'm adding the copyright statement back.Print
addURL() will not work in Java 9 as ClassLoader.getSystemClassLoader() is not URLClassLoader since Java 9.Halden
G
18

With Java 9, the answers with URLClassLoader now give an error like:

java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader

This is because the class loaders used have changed. Instead, to add to the system class loader, you can use the Instrumentation API through an agent.

Create an agent class:

package ClassPathAgent;

import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.jar.JarFile;

public class ClassPathAgent {
    public static void agentmain(String args, Instrumentation instrumentation) throws IOException {
        instrumentation.appendToSystemClassLoaderSearch(new JarFile(args));
    }
}

Add META-INF/MANIFEST.MF and put it in a JAR file with the agent class:

Manifest-Version: 1.0
Agent-Class: ClassPathAgent.ClassPathAgent

Run the agent:

This uses the byte-buddy-agent library to add the agent to the running JVM:

import java.io.File;

import net.bytebuddy.agent.ByteBuddyAgent;

public class ClassPathUtil {
    private static File AGENT_JAR = new File("/path/to/agent.jar");

    public static void addJarToClassPath(File jarFile) {
        ByteBuddyAgent.attach(AGENT_JAR, String.valueOf(ProcessHandle.current().pid()), jarFile.getPath());
    }
}
Geisel answered 27/9, 2017 at 21:28 Comment(0)
B
12

Here is a quick workaround for Allain's method to make it compatible with newer versions of Java:

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
try {
    Method method = classLoader.getClass().getDeclaredMethod("addURL", URL.class);
    method.setAccessible(true);
    method.invoke(classLoader, new File(jarPath).toURI().toURL());
} catch (NoSuchMethodException e) {
    Method method = classLoader.getClass()
            .getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
    method.setAccessible(true);
    method.invoke(classLoader, jarPath);
}

Note that it relies on knowledge of internal implementation of specific JVM, so it's not ideal and it's not a universal solution. But it's a quick and easy workaround if you know that you are going to use standard OpenJDK or Oracle JVM. It might also break at some point in future when new JVM version is released, so you need to keep that in mind.

Bartolommeo answered 2/11, 2018 at 1:16 Comment(3)
With Java 11.0.2, I get: Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make void jdk.internal.loader.ClassLoaders$AppClassLoader.appendToClassPathForInstrumentation(java.lang.String) accessible: module java.base does not "opens jdk.internal.loader" to unnamed module @18ef96Peristome
Works with Java 8 EE in application server environment.Interrupter
Add to VM options: -add-opens java.base/jdk.internal.loader=ALL-UNNAMED --add-opens jdk.zipfs/jdk.nio.zipfs=ALL-UNNAMEDFerland
S
9

The best I've found is org.apache.xbean.classloader.JarFileClassLoader which is part of the XBean project.

Here's a short method I've used in the past, to create a class loader from all the lib files in a specific directory

public void initialize(String libDir) throws Exception {
    File dependencyDirectory = new File(libDir);
    File[] files = dependencyDirectory.listFiles();
    ArrayList<URL> urls = new ArrayList<URL>();
    for (int i = 0; i < files.length; i++) {
        if (files[i].getName().endsWith(".jar")) {
        urls.add(files[i].toURL());
        //urls.add(files[i].toURI().toURL());
        }
    }
    classLoader = new JarFileClassLoader("Scheduler CL" + System.currentTimeMillis(), 
        urls.toArray(new URL[urls.size()]), 
        GFClassLoader.class.getClassLoader());
}

Then to use the classloader, just do:

classLoader.loadClass(name);
Shipload answered 15/9, 2008 at 15:22 Comment(1)
Note that the project does not appear to be very well maintained. Their roadmap for the future contains several releases for 2014, for example.Kovacev
I
6

If you are working on Android, the following code works:

String jarFile = "path/to/jarfile.jar";
DexClassLoader classLoader = new DexClassLoader(jarFile, "/data/data/" + context.getPackageName() + "/", null, getClass().getClassLoader());
Class<?> myClass = classLoader.loadClass("MyClass");
Incurable answered 15/8, 2013 at 12:57 Comment(0)
D
6

Another working solution using Instrumentation that works for me. It has the advantage of modifying the class loader search, avoiding problems on class visibility for dependent classes:

Create an Agent Class

For this example, it has to be on the same jar invoked by the command line:

package agent;

import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.jar.JarFile;

public class Agent {
   public static Instrumentation instrumentation;

   public static void premain(String args, Instrumentation instrumentation) {
      Agent.instrumentation = instrumentation;
   }

   public static void agentmain(String args, Instrumentation instrumentation) {
      Agent.instrumentation = instrumentation;
   }

   public static void appendJarFile(JarFile file) throws IOException {
      if (instrumentation != null) {
         instrumentation.appendToSystemClassLoaderSearch(file);
      }
   }
}

Modify the MANIFEST.MF

Adding the reference to the agent:

Launcher-Agent-Class: agent.Agent
Agent-Class: agent.Agent
Premain-Class: agent.Agent

I actually use Netbeans, so this post helps on how to change the manifest.mf

Running

The Launcher-Agent-Class is only supported on JDK 9+ and is responsible for loading the agent without explicitly defining it on the command line:

 java -jar <your jar>

The way that works on JDK 6+ is defining the -javaagent argument:

java -javaagent:<your jar> -jar <your jar>

Adding new Jar at Runtime

You can then add jar as necessary using the following command:

Agent.appendJarFile(new JarFile(<your file>));

I did not find any problems using this on documentation.

Diplostemonous answered 10/10, 2018 at 13:42 Comment(3)
For some reason when using this solution I get "Exception in thread "main" java.lang.ClassNotFoundException: agent.Agent". I packaged "Agent" class into my main "war" application, so I am sure it's thereExtol
A problem is java -cp <your jar> <MainClass>. Where is no instrumentation.Coffman
@czdepski: thanks for finding a solution that can be easily and cleanly integrated in a post-SE11 project. You saved my bacon! ;-)Burnsides
D
5

Another version of the hackish solution from Allain, that also works on JDK 11:

File file = ...
URL url = file.toURI().toURL();
URLClassLoader sysLoader = new URLClassLoader(new URL[0]);

Method sysMethod = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
sysMethod.setAccessible(true);
sysMethod.invoke(sysLoader, new Object[]{url});

On JDK 11 it gives some deprecation warnings but serves as a temporary solution those who use Allain solution on JDK 11.

Diplostemonous answered 2/10, 2018 at 21:50 Comment(2)
Can I also remove jar?Kitchen
This doesn't work for me with Java 11, saying java.lang.ClassNotFoundException: package01.WorkerImpl.Halden
A
5

I know I'm late to the party, but I have been using pf4j, which is a plug-in framework, and it works pretty well.

PF4J is a microframework and the aim is to keep the core simple but extensible.

An example of plugin usage:

Define an extension point in your application/plugin using ExtensionPoint interface marker:

public interface Greeting extends ExtensionPoint {

    String getGreeting();

}

Create an extension using @Extension annotation:

@Extension
public class WelcomeGreeting implements Greeting {

    public String getGreeting() {
        return "Welcome";
    }

}

Then you can load and unload the plugin as you wish:

public static void main(String[] args) {

    // create the plugin manager
    PluginManager pluginManager = new JarPluginManager(); // or "new ZipPluginManager() / new DefaultPluginManager()"

    // start and load all plugins of application
    pluginManager.loadPlugins();
    pluginManager.startPlugins();

    // retrieve all extensions for "Greeting" extension point
    List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
    for (Greeting greeting : greetings) {
        System.out.println(">>> " + greeting.getGreeting());
    }

    // stop and unload all plugins
    pluginManager.stopPlugins();
    pluginManager.unloadPlugins();

}

For further details please refer to the documentation

Atcliffe answered 25/7, 2020 at 23:36 Comment(0)
B
4

The solution proposed by jodonnell is good but should be a little bit enhanced. I used this post to develop my application with success.

Assign the current thread

Firstly we have to add

Thread.currentThread().setContextClassLoader(classLoader);

or you will not able to load resource (such as spring/context.xml) stored into the jar.

Do not include

your jars into the parent class loader or you will not able to understand who is loading what.

see also Problem reloading a jar using URLClassLoader

However, OSGi framework remain the best way.

Balneal answered 25/7, 2015 at 7:40 Comment(1)
Your answer seems a little confusing, and is perhaps more suited as a comment to the answer by jodonnell if it is merely a simple improvement.Kovacev
V
4

In case anyone searches for this in the future, this way works for me with OpenJDK 13.0.2.

I have many classes that I need to instantiate dynamically at runtime, each potentially with a different classpath.

In this code, I already have an object called pack, that holds some metadata about the class I am trying to load. The getObjectFile() method returns the location of the class file for the class. The getObjectRootPath() method returns the path to the bin/ directory containing the class files that include the class I am trying to instantiate. The getLibPath() method returns the path to a directory containing the jar files constituting the classpath for the module the class is a part of.

File object = new File(pack.getObjectFile()).getAbsoluteFile();
Object packObject;
try {
    URLClassLoader classloader;

    List<URL> classpath = new ArrayList<>();
    classpath.add(new File(pack.getObjectRootPath()).toURI().toURL());
    for (File jar : FileUtils.listFiles(new File(pack.getLibPath()), new String[] {"jar"}, true)) {
        classpath.add(jar.toURI().toURL());
    }
    classloader = new URLClassLoader(classpath.toArray(new URL[] {}));

    Class<?> clazz = classloader.loadClass(object.getName());
    packObject = clazz.getDeclaredConstructor().newInstance();

} catch (Exception e) {
    e.printStackTrace();
    throw e;
}
return packObject;

I was using the Maven dependency: org.xeustechnologies:jcl-core:2.8 to do this before, but after moving past JDK 1.8, it sometimes froze and never returned being stuck "waiting for references" at Reference::waitForReferencePendingList().

I am also keeping a map of class loaders so that they can be reused if the class I am trying to instantiate is in the same module as a class that I have already instantiated, which I would recommend.

Volsung answered 12/3, 2020 at 23:4 Comment(0)
E
3

please take a look at this project that i started: proxy-object lib

This lib will load jar from file system or any other location. It will dedicate a class loader for the jar to make sure there are no library conflicts. Users will be able to create any object from the loaded jar and call any method on it. This lib was designed to load jars compiled in Java 8 from the code base that supports Java 7.

To create an object:

    File libDir = new File("path/to/jar");

    ProxyCallerInterface caller = ObjectBuilder.builder()
            .setClassName("net.proxy.lib.test.LibClass")
            .setArtifact(DirArtifact.builder()
                    .withClazz(ObjectBuilderTest.class)
                    .withVersionInfo(newVersionInfo(libDir))
                    .build())
            .build();
    String version = caller.call("getLibVersion").asString();

ObjectBuilder supports factory methods, calling static functions, and call back interface implementations. i will be posting more examples on the readme page.

Endosperm answered 25/5, 2018 at 4:32 Comment(0)
G
3

This can be a late response, I can do it as this (a simple example for fastutil-8.2.2.jar) using jhplot.Web class from DataMelt (http://jwork.org/dmelt)

import jhplot.Web;
Web.load("http://central.maven.org/maven2/it/unimi/dsi/fastutil/8.2.2/fastutil-8.2.2.jar"); // now you can start using this library

According to the documentation, this file will be download inside "lib/user" and then dynamically loaded, so you can start immediately using classes from this jar file in the same program.

Gallinacean answered 21/10, 2018 at 0:20 Comment(0)
A
1

I needed to load a jar file at runtime for both java 8 and java 9+ (above comments don't work for both of these versions). Here is the method to do it (using Spring Boot 1.5.2 if it may relate).

public static synchronized void loadLibrary(java.io.File jar) {
    try {            
        java.net.URL url = jar.toURI().toURL();
        java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
        method.setAccessible(true); /*promote the method to public access*/
        method.invoke(Thread.currentThread().getContextClassLoader(), new Object[]{url});
    } catch (Exception ex) {
        throw new RuntimeException("Cannot load library from jar file '" + jar.getAbsolutePath() + "'. Reason: " + ex.getMessage());
    }
}
Avirulent answered 18/2, 2020 at 12:55 Comment(0)
N
1

For dynamic uploading of jar files, you can use my modification of URLClassLoader. This modification has no problem with changing the jar file during application operation, like the standard URLClassloader. All loaded jar files are loaded into RAM and thus independent of the original file.

In-memory jar and JDBC class loader

Nicaragua answered 8/7, 2020 at 18:55 Comment(1)
Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference. See here for instructions how to write better "link-based" answers.Dysgenic
C
0

I tried the ones posted by Jodonnell (edited by Konrad Rudolph) and it works on jdk 11 with some deprecation. Here's the example that resolves deprecation in JDK 11:

File jarFile = new File("/path/to/jar/file.jar");
        URLClassLoader child = new URLClassLoader(
                new URL[] {jarFile.toURI().toURL()},
                this.getClass().getClassLoader()
        );
        Class<?> classToLoad = Class.forName("com.MyClass", true, child);
        Method method = classToLoad.getDeclaredMethod("myMethod");
        Object instance = classToLoad.getDeclaredConstructor().newInstance();
        Object result = method.invoke(instance);
Cockshy answered 19/2 at 16:1 Comment(2)
don't copy answers, if you think you can improve them, edit them instead. https://mcmap.net/q/86931/-how-to-load-jar-files-dynamically-at-runtimeBluebell
Not able to edit them based on my account restrictions.Cockshy
I
-3

I personally find that java.util.ServiceLoader does the job pretty well. You can get an example here.

Islas answered 23/9, 2011 at 2:48 Comment(1)
ServiceLoader don't add jar files dynamically at runtime. jar files must be in classpath previously.Jonahjonas

© 2022 - 2024 — McMap. All rights reserved.