Why does Tomcat use reflection for Catalina instancilization - java

Reading the Tomcat 7 source code, I just wonder why Tomcat instance Catalina and invokes the related methods by using reflection instead of simply using new to create the object and calling the method directly?

The answer is in the javadoc comment of the Bootstrap class:
/**
* Bootstrap loader for Catalina. This application constructs a class loader
* for use in loading the Catalina internal classes (by accumulating all of the
* JAR files found in the "server" directory under "catalina.home"), and
* starts the regular execution of the container. The purpose of this
* roundabout approach is to keep the Catalina internal classes (and any
* other classes they depend on, such as an XML parser) out of the system
* class path and therefore not visible to application level classes.
*
* #author Craig R. McClanahan
* #author Remy Maucherat
* #version $Id: Bootstrap.java 1142323 2011-07-02 21:57:12Z markt $
*/
public final class Bootstrap { ... }

Related

What are the possible values of spring.datasource.initialization-mode?

I am configuring a database in Spring JPA and I want to know what the possible values are of spring.datasource.initialization-mode. I found this page with common properties but it doesn't give all possible values. I'd expect there to be some documentation on all possible values of all properties you can set.
I am using the property in the props section in my applicationContext.xml as properties for the entityManagerFactory
<util:properties id="props">
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL82Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.ddl-auto">create</prop>
<prop key="spring.jpa.show-sql">true</prop>
<prop key="spring.jpa.generate.ddl">true</prop>
<prop key="spring.jpa.hibernate.ddl-auto">create</prop>
<prop key="spring.datasource.initialization-mode">always</prop>
<prop key="spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation">true</prop>
</util:properties>
When all else fails, you remember "use the source, Luke!". The values are given in the Javadoc of the enum DataSourceInitializationMode. Values are always, embedded and never.
Forgive me for butting in almost a year late. After having faced a similar problem as explained by Christine, I decided to take the clue and begin searching in the source. It would appear that the following is detailed in the link here https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/jdbc/DataSourceInitializationMode.html :
Enum Constant Summary Enum Constants
Enum Constant and Description
ALWAYS Always initialize the datasource.
EMBEDDED Only initialize an embedded datasource.
NEVER Do not initialize the datasource.
Spring Behaviour varies w.r.t to the Spring Version
From 2.7 Version of Spring
Spring Creates Table using schema.sql and data.sql in classpath for Embedded i.e memory database
spring.datasource.url=jdbc:h2:mem:somedbofyours
I have tried the following configuration
Create file based H2
spring.datasource.url=jdbc:h2:file:~/db/eesandb
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=none
Whatever be the above combinations, it didnt create or execute schema.sql in 2.7.4
The Following Property spring.sql.init.mode determines creation of DDL,DML by referring schema.sql and data.sql in classpath
Example spring.sql.init.mode = always (or)
spring.datasource.initialization-mode=always.
Visit Similar Post Why Spring Boot 2.0 application does not run schema.sql?
Simply put, there're 3 options: always, embedded and never.
Note that: the property spring.datasource.initialization-mode is outdated.
You may want to consider spring.sql.init.mode instead for new projects.
source for the old one
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.jdbc;
/**
* Supported {#link javax.sql.DataSource} initialization modes.
*
* #author Vedran Pavic
* #author Stephane Nicoll
* #since 2.0.0
* #see AbstractDataSourceInitializer
*/
public enum DataSourceInitializationMode {
/**
* Always initialize the datasource.
*/
ALWAYS,
/**
* Only initialize an embedded datasource.
*/
EMBEDDED,
/**
* Do not initialize the datasource.
*/
NEVER
}
source for the new
/*
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.sql.init;
/**
* Supported database initialization modes.
*
* #author Andy Wilkinson
* #since 2.5.1
* #see AbstractScriptDatabaseInitializer
*/
public enum DatabaseInitializationMode {
/**
* Always initialize the database.
*/
ALWAYS,
/**
* Only initialize an embedded database.
*/
EMBEDDED,
/**
* Never initialize the database.
*/
NEVER
}

How to create a shortcut to a unit test from a comment?

I would like to add a shortcut to a specific unit test from my class level comment. This way a developer can quickly navigate to a test within their IDE.
Using IntelliJ, I added the following to the class comment for "MyClass":
/**
* #see com.my.address.AnotherClass
* #see com.my.address.MyClassTests
* {#link com.my.address.MyClassTests#nameOfMyTest NameOfMyTest}
*/
IntelliJ presents this error for the two shortcuts that reference the tests:
Cannot resolve symbol 'com.my.address.MyClassTests' less... (⌘F1)
This inspection points out unresolved references inside javadoc
Is it possible to present a shortcut to a unit test from comment? If so, how?
--- additional info (Jan.28/2015) ---
This is my module setting in IntelliJ for this project:
Assuming they are in the same package you don't need the package name, but you do need a # between the class name and the method name:
{#link MyClassTests#NameOfMyTest()}

#WebServlet annotation seems to interfere with javadoc. Is there an easy workaround?

I've just started using the javadoc tool to create documentation for a package containing a servlet class. It's working as expected for every class except the servlet, where the description doesn't appear.
After trial and error I narrowed down the cause to the #WebServlet annotation I use to declare the servlet for Tomcat. I'd rather not remove this annotation if it can be avoided. Is there an easy workaround that I could use?
This is the relevant section of my code:
import javax.servlet.annotation.WebServlet;
#WebServlet("/MyServlet")
/**
* MyServlet description
*
* #author ViscountRandom
* #since 2014-08-26
*/
public class MyServlet extends HttpServlet {
...
The resulting javadoc page can be seen here (note the description is missing and the #WebServlet annotation appears above the class name).
Thanks in advance for any help you can offer.
EDIT: I have tried repositioning the annotation in my code but that had no effect.
For me, it seems to be working, see
/**
* My javadoc
* <br>25/08/2014
*/
#WebServlet
public class MyServlet {
}
generates
while
#WebServlet
/**
* My javadoc
* <br>25/08/2014
*/
public class MyServlet {
}
generates
I am using Java 7.
If you just want your javadoc to go right above the method declaration in the source code, you can still try
/**
* My javadoc
* <br>25/08/2014
*/
#WebServlet public class MyServlet {
}
which will generate

Is Sitemesh 2.4 release broken?

We've recently upgraded one of our projects. This involves new versions of JARs also.
Sitemesh was one of them. We updated from 2.2.1 to 2.4.2. Things stopped working.
We had a custom filter extend Sitemesh's PageFilter which now does not work because in v2.4 PageFilter extends SiteMeshFilter which does not expose the same methods (the ones we were overriding).
OK, no biggy, we'll just change our code to match, but then I saw this in the source code I downloaded from http://java.net/downloads/sitemesh/
/**
* Core Filter for integrating SiteMesh into a Java web application.
*
* #author Joe Walnes
* #author Scott Farquhar
* #since SiteMesh 3
*/
public class SiteMeshFilter implements Filter {
private FilterConfig filterConfig;
private ContainerTweaks containerTweaks;
private static final String ALREADY_APPLIED_KEY = "com.opensymphony.sitemesh.APPLIED_ONCE";
............
#since SiteMesh 3? This is v2.4.2. What 3?
Is the release corrupt or what? Am I missing something?
I'm using sitemesh 2.4.2 in one project and it works fine.
You can see that that change (which mentions Sitemesh 3) was done back in 2005 when they refactored the architecture to be compatible with sitemesh3. Here's the commit in github.
I remember getting a similar impression when I was browsing the javadocs a few months ago :).
So the answer is: The jar is not corrupt, it's just the result of a crooked merge.

How to load JAR files dynamically at Runtime?

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?
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.
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);
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.
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.
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");
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();
}
}
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());
}
}
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.
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);
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");
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
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.
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.
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.
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.
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.
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.
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());
}
}
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
I personally find that java.util.ServiceLoader does the job pretty well. You can get an example here.

Categories

Resources