I have an application that has both JPMS modules and old fashioned non modular jar files. I don't want to put all of the jars on the classpath because then I loose some specific functionality baked into the modules (such as JPMS service provider interface). I also can't put the old fashioned jars on the module path as automatic modules because they have many package name conflicts.
The solution is to use both the module path and classpath at once. I could define the contents of my classpath and module path manually but this is liable to breaking if a dependency changes with a new version. As such this really needs to be done automatically and I have seen that this is actually done with maven-surefire when running unit tests as well as eclipse when launching the application from the IDE.
Does anyone know if there is a an existing library (or preferably maven plugin) that allows me to separate the dependencies that can be on the module path and the dependencies that have to be on the classpath in the same way that surefire or eclipse does?
Related
I am upgrading a legacy application to java 11. Currently we don't use maven or gradle.
As i've updated it, i've replaced certain libraries that have been removed from the jdk in 11. Some of the newer dependencies i've added contain module-info.java files. When I go to create a custom jre for my application using jdeps (in preparation for jlink), i have the option of specifying a class path and a module path. My question is, can the paths be the same 'lib/*' directory? In my mind this would try to use the jars as both modular jars and regular jars. If i must separate them, maybe there is a tool to help me know which ones need to be put in a separate directory (identify jar's containing 'module-info.java') and give me a list of them.
In short, yes you do need to put modular jars in a separate directory than your non modular jar dependencies for jdeps.
Here is a great video on the module system:
https://www.youtube.com/watch?v=M7q3C8OwJe8
I have used SBT to build a pure-java project. This project consists of multiple modules, and projectB depends on projectA.
Now after switching to JDK 11, this dependency should be modeled not via classpath but via module path.
Is it possible to tell SBT that module depencencies must be resolved via module path instead of the classpath?
Basically I have set up a inter-(sbt-)module depencencs that says projB.dependsOn(projA) - so can I tell SBT somehow to put this depencency on the javac module-path instead of using the classpath? I see currently no way of doing that.
EDIT: Further analysis showed that I needed also to use normal library depencencies (from libraries on a nexus repository) as module depencencies. So the next question would be - how can I treat these library depencencies as module depencencies in SBT?
I do know that SBT is a scala build tool, and that it is something of a stretch to use it for a java-only project, but I use SBT for other (scala-)projects, and I do not want to introduce too many different build tools, until I really have to. So - is there a sbt-level solution for this?
EDIT: To clarify the issue - the project in question is a kind of open-source library which we modified. The SBT build file is from our organization, so I have to maintain it.
The project switched to using module-info classes, and it depends on other (binary-only) libraries via module path, if you include module-info.java in the compilation, that is.
So in our SBT build I have to use the module path for some of the external library depencencies.
Also the SBT-subprojects have dependencies on each other, i.e. we also have to use the module path for that.
Is there a way to tell SBT that:
This library must be on the module path, and not on the classpath
This project dependency must be on the module path, and not via classpath?
That is the core of the question. I may be able to write a plugin for the first problem (having not libraryDepencencies, but modularLibraryDepencencies for example) - but I do not yet know how I could handle the second part (tell a project that project depencencies must be on the module path and not on the class path).
I am trying to add a dependency as jar file and that jar is shipped with all the classes which are needed for it to run.
compile files('lib/org.hl7.fhir.igpublisher.jar')
Along with that, I have few other dependencies which are added as maven dependencies.
compile ("ca.uhn.hapi.fhir:hapi-fhir-base:2.3")
Now I am facing a lot of issues related to class conflicts because same classes have been shipped with different versions.
In an ideal case, how should I solve this problem? I want to say that the local jar should always use its own files and other dependencies should ignore the local jar files.
Note:- I am using IntelliJ idea.
This is a tricky problem. There is only one classpath and multiple versions of the same class mean that only one of that versions is visible and the other ones are hidden.
One should generally avoid to declare dependencies on "fat jars" that contain their own dependencies. If possible, one should use the slim version without the dependencies (often both versions are published). If there is not alternative one can construct such a slim jar yourself by manually splitting up the jar file. It is also possible to control the structure by carefully ordering the dependencies on the classpath, but this is a little brittle.
I am using Maven and my configuration (which is most likely the default) produces this:
That means my compiled code is 4% of the file. Largest inflation was caused by the GitHub API library - I am strongly considering that I'll just drop it.
But my question is about the small file, not the big one. Maven creates it for a reason right? Could I maybe somehow distribute it and have it work on clients' computers? Why does it exist and what useful can be done with that?
Given your question, your Maven project is most likely a jar project that uses the maven-assembly-plugin to generate an uber-jar. In your output, there are 2 different files that are the result of 2 completely different process.
autoclient-3.0.jar is what's called the main artifact. This is the primary result of the Maven build. It consists of the compiled classes in your project alone, packaged into a jar by the maven-jar-plugin. You have this file because your project has the jar packaging. In Maven, the jar packaging automatically bind goals to build phases of the default lifecycle: among others, it includes an invocation of the jar:jar goal, which creates this main JAR. But you have to realize that this JAR only contains your classes. If you try to run the code, it will probably fail because the dependencies won't be there. What's its purpose if you can't run it then? Well, its purpose is to serve as a library for other projects, not as executable code.
Take, for example, a utility library that you would like to create: this library is not intended to be ran directly, it's intended to be used as a dependency for another project which will be executable. With the notion of transitive dependencies, Maven will automatically include in the buildpath of the other project your library and all its transitive dependencies. As such, your library does not need to embed its dependencies directly: they will be resolved correctly during the build of the other project.
autoclient-3.0-jar-with-dependencies.jar is what's called an additional artifact. jar-with-dependencies is a classifier that is used to distinguish this artifact from the main one. It is the result of the execution of the maven-assembly-plugin with the predefined jar-with-dependencies descriptor file. This artifact consists of the compiled classes of your project and all the direct and transitive dependencies of your project. An uber-jar (or fat jar) is really that: it aggregates all the dependencies and your code inside one big jar. The advantage is that you don't need to distribute separately the dependencies, they are already included in the JAR. But do note that its purpose is not to serve as a library, it is to be used as executable code.
Note that for a single project, it could make sense to keep both JAR: one intended to be used as a library (the main JAR) and the other intended to be used as runnable JAR.
I created an Eclipse 4 application and I needed a jar offering a functionality as part of my application (this could be anything e.g. log4j to make it trivial).
I added the jar as part of my project's classpath (Right Click->Configure Build Path) but on runtime my service failed with a ClassNotFound error (from OSGI I guess?).
Anyway searching this it turned out, at least as I have understand it, that I should add the jar as part of another Plugin and create a dependency from my application/service to this new plugin.
I.e. I created a Plugin Project from Existing JAR archives.
This time the setup worked.
So if I understand this, when developing for Eclipse/OSGi we should not add jars in the classpaths directly but add them via plugins (why?).
Question: If I am correct so far, what is the standard practice to include jars when developing a project?
Define/Create one Plugin Project from existing JAR archives and add all the required third party libraries needed there, or have a different plugin project per needed jar or something else perhaps???
Sorry if my terminology is not accurate. I am new in OSGi and Eclipse programming
Note: When talking about jars I am not refering to other OSGi services. I am refering to the norm of using ready, reliable third party libraries that would be needed by many parts of an application. E.g. log4j or an xml parsing library or apache commons etc
For the runtime it is always the Manifest and the headers there that control what is in your bundle classpath. There are three ways to get access to a jar:
Import-Package header. This is the recommended way. You define one import per package you need. You jar you want to access has to be deployed in the runtime as a bundle. It also needs to export all needed packages.
Require-Bundle . This is another way to access bundles. You define the id of the bundle you need and see all packages it exports. As Require-Bundle binds you more closely to the other bundle the Import-Package way should be preferred.
Bundle-Classpath . This allows to add jars to your classpath that you embed into your own bundle. This should only be a last resort when the other way do not work. You can have nasty classloading issues when mixing this with the other methods.
You can find many pre built bundles in maven central. Many jars today already contain an OSGi manifest. For the cases where this is not true many jars are repackaged as bundles by servicemix. See groupId: org.apache.servicemix.bundles. There is also the spring bundle repository where you find some more.
Below I listed some resources you might want to read:
http://www.aqute.biz/Blog/2007-02-19
http://wiki.osgi.org/wiki/Import-Package
http://wiki.osgi.org/wiki/Require-Bundle
http://www.vogella.com/blog/2009/03/27/required-bundle-import-package/
The examples you have mentioned are available as OSGi bundles, so you don't need to make them bundles yourself. You don't typically use direct jar dependencies in OSGi, you typically use package or bundle dependencies. In the log4j example you are referring to, you should use import package as there can be multiple bundle providers (newer log4j jar, springsource bundled version of older log4j, slf4j implementation...). This will disconnect your code dependencies from the actual provider.
These dependencies are maintained via you manifest, not your project classpath. In an eclipse plugin project, the projects build classpath is derived from the entries in the manifest.
Even though you are not using services, all code dependencies are still maintained via the manifest.
Extactaly same problem we faced in our project.
we have some legacy jar which are not OSGi compatible, we create lib folder parallel to BundleContent and added it into the classpath section of manifest.
Bundle-ClassPath: .,
/lib/<legacy jar>.jar
There is no need to exporting and importing of packages unnecessarily if only one bundle is going to consume it,