I want to load a library in separated classloader because don't want to add directly as dependency to do not conflict with other versions in the project.
I created a loader:
public LibLoader(String resourcePath) {
//resourcePath="/lib/Log4JHack-1.0.jar"
URL url = getClass().getResource(resourcePath);
loader = new URLClassLoader(new URL[] {url}, getClass().getClassLoader());
}
url = [file:/D:/..../lib/Log4JHack-1.0.jar]
if url is a file, then it works well.
url = [jar:file:/C:/..../Log4JHackLoader-1.0.jar!/lib/Log4JHack-1.0.jar]
if url is a jar:file (jar inside jar), then it don't work:
ERROR StatusLogger Unable to open jar:jar:file:/C:/Users/Dani/.m2/repository/hu/daniel/hari/log4jhack/Log4JHackLoader/1.0/Log4JHackLoader-1.0.jar!/lib/Log4JHack-1.0.jar!/META-INF/log4j-provider.properties java.net.MalformedURLException: no !/ in spec
at java.net.URL.<init>(URL.java:620)
at java.net.URL.<init>(URL.java:483)
at java.net.URL.<init>(URL.java:432)
at java.net.JarURLConnection.parseSpecs(JarURLConnection.java:175)
at java.net.JarURLConnection.<init>(JarURLConnection.java:158)
at sun.net.www.protocol.jar.JarURLConnection.<init>(JarURLConnection.java:81)
at sun.net.www.protocol.jar.Handler.openConnection(Handler.java:41)
at java.net.URL.openConnection(URL.java:972)
at java.net.URL.openStream(URL.java:1038)
at org.apache.logging.log4j.util.ProviderUtil.loadProvider(ProviderUtil.java:79)
at org.apache.logging.log4j.util.ProviderUtil.<init>(ProviderUtil.java:66)
at org.apache.logging.log4j.util.ProviderUtil.lazyInit(ProviderUtil.java:122)
at org.apache.logging.log4j.util.ProviderUtil.hasProviders(ProviderUtil.java:106)
at org.apache.logging.log4j.LogManager.<clinit>(LogManager.java:91)
at hu.daniel.hari.log4jpattern.logrenderer.log4j.log4j.capture.log4j2.StringLoggerCapture.<clinit>(StringLoggerCapture.java:34)
at hu.daniel.hari.log4jpattern.logrenderer.log4j.Log4j2Hack.doRender(Log4j2Hack.java:30)
at hu.daniel.hari.log4jpattern.logrenderer.log4j.Log4j2Hack.render(Log4j2Hack.java:23)
at hu.daniel.hari.log4jpattern.logrenderer.log4j.renderer.AbstractLog4jRendererAdapter.render(AbstractLog4jRendererAdapter.java:25)
at hu.daniel.hari.log4jpattern.logrenderer.service.LogRendererServiceImpl.getOutput(LogRendererServiceImpl.java:44)
at hu.daniel.hari.log4jpattern.logrenderer.service.LogRendererServiceImpl.render(LogRendererServiceImpl.java:37)
at hu.daniel.hari.log4jpattern.logrenderer.TestMain.main(TestMain.java:14)
Caused by: java.lang.NullPointerException: no !/ in spec
at sun.net.www.protocol.jar.Handler.parseAbsoluteSpec(Handler.java:171)
at sun.net.www.protocol.jar.Handler.parseURL(Handler.java:151)
at java.net.URL.<init>(URL.java:615)
... 20 more
Since I want to pack the loadable Log4JHack-1.0.jar
to Log4JHackLoader-1.0.jar, I need a solution for loading from inside jar.
It's not 100% clear to me what you're trying to do here. Why are you trying to include conflicting dependencies in your classpath?
In any case, this is a known limitation of UrlClassLoader. Have you considered extracting the nested jar as a temporary file on the file system, and then pointing your class loader to it?
The github.com/squark-io/nested-jar-classloader project is able to load classes in nested jar files, but has a lot of external dependencies. I added this same mechanism in my project (sourceforge.net/projects/mdiutilities/) but in this case without external dependencies.
These two projects do not work by making a temporary copy of the nested jar files, but directly load the classes bytes.
Related
Using the code:
ModuleFinder.of(Paths.get(path)).findAll()
I am able to retrieve a Set<ModuleReference> of all the .jars in the path folder.
My next step would be getting a Module from a ModuleReference but there's no method that returns that, I can get a ModuleDescriptor but even that one doesn't help. Is there a way to do this?
If you desire to access the module content you should open the ModuleReference that you've attained.
This would provide you access to the ModuleReader which
is intended for cases where access to the resources in a module is
required
A resource in a module is identified by an abstract name that is a
'/'-separated path string. For example, module java.base
may have a resource "java/lang/Object.class" that, by convention,
is the class file for java.lang.Object. A module reader may treat
directories in the module content as resources (whether it does or not is
module reader specific). Where the module content contains a directory
that can be located as a resource then its name ends with a slash ('/'). The directory can also be located with a name that drops the trailing slash.
Do keep in mind though, that the docs also specify :
A ModuleReader is open upon creation and is closed by invoking the
close
method. Failure to close a module reader may result in a resource
leak. The try-with-resources statement provides a useful construct to
ensure that module readers are closed.
One way to get the Module from the resources would be to access it using the Class#getModule as:
Module module = com.foo.bar.YourClass.class.getModule();
Edit: I've learned with time a better way to use the ModuleFinder to access a Module as suggested by #Alan as well could possibly be :
ModuleFinder finder = ModuleFinder.of(path);
ModuleLayer parent = ModuleLayer.boot();
Configuration configuration = parent.configuration().resolve(finder, ModuleFinder.of(), Set.of("curious")); // 'curious' being the name of the module
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
ModuleLayer layer = parent.defineModulesWithOneLoader(configuration, systemClassLoader);
Module m = layer.findModule("curious").orElse(null);
The classes in java.lang.module are more model world. Look at j.l.module.Configuration and also j.l.ModuleLayer to see how to create a configuration and instantiate it in the Java virtual machine as a layer of modules. There is a code fragment in the ModuleLayer javadoc that might get you going, just bewarned that this is an advanced topic and best to master the basics first.
I am trying to create Java plugins for an existing application. The plugins would like to re-use a lot of the already existing code-base in the main application (e.g. logging, error handling, etc).
I am trying to load plugins as .jar files like this:
String localPath = "...";
String pluginName = "...";
File jarFile = new File(localPath);
ClassLoader pluginLoader = URLClassLoader.newInstance(new URL[]{jarFile.toURL()});
pluginLoader.loadClass(pluginName).newInstance();
The problem I am having is that the classes I would like to import inside the plugin can not be found, even though they exist in the main app, I am getting errors like this:
NoClassDefFoundError: com/foo/exception/FooException
at com.foo.plugins.PluginManager.loadPlugin(PluginManager.java:187)
at com.foo.plugins.PluginManager.loadPlugins(PluginManager.java:86)
...
com/foo/exception/FooException is used everywhere in the code, but I didn't want to have to include this class (and many many others) in the plugin jar file. Instead I would like the ClassLoader to somehow be aware of the locally existing classes. Is this possible? If so, how can I do it?
You need to use your main application's classloader as a parent:
ClassLoader mainLoader = ThisClass.class.getClassLoader(); // some class in the main application
ClassLoader pluginLoader = URLClassLoader.newInstance(new URL[]{jarFile.toURL()}, mainLoader);
Then classes loaded by the plugin classloader will have access to classes loaded by the main classloader (as well as that classloader's parent, if it has one, and so on).
I'm trying to find out if we can load a oracle commerce component from file system. Generally we assemble all the code into an ear file and deploy it, however, I got a requirement where in I have to store some components in file system rather than packaging them along with ear file.
I know that we can use URLClassloader to load a class as shown below,
File classDir = new File("A:\\LodeeModule\\classes");
URL[] url = { classDir.toURI().toURL() };
ClassLoader loader = new URLClassLoader(url);
for (File file : classDir.listFiles()) {
String filename = file.getName().replace(".class", "");
loader.loadClass("com.buddha.testers." + filename).getConstructor().newInstance();
}
but how can we use the same for an component which has to be resolved by Nucleus at later point of time? Is there any way to instruct Nucleus to resolve component from file system?
You should just be able to add the JAR that contains the components classes to the CLASSPATH system variable used by the application server instance.
Then in the component configuration just define the implementing class as you normally would
$class=some.class.path.class
If you are using Jboss EAP 6+ on a newer version of ATG (11.0+) you might have some more trouble, you have to jump through some more hoops due to its classloader
https://docs.jboss.org/author/display/AS7/Class+Loading+in+AS7
Essentially you would need to define a jboss module containing your jar files, and define a dependency between the ear's "module" and the module containing your classes.
Alternatively you can define a ClassLoaderService that will manage the classes for your JARs
To do this, you need to define a new ClassLoaderService, so create a new properties file as you would with any other component.
/my/custom/ClassLoaderService.properties
$class=atg.nucleus.ServicesManifestClassLoaderService
$description=Custom Class Loader Service.
# The files to go into the classpath of the classloader
classpathFiles=\
/path/to/my/jars/lib/someClasses.jar,\
/path/to/my/jars/lib/someOtherClasses.jar
loggingDebug=false
Then in the actual component that you need these classes for add this line;
$classloader=/my/custom/ClassLoaderService
I think you're looking for the atg.dynamo.data-dir property. If you specify that property dynamo will look at that location for the "server configs" or properties files. This allows you to separate the configs from the ear file.
Note: You can still include configs in the ear, I believe they will still have first precedence
It's usually specified when you start the server, something like:
run.sh -c <your server> -Datg.dynamo.data-dir=/data/something/serverconfigs
This feature is largely undocumented, but many people know about it.
See http://docs.oracle.com/cd/E24152_01/Platform.10-1/ATGPlatformProgGuide/html/s0302developmentmodeandstandalonemode01.html
EDIT:
I mistook what you were originally asking. You might want to take a look at the disposable class loader that ATG provides, but keep in mind this is only intended for development purposes.
We have a project moving to use Storm and as such our code must be packaged in a jar. We had previously used com.sun.jndi.fscontext.RefFSContextFactory as our InitialContextFactory implementation to load the jndicontext bindings from a file in the system config directory in the classpath (worked fine). However when attempting to use this factory to load the context from within the jar, we get the following:
javax.naming.InvalidNameException: unknown protocol: jar
at com.sun.jndi.fscontext.FScontextFactory.getFileNameFromURLSTring(FSContextFactory.java:139)
at com.sun.jndi.fscontext.RefFSContextFactory.createContext(RefFSContextFactory.java:31)
This is due to the factory attempting to load jdni context from the following URL:
"jar:file:/mount/storm-dir/data/storm.jar!/jndicontext"
This is a valid URL yet the factory does not understand how to open a jar. Is there an implementation of javax.naming.spi.InitialContextFactory that does? Alternatively is there a way I could work around this issue and add a config directory to Storm's classpath?
I have some classes with this organisation
--> : Inherit
TwittEntititesNetwork --> TwitterGephiStreamer
TwittGrapher --> TwitterGephiStreamer
TwitterGephiStreamer is Abstract
TwitterGephiStreamer have a method : myMethod()
Directory
./myApp.jar
./NetworkLogicDirectory/TwittGrapher.jar
./NetworkLogicDirectory/TwittEntititesNetwork.jar
I use this code to load dynamically the daughters classes (which are in another .jar file)
public static TwitterGephiStreamer LoadNetworkLogicJar() throws Exception
{
File dir = new File(NetworkLogicDirectory);
URL[] urls = new URL[dir.listFiles().length];
for(int i = 0;i < dir.listFiles().length;i++)
{
File s = dir.listFiles()[i];
String url = "file:///"+s.getAbsolutePath();
urls[i] = new URL(url);
}
ClassLoader = new URLClassLoader(urls);
if(defaultProps.containsKey("NetworkLogic") && !defaultProps.getProperty("NetworkLogic").isEmpty())
{
Class<?> networkLogicClassLoader = ClassLoader.loadClass("org.naoyun.gephistream.networklogic."+defaultProps.getProperty("NetworkLogic"));
Object object = networkLogicClassLoader.newInstance();
return (TwitterGephiStreamer) object;
}
else
{
throw new Exception("blabalbalbal ");
}
}
So it's have to return a TwitterGephiStreamer which I can use as a normal class and I can use myMethod() normally .
When I run on eclispe it's works well I don't have any error.
When I export my app as a runnable .jar (myApp.jar) it's throw me this error :
java.lang.ClassCastException: org.naoyun.gephistream.networklogic.TwittEntitiesNetwork cannot be cast to org.naoyun.gephistream.TwitterGephiStreamer
at org.naoyun.utils.ConfigurationTools.LoadNetworkLogicJar(ConfigurationTools.java:62)
at org.naoyun.TwitterStreamer.<init>(TwitterStreamer.java:34)
at org.naoyun.TwitterStreamer.main(TwitterStreamer.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)
So, I don't know how to resolve this problem. Is my code creepy but Eclispe can handle it on the fly, or is there other stuff that I'm not up do date?
Thanks for your time !
You probably have org.naoyun.gephistream.TwitterGephiStreamer included more than once on your dynamic classpath. When the subclass is loaded it loads a copy of TwitterGephiStreamer that is conflicting with a previous loaded copy.
Since, TwittGrapher.jar and TwittEntititesNetwork.jar depend on the same core classes/interfaces (i.e. TwitterGephiStreamer), I would suggesting putting those types in a separate utility jar. This should help eliminate any duplicated types on the classpath and provide for a clean jar dependency tree:
myApp
/ \
TwittGrapher TwittEntitiesNetwork
\ /
TwittUtil
You should not use Eclipse to build your executable jar but rather make one manually, e.g. using an Ant build script.
When Eclipse builds an executable jar it packages everything together in one tidy package, loading dependent jars through its own classloader, found in the "jarinjarloader" package you see referenced in your stack trace. This classloader can't find your external jar when it's not part of the "omni-jar" that Eclipse builds.
I've used the URLClassLoader in the past to successfully do what you're trying to do: load external jars at runtime. Here is a question on SO that explains how to use it:
Is it possible to “add” to classpath dynamically in java?
This tutorial should help with using Ant to build an executable jar:
Build an executable jar file by referencing your dependencies
This Ant tutorial is similar and shows how to do it with Eclipse, though it doesn't set the dependencies in the manifest. You'll need to do that for your core application classes, not any external jars to be loaded at runtime.