How to extend my jar later with another .jar or .java - java

I imagine my application like this:
I have an application for encrypting files. And I want to extend ciphers in my application(jar) with another cipher later.
So, I will have a folder with ciphers (jar or java files) and my application will read the files from this folder. Then, in GUI, there will be a list with files(jar or java), which have a method encrypt and decrypt (I would test it with reflection). And user will choose one.
Could someone give me an advice? How make it, that it could be extended? Or how my application(.jar) could work with another .jar, .java file(read them and run them)?

Another way to do this would be the java.util.ServiceLoader mechanism. (Read the documentation.)
Your additional jar would contain a file META-INF/services/my.package.Cipher listing all implementations of your Cipher interface (or abstract class), and then you can say
ClassLoader cipherLoader =
new URLClassLoader(new URL[]{new URL("jar:file:myAdditional.jar")},
Cipher.class.getClassLoader());
ServiceLoader<Cipher> serviceLoader = ServiceLoader.load(Cipher.class, cipherLoader);
for(Cipher c : serviceLoader) {
c.encrypt(...);
}
Using ServiceLoader mandates that your implementations have public no-argument constructors - if they don't have, use instead a ServiceLoader for some factory interface.
The URL should be either a jar: URL to the jar file in which the classes are, or a non-jar URL which points to the root directory where the .class files are in (in their package structure).

I would check out the Java Extension Mechanism.
This document describes the mechanism
provided by the Java™ platform for
handling optional packages. An
optional package is a group of
packages housed in one or more JAR
files that implement an API that
extends the Java platform. Optional
package classes extend the platform in
the sense that the virtual machine can
find and load them without their being
on the class path, much as if they
were classes in the platform's core
API.

Related

What prevents someone from subverting qualified exports by pretending to be a module they are not?

Given:
module A
{
exports fuzzy.bunny to B;
}
What prevents a malicious player from pretending to be module B in order to gain access to module A's secrets?
I know that Java has some sort of signing mechanism in META-INF that allows each module to ensure that its own class files have not been modified, but what mechanism ensures that one module can trust another module's class files?
Might not precisely be the answer you are looking for, but a part of JMOD file creation using jmod tool explains it briefly :
--hash-modules
With the --hash-modules option or the jmod hash command, you can, in
each module's descriptor, record hashes of the content of the modules
that are allowed to depend upon it, thus "tying" together these
modules. This let’s you to allow a package to be exported to one or
more specifically-named modules and to no others through qualified
exports. The runtime verifies if the recorded hash of a module matches
the one resolved at run time; if not, the runtime returns an error.
further from the same documentation
These hashes are recorded in the JMOD archive file being created, or a
JMOD archive or modular JAR on the module path specified by the jmod hash command.
Useful: There is a hashing example in the link shared above as well which depicts a use case to which your question quite relates.

Dynamic loading and unloading of jar

We have a java based (jersey+grizzly) REST Server which exposes calls like
foo.com/{game}/rules
foo.com/{game}/players
foo.com/{game}/matches
There can be arbitrary number of games
Each game has different implementations for rules, players, matches
For historical reasons, we would want separate jars for each game implementation
So there is REST Server
as and when there is a call like foo.com/tennis/rules
we want the REST Server to dynamically load 'tennis' jar. The jar does its operation. Then jar should be unloaded
If the next call was for foo.com/football/players
we want the REST Server to dynamically load 'football' jar. The jar does its operation. Then jar should be unloaded
Is there a technique to do this ?
Apparently there is a very old question around [this]: java: is there a framework that allows dynamically loading and unloading of jars (but not osgi)?
I don't know how it works on Java 8, but unloading a class in Java 7 requires unloading not only the class, but its loader, along with all references from other objects that this class might have.
Once all of them were unloaded the System.gc will be called. If other classes are still holding references then the gc won't do its job.
OSGI (as suggested by #Joop Eggen) is a viable option. JRebel, is not.
proxy-object proxy-object library
Load java jar files dynamically in isolated class loader to avoid dependency conflicts and enable modular updates. All jar files in the [main jar folder]/lib/myLib/2.0/*.jar will be loaded.
Code Examples
Create Object from a JAR file located in the myLib/2.0 folder:
File libDir = new File("myLib/2.0");
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();

How to use ServiceRegistry

I run into a problem, I wrote a Class(EasybImpl) which implement EasybPlugin, but when I iterate providers, I could not get the EasybImpl support that i wrote.
I thought the EasybImpl is using the System classloader and the console println the classloader as sun.misc.Launcher$AppClassLoader. what's wrong with it.
Iterator providers = ServiceRegistry.lookupProviders(EasybPlugin.class,
ClassLoader.getSystemClassLoader());
Did you declare your class as a service provider?
To declare a service provider, a services subdirectory is placed within the META-INF directory that is present in every JAR file. This directory contains a file for each service provider interface that has one or more implementation classes present in the JAR file. For example, if the JAR file contained a class named com.mycompany.mypkg.MyServiceImpl which implements the javax.someapi.SomeService interface, the JAR file would contain a file named:
META-INF/services/javax.someapi.SomeService
containing the line:
com.mycompany.mypkg.MyService
http://docs.oracle.com/javase/6/docs/api/javax/imageio/spi/ServiceRegistry.html
Four steps are required:
you build a META-INF/services subdirectory into your JAR file.
in the 'services' directory, create a file for each service you are implementing. (in your case, this would be file META-INFA/services/my.package.EasybPlugin).
in that file, declare the name of your implementation class(es).
you can then use the java.util.ServiceLoader API to discover & load registered services.
An example of the service provider file:
# providers of EasyBPlugin SPI
# (comment lines begin with pound)
my.package.StandardEasybPlugin
You can then find and load service implementations like this:
ServiceLoader<EasybPlugin> loader = ServiceLoader.load(EasybPlugin.class);
for (EasybPlugin plugin : loader) {
// ...
}
Note 1: the ServiceProvider API you were looking at is really targetted at Image IO, eg reading image file formats. ServiceLoader is for general use & should be preferred unless your purpose is not reading images.
There's a more detailed tutorial here: http://literatejava.com/extensibility/java-serviceloader-extensible-applications/
References:
https://docs.oracle.com/javase/tutorial/ext/basics/spi.html#the-serviceloader-class
https://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html
https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html targetted at sound, but clear simple examples of the service provider files.

How to make the java ServiceLoader work in a NetBeans 6.9 module application

I have troubles using the java ServiceLoader in a NetBeans module application. Here is what I'm trying to do (and it works in a normal java application in Eclipse):
I have an interface.jar, which declares the interface. And I have implementations.jar, which has several implementations of this interface, all specified in the spi/META-INF/services/my.package.name.MyInteface file (this file is in the implemenations.jar).
I also have a class ImplementationHandler (in yet another handler.jar), which has the following method to load all implementations:
private static List<MyInterface<?>> loadAllImplementations() {
List<MyInterface<?>> foundImplementations = Lists.newArrayList();
try {
for (MyInterface implementation : ServiceLoader.load(MyInterface.class)) {
foundImplementations.add(implementation);
}
} catch (ServiceConfigurationError error) {
system.out.println("Exception happened");
}
return foundImplementations;
}
This code returns all implementations in Eclipse normal application (the foundImplementations.size() > 0).
However under NetBeans, it can't find anything (foundImplementations.size() == 0).
More details:
I have the source of a NetBeans module application (open source, not written by me), which I need to extend by using some of MyInterface implementations. The interface.jar, implementations.jar and the handler.jar are created in Eclipse and they are part of another application.
In the NetBeans, I opened the module which needs to use the new impplementations and I added all my 3 jars as external libraries (NetBeans copied them into its ext folder, which I don't want but I can't do anything about - I want them in another myext folder, but that's another story). Then I rebuilt everything and tried to use one of my implementations, but it was not found... The code that gets an implementation is in the ImplementationHandler class and looks like:
public static final <T> MyInteface<T> getByName(String name) {
for (MyInteface implementation : loadAllImplementations()) {
if (implementation.getName().equals(name)) {
return implementation;
}
}
throw new IllegalArgumentException("Unable to find MyInterface class for: " + name);
}
I got my exception "Unable to find MyInteface class for: myImplementationName"...
My knowledge about NetBeans is very limited and I was wondering is there something more that I need to do in order to get this working?
In order to work, you have to make Netbeans create this services sub folder inside META-INF. It's very easy to do, but the information is easily accessible.
To add something to META-INF, you need to create a folder of this name in your src/ (the source directory [spi?]) folder. In this case you also need the services folder and in it, create a text file with the same fully qualified name as your service interface. In the end you should have this structure: src/META-INF/services/my.package.MyInterface.
Finally, this [my.package.MyInterface] file's content should list all the implementation classes (one per line).
With this setup, Netbeans will create the appropriate jar when building your app.
Take a look at this ServiceLoader example. It's a complete example, although it does not explain the Netbeans integration I just described.
The ServiceLoader.load(Class) uses the current thread's context class loader to load all the implementations. It may be that in your case your implementation classes in your jar file (implementation.jar) are not in that class loader's classpath.
You may have to try different approaches for this :
You may either need to have all the jars in the netbeans module's classpath or,
You may need to create a class loader (probably a URLClassLoader having those jars in its classpath) and use the ServiceLoader.load(Class, ClassLoader) and pass a that classloader.
There is another option you could try but I am not sure about this: The jar file spec allows you to specify Class-Path manifest attribute, to which you can add 'implementation.jar' entry. More details here
Most likely, the handler.jar and implementations.jar are not loaded by the same class loader. Also you may want to take a look as to why your files are getting to ext folder.
Hope this helps.
Edit:
Also try calling the ServiceLoader.load(Class, null) which uses the System class loader (the one that started the application). Probably that classloader may be able to find classes in jars located in the ext directory.
I gave up, and replaced ClassLoader with JSPF. This works out of the box and I don't need to know about the internals of a third party program, written as NetBeans module and how this affects the classpath given to the class loaders.

Class.getResourceAsStream() issue

I have a JAR-archive with java classes. One of them uses some resource that is embedded into the same JAR. In order to load that resource I use
MyClass.class.getResourceAsStream(myResourceName);
One thing that bothers me though is whether it is guaranteed that required resource will be loaded from within the same JAR. The documentation for "getResourceAsStream()" method (and corresponding ClassLoader's method) is not really clear to me.
What would happen if there's a resource with the same name located somewhere in JVM classpath before my JAR? Will that resource be loaded instead of the one embedded in my JAR? Is there any other way to substitute resource embedded in JAR?
Yes. The first matching resource found on the class path is returned, just like an executable search path. This is why resources are often "namespaced" by putting them in directories that mirror the package structure of the library or application.
This behavior may be slightly different in the presence of custom classloaders (say in OSGi), but for vanilla Java apps, it is the case.
It works much the same way as for finding class files. So first try the parent class loader (recursively) then do whatever the class loader implementation does to find files.
There is no checking of the immediate caller class loader (as ResourceBundle does - see section 6.3 of the Java Secure Coding Guidelines). However, you do need permissions to open the URL, as ClassLoader.getResourceAsStream just calls URL.openStream in the default implementation.
Specify the package. Assuming you use com.yourcompany.file it SHOULD be unique. (Unless someone WANTS to override your config file via the classpath.)
If you want to read the file only from a specific JAR you can open the JarFile and read it directly.

Categories

Resources