How can I have multiple and separated classpaths in java? - java

When I write code in nodejs, I can have multiple versions of the same library because 'require'-ing a library is not global.
The classpath is different. Each library is looking at its node_modules library.
However, in java, I cannot seem to be able to have multiple versions of same library in the classpath.
As the classpath is global.
Is there any way to make java behave more like node in that sense - by making each classloader have a different classpath?
why is my question different than that one
That question is based on "given a classpath, ... question .." while I am not assuming anything on the classpath. quite the contrary, I would like to have a different classpath for each library if possible.

Ok, I read a lot about it.. I have a long answer and a short one.
The long one - I will write a blog post about it and link it here..
Here is the short one
Assumptions
dep_1.jar - is a dependency I have, and is not dependent on any other jar. (a single jar packaging)
The API is available in my classpath. I only want to separate the implementation's classpath.
The class I want to instantiate has an empty constructor
Lets talk about a specific use case
// jar dep_1-api.jar
package guymograbi;
public interface IMyMessage {
public String getMessage();
}
// jar dep_1.jar
package guymograbi;
public class MyMessage implements IMyMessage{
public String getMessage(){ return "my message"; }
}
Code
ClassLoader classloader = new URLClassLoader(new URL[]{ new File("java_modules/dep_1.jar").toURI().toURL() })
return (IMyMessage.class) classloader.loadClass("guymograbi.MyMessage").newInstance();
This will return an IMyMessage implementation without changing my classpath.
I can then load another implementation of the same interface from another jar, etc.. etc..
This solution is:
small enough to embed in any library that wants to support it.
small enough to quickly wrap any existing library with it.
almost zero learning curve.. just a need to apply to an easy standard.
Further reading
So it seems there are many ways in which you can write this solution.
You can use Spring, Guice and ServiceLoader to do the actual implementation.
ServiceLoader - I kinda liked this solution has it has something similar to main entry in package.json. AND - obviously - it does not require a dependency that will mess with my classpath!
I also recommend checking io.github.lukehutch:fast-classpath-scanner that allows you to easily find all classes that implement a specific interface.
Next Step
The next step for me is to construct a proper java_modules folder and allow dep_1 to be a folder containing index.jar and its own java_modules folder and cascade the solution downwards..
I plan to do so using the answer from: How to get access to Maven's dependency hierarchy within a plugin
And then write a maven plugin (like assembly) to pack it all up properly.

Related

Equivalent to C++ header files in Java?

I've got a large Java library and I want to develop several smaller applications that interface this library. The library will be present on the target device's class-path as JAR, but I would like to avoid the need to have the entire library (either JAR or source) present at compile-time if possible. (If it matters, the JAR is quite huge and I want to protect intellectual property too, though it's not my primary concern.)
In C++, I would solve this issue by creating a DLL (.so) and copying just the relevant class and function definition headers to my project and adding them to include path at compile time, let the dynamic linker do the job at runtime.
How to do this in Java? One idea I have would be to remove private methods/members, and strip methods of relevant classes so that their bodies are empty and the real classes and methods with same signatures are loaded at runtime. However, this approach seems quite ugly and rudimental, plus some tool would be needed to automate this process. Is there a tool to do this? Is there a better way?
I don't think it's a duplicate of this question. The point of that question is to minimize size of the resulting JAR file at compile time, by removing unnecessary classes. My point is not to remove unused definitions, but to avoid the need of having the complete library JAR at compile time at all. Though these are simmilar and there may be a way how to achieve what I want using ProGuard, the linked question does not discuss it.
There's no exact equivalent for header files in Java, but compiling against the "header" (in the meaning of "contract") without having the actual implementation can be achieved using interfaces:
Create interface for every class you want the "header" for, with relevant methods
Make the actual classes implement the respective interfaces
Pack the interfaces into one JAR, and the implementations into another one
(if you're using a build tool like Maven, use two projects, and let the implementation project depend on the interface one)
Only provide the interface JAR at compile time, and both at run time
There of course will need to be some artifact that knows the actual implementations and can instantiate them, or you'll have to use some lookup that searches classpath for a suitable implementation.
If your problem is only due to the minimization of the final jar and you use Apache Maven, you could try to use the option "provided" when you declare a dependency in the pom.xml.
For example I use this dependency declaration:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.48</version>
<scope>provided</scope>
</dependency>
This means that my java compiler use the bouncycastle library to compile, but finally it doesn't include it in the final jar. You should provide it during execution.

Pulling in a subset of classes from a sibling project

In my root project, I have many sub-projects:
common
dependant-1
dependant-2
...
standalone
In this scenario, common is a shared library. All other projects are dependant on it, with the exception of standalone. (standalone is actually a standalone client JAR).
What I would like to do is get 5 classes from common into the jar file produced in standalone. I only need those exact 5 classes (out of ~200) and want to avoid bringing common as a full dependency (along with all of common's dependencies). Granted it's an unusual setup, but I don't want to include classes that the client has no business with and the classes I am including just contain enum or static final.
So far, I have tried the following in the build.gradle for standalone:
jar {
manifest.attributes(
// .... Removed
)
with project(':common').jar {
include('com/classpath/ClassA.class')
include('com/classpath/ClassB.class')
include('com/classpath/ClassC.class')
include('com/classpath/ClassD.class')
include('com/classpath/ClassE.class')
}
}
This works very well for the standalone project, but tramples the other projects that were fully dependant on common; common.jar will now only ever contain the classes listed above, regardless of which dependant project.
(I'm guessing this is expected behaviour: in Gradle's configuration phase, it sees the specific configuration I have for common in standalone and applies that to common).
So, in short, what is the neatest solution to this? I'm thinking that I may need to provide a configuration in the build.gradle for the common project. I'm not sure how to do this yet (RTFM). I just wanted to check that there isn't a better approach to this?
EDIT: to answer some of alexvetter's questions.
I hadn't yet tried to include them from build/classes, but this did the trick (see the accepted answer).
I did consider creating a new common-base project. If there were much more than 5 classes that I needed, I absolutely would have gone this way. And yes, I only need the classes at runtime.
Just to clarify the last point... the standalone jar file is actually a JAX-WS client jar that we provide to another team. The reason I needed to inlcude these specific classes in the client JAR is so that the client knows what values to give to certain web service method calls. Ideally, I would have replaced all of the included classes with enum (like I mentioned, they are literally just static final definitions) and JAX-WS would have looked after everything. (I actually did this to replace one of the class files that I was including.) If I had done this for all the classes I needed to include, it would have triggered many more code changes for them (why this was a problem is a whole different story ;)
Following should do the trick:
from("${project(':common').buildDir}/classes/main") {
include('com/classpath/ClassA.class')
include('com/classpath/ClassB.class')
include('com/classpath/ClassC.class')
include('com/classpath/ClassD.class')
include('com/classpath/ClassE.class')
}
But as I already stated in my comments. This classes are only available on runtime and you should probably create a new project (e.g. base) which is a dependency of common and standalone

Two JARs on buildpath with identical method names but different constructors. How can I specify which JAR's method to use?

I am building a tool from several different open source libraries. My buildpath is in the following order:
My first JAR file, stanford-corenlp-3.3.0.jar, contains a package called edu.stanford.nlp.process, which has the Morphology.class class.
My second JAR file, ark-tweet-nlp-0.3.2.jar, contains an identical package name (edu.stanford.nlp.process), and an identical class name Morphology.class.
In both JARS, inside their respective Morphology classes there exists a method called stem(). However, the constructors for these methods are different. I want to use the stem(String, String) method from my second JAR file, but since the import statement (import edu.stanford.nlp.process.Morphology;) does not specify which JAR to use, I get an error since it thinks the first JAR on the buildpath is the one I want to implement.
I don't want to change the order of my buildpath since it would throw off my other method calls.
How can I specify which JAR's Morphology class to use? Is there an import statement that specifies the JAR, along with the package.class?
EDIT: What about a way to combine my two JARs so that the two Morphology classes merge, giving me two methods with different constructors?
As several others pointed out above, it is possible to tweak Java's classloader mechanism to load classes from certain places… but this is not what you are looking for, believe me.
You hit a known problem. Instead of worrying how to tell Java to use a class from one JAR and not from the other, you should consider using a different version of ArkTweet.
Fetch the ArkTweet JAR from Maven Central. It does not contain Stanford classes.
When you notice that people package third-party classes in their JARs, I'd recommend pointing out to them that this is generally not a good idea and to encourage them to refrain from doing so. If a project provides a runnable fat-jar including all dependencies, that is fine. But, it should not be the only JAR they provide. A plain JAR or set of JARs without any third-party code should also be offered. In the rare cases that third-party code was modified and must be included, it should be done under the package namespace of the provider, not of the original third-party.
Finally, for real solutions to building modular Java applications and handling classloader isolation, check out one of the several OSGi implementations or project Jigsaw.
The default ClassLoader will only load one of the jars, ignoring the second one, so this can't be done out of the box. Maybe a custom ClassLoader can help.
For more info about ClassLoaders start from here.
Good luck!
EDIT: We are looking at some horrible packaging choices causing as side effect this Jar Hell here. The author of this "Ark Twitter" library decided it was a good idea to release a JAR artifact that includes a third party library (the Stanford NLP library). This leads to unnecessarily tight coupling between Ark Twitter and the specific version of the Stanford NLP library used by it. This is a very bad practice that should be discouraged in any case: this violates the whole idea about transitive dependencies.
EDIT (continued): One possible (and hopefully working) solution is to rebuild the Ark Twitter JAR so that it does not include the aforementioned library but only its own code (basically the cmu.arktweetnlp package only) and hoping that the version of NLP required by your project works with Ark Twitter. Ideally you should submit a pull request to the author of the library but in the meantime you can get away with un-jarring and re-jarring the existing JAR file.
EDIT 2: Looking at the JAR file again, it's much worse that I originally thought: ALL the dependencies are repackaged in the released JAR file. This is really the worst possible solution for releasing a library. Good luck.
I think your problem can be solved simply by using the lemma(String word, String tag) method in the current CoreNLP's Morphology class:
String word = ...;
String tag = ...;
String lemma = morphology.lemma(word, tag);
WordTag wt = new WordTag(lemma, tag);
When the class was revised a couple of years ago, the method you're looking for was deleted. The feeling was that with most of the Stanford NLP code moving to using CoreLabels, methods that return WordTag are less useful (though deleting all such methods is still a work in progress).
No there isn't. This is a weakness of Java, that cannot be simply solved. You should use only one of the libraries. Having both on the classpath will make java always select the first one.
This problem is named as Jar hell.
The order in the buildpath generally determines the order in which the classloader will search for the class. In general, though, you don't want duplicates of the same class in your build path--and it sure doesn't seem like ark-tweet-nlp-0.3.2.jar should have a edu.stanford package within it.
When you load a class, it's loaded at given address, and that address is then placed in the header of objects created from the class, so that (among other things) the methods in the class can be located.
So if you somehow load ClassA, with method abc(String), from zip file XYZ.zip, that loads into address 12345. Then (using a class loader trick) you load another ClassA, with method abc(String, String), from zip file ZYX.zip, and that loads into address 67890.
Now create an instance of the first ClassA. In its header will the class address 12345. If you could somehow attempt to invoke the method abc(String,String) on that class, that method would not be found in the class at 12345. (In actuality, you will not even be able to attempt the call, since the verifier will stop you because, to it, the two classes are entirely different and you're trying to use one where the other is called for, just as if their names were entirely different.)

How to create my own java library(API)?

I created a program in Java and I designed it so that methods that I want them to appear (getter methods) in the main, I can call them easily after initiate the class that holds these methods.
The question is that, I need to make this application (that holds the getter methods) to be like an API so that I can give my application for developers to use my functions (the getter methods) if they need them, and only what they need is to add this file (I think the API after is done shown as .jar file).
How can I make it so that I can make my code reusable with other application? It's similar to the .dll, I think.
Thanks a lot ;)
Create a JAR. Then include the JAR. Any classes in that JAR will be available. Just make sure you protect your code if you are giving out an API. Don't expose any methods / properties to the end user that shouldn't be used.
Edit: In response to your comment, make sure you don't include the source when you package the JAR. Only include the class files. That's the best you can really do.
To be useable as an API, your classes should:
Use a unique package (ideally following the convention, i.e. the reverse of a domain you own as prefix). This prevents naming conflicts
Have only those classes and methods public or protected that are intended to be used by others. This makes it easier to use.
Have extensive Javadoc comments.
Be available as a JAR file - ideally separate JARs for binary distribution, source code and javadoc files.
You need to package your application as a jar file. You can use ant jar task to create jar files or you can use the jar command.
For ant tasks look at this link.
For creating it manually look at this link.
Make sure you write and publish javadocs for all your public and protected classes and methods.
To create the jar:
jar cf <jar_name> <sources>
There are several ways you can expose your code. Creating a jar and distributing that may be the easiest as other developers will just have to include your jar. However, if you are talking about "anyone" accessing your code, a web service may make more sense as you can provide access to the data without providing all of the necessary code. You mention providing access to your getters - if you just create a class that has getters, the other developers can use them, but how are they going to be populated? If your application is self contained in that it gets the necessary data and provides the getters, that should work, but if you are talking about providing access to data from your running application, a web service makes more sense as your application can retrieve the data and provide access via publicly accessible methods.
You most likely want to create interfaces as well so developers can code against the interface and you can change the internal workings without impacting them. Any API that will be used by others should be extensively documented as well.
Well, depends on your IDE. I use Netbeans, so I just hit build project, and viola! A jar file is created in my directory specified. Now, that's just for compiling. All anyone has to do is download your .jar file, and if in Netbeans, right click libraries, add jar/folder, and select the downloaded file.
You can also consider:
Include some samples that demonstrate how to use your library
Build your jar using Apache Maven
Put your jar in a public maven repository
Publish a new version of your library as you find/fix bugs
If you want to hide your implementation, you can pack your jar with obfuscation, so that if someone decompiles your classes, the code will be difficult to read

Implementing dynamic plugins in Java

I'd like to implement a dynamic plugin feature in a Java application. Ideally:
The application would define an interface Plugin with a method like getCapabilities().
A plugin would be a JAR pluginX.jar containing a class PluginXImpl implementing Plugin (and maybe some others).
The user would put pluginX.jar in a special directory or set a configuration parameter pointing to it. The user should not necessarily have to include pluginX.jar in their classpath.
The application would find PluginXImpl (maybe via the JAR manifest, maybe by reflection) and add it to a registry.
The client could get an instance of PluginXImpl, e.g., by invoking a method like getPluginWithCapabilities("X"). The user should not necessarily have to know the name of the plugin.
I've got a sense I should be able to do this with peaberry, but I can't make any sense of the documentation. I've invested some time in learning Guice, so my preferred answer would not be "use Spring Dynamic Modules."
Can anybody give me a simple idea of how to go about doing this using Guice/peaberry, OSGi, or just plain Java?
This is actually quite easy using plain Java means:
Since you don't want the user to configure the classpath before starting the application, I would first create a URLClassLoader with an array of URLs to the files in your plugin directory. Use File.listFiles to find all plugin jars and then File.toURI().toURL() to get a URL to each file. You should pass the system classloader (ClassLoader.getSystemClassLoader()) as a parent to your URLClassLoader.
If the plugin jars contain a configuration file in META-INF/services as described in the API documentation for java.util.ServiceLoader, you can now use ServiceLoader.load(Plugin.class, myUrlClassLoader) to obatin a service loader for your Plugin interface and call iterator() on it to get instances of all configured Plugin implementations.
You still have to provide your own wrapper around this to filter plugin capabilites, but that shouldn't be too much trouble, I suppose.
OSGI would be fine if you want to replace the plugins during runtime i.g. for bugfixes in a 24/7 environment. I played a while with OSGI but it took too much time, because it wasn't a requirement, and you need a plan b if you remove a bundle.
My humble solution then was, providing a properties files with the class names of plugin descriptor classes and let the server call them to register (including quering their capabilities).
This is obvious suboptimal but I can't wait to read the accepted answer.
Any chance you can leverage the Service Provider Interface?
The best way to implement plug-ins with Guice is with Multibindings. The linked page goes into detail on how to use multibindings to host plugins.
Apologize if you know this, but check out the forName method of Class. It is used at least in JDBC to dynamically load the DBMS-specific driver classes runtime by class name.
Then I guess it would not be difficult to enumerate all class/jar files in a directory, load each of them, and define an interface for a static method getCapabilities() (or any name you choose) that returns their capabilities/description in whatever terms and format that makes sense for your system.

Categories

Resources