Understanding Maven dependencies and assembly - java

I am not very much experienced with Maven and it's compilation and packaging logic gets me confused.
I have some dependencies declares as :
<dependency>
<groupId>com.dependency_group</groupId>
<artifactId>dependency_1</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.dependency_group</groupId>
<artifactId>dependency_2</artifactId>
<version>1.0.0</version>
<scope>provided</scope>
</dependency>
So as far as I understand, dependency_1 will be added to the classpath of my program as something that comes along with my jar, and dependency_2 on the other hand, will be added to the classpath as something that the system runtime will provide upon deployment.
Then I run the package goal of Maven and none of my dependencies are packed with my code (I am using the shade plugin, but even without it nothing changes).
I expected that when some dependency is set as compile scope, it will be exported with my compiled code, since AFAICS, there's no point in setting the classpath saying a dependency will come along with my code, and Maven just don't package that dependency with it. It looks to me as if Maven is not obeying it's contract.
So:
1 - What's the logic behind this?
2 - Do I have to always use the Assembly plugin?
3 - Are there cases where people will define a dependency as compile and will not want it packaged within a jar?

Let me shed some light on the main point here. There are fundamentally two kinds of java artifacts:
Applications, i.e. ears, wars, executable jars
Libraries, i.e. jars that are meant to be used as dependencies for other artifacts.
For Applications your reasoning makes perfectly sense. Wars and Ears automatically package all their compile dependencies and you need no assembly plugin for that. For libraries, you do not pack the dependencies into the library. Maven handles transitive dependency resolution and would be confused if you put a fat jar on the classpath.
The thing is that packaging jar can be both a libary or an application. If you want a standalone application, you need to tell Maven to package everything, e.g. by using the assembly plugin or shade plugin.

You use compile scope when you want some dependencies to come along with your code. For example you want Jackson to be a part of your application if you are using it for json serialization.
You use provided scope, if you want dependency to be on the classpath during the compilation but wont be included within your application. It must be provided by running environment. For example you want Lombok as it is compile only library, or you want to have Servlet Api dependency as provided when you are writing servlet application because such app will be ran on servlet container thus there is no need to pack it within your application (it will be available in container runtime)
Do I have to always use the Assembly plugin
Nobody forces you to do so.

Related

How to find if I need to exclude dependencies in a maven java project?

I use both Intellij IDEA (2018.3.5) & Eclipse IDEs, but I prefer Intellij. I have a maven based Java project with multiple poms. I added some dependencies to one of the pom files. I need to find out if there are any dependency conflicts which could prevent the build from running when its deployed, and then exclude them. I tried the steps given below to find conflicts which could cause problems. Are they enough or do I need to do more ?
Check if there are any compile time dependency conflicts with mvn clean install -DskipTests. Build was successful with no errors.
Check if Intellij shows no problems under File > Project Structure > Problems. There are no problems.
I also saw the dependency tree with mvn dependency:tree -Dverbose. It has a lot of "omitted for duplicate" and "omitted for conflict with" items, but the build was successful. I don't see any errors though. Does this mean that everything is okay or do I have to do something more about these conflicts ?
The best way to tell if everything is fine with your application is to have good tests.
However normally one doesn't exclude transitive dependencies from project's <dependency> libraries. Doing it can potentially break the dependency in a subtle and hard to notice way. It's usually safer to remove the whole <dependency>.
There are few scenario when one should use <exclude>:
Dealing with incompatible transitive dependencies between different libraries e.g. A requires library C-1.0 but library B requires library C-2.0 while C-1.0 and C-2.0 can't coexist on the classpath.
Having transitive dependencies already provided by system e.g. deploying to Tomcat with additional JARs in the TOMCAT_HOME/lib directory.
If you decide to exclude a dependency it's important that you check the final artifact because sometimes plugins do weird things e.g. there were versions of maven-assembly-plugin affected by a bug that resulted in different dependencies being resolved during shaded JAR creation than maven-dependency-plugin used for compilation.

OSGi (Karaf) Resolution vs Maven dependencies

In my example below there seems to be a discrepancy / duplication in the required steps in OSGi resolution and Maven dependency support.
I have a jar which is dependent on a external 3rd party jar, in this case time4j.
<dependency>
<groupId>net.time4j</groupId>
<artifactId>time4j-core</artifactId>
<version>4.16</version>
</dependency>
I can then run my simple jar locally by importing the packages and running etc.
When importing into OSGi I have to ensure that this jar is imported first, often using the PAX Wrap Url.
This is ok for 1 jar dependency but what about when there are multiple dependencies. I could use a features.xml file to collect these jars but why? I've already specified them in my pom.xml.
It seems there should be a way for OSGi / Karaf to read the pom.xml dependencies and import these into the container using the PAX Wrap url when needed.
Have I missed something here?
Sorry but your expectations are not in sync with reality.
First of all Maven dependencies are build-time dependencies. That's why you declare dependencies you know to be available in the runtime as provided
<scope>provided</scope>
Neither OSGi nor Karaf can do anything about your build time dependencies.
BUT with OSGi you can make sure your build dependencies are also available in your runtime and don't interfere with other libraries that might be available.
That's why you need to declare your imports and exports etc.
Karaf does help you with some of the dependencies for example with feature files.
If you have a feature definition maven project, all of your compile scope dependencies can be included in one feature file.
BUT, the OSGi resolver only looks at the currently available bundles and nothing more, no connection what so ever to maven, if you want to have some sort of automagic resolving of external dependencies you need to make sure that you have
a) an OBR resolver enabled (this depends on the karaf version you are using, with 4.x it's already included) and
b) an OBR repository at hand, Karaf Cave would be the project to look for in that case, cause it can reside like a proxy on top of a maven repository.

IntelliJ Idea: how to expose classes, interfaces, annotations in developed plugins

I have created a plugin for IntelliJ Idea. In the plugin I have defined an annotation I want to use in my projects, but it doesn't seem to be accessible.
How should I specify in the plugin.xml file the packages I want to expose?
When you install a plugin, it will be on a certain place - e.g. C:\Users\xxx\.IdeaIC14\config\plugins\...
Now that you know where your jar file is, you can add it as a dependency to your project. If you use Maven, you can add something like this to your pom:
<dependency>
<groupId>yourplugin</groupId>
<artifactId>yourplugin</artifactId>
<version>1</version>
<systemPath>C:\Users\xxx\.IdeaIC14\config\plugins\yourplugin.jar</systemPath>
<scope>system</scope>
</dependency>
Or you can install the jar into your local repository and then use it as normal maven dependency.
If not, then add the dependency directly in the project settings as it was any other jar.
plugin.xml has nothing to do with any of this, this is all about jars and classpath. What you could do in your plugin is some user friendly inspections and actions, which would add the dependency for you.
By default, plugins can automatically read and access public members of any other plugin installed on the same IDE (ie. your plugin can read public variables, call public functions - everything goes on the same classpath). If you depend on another plugin, you must first add it as an explicit dependency within the plugin configuration file, so that the end user's IDE will know to download and install your plugin's required plugin dependencies if they are missing.
During development, you should now be using Gradle. In your project's build.gradle (or build.gradle.kts) file, the intellij.plugins property of the gradle-intellij-plugin will let you specify the the id and version of the plugin dependency. The values for these attributes can be found on the Plugin Repository, cf. Plugin XML ID). Subsequently, the gradle-intellij-plugin will add the desired IntelliJ Platform Plugin to your project as an External Library, thereby allowing you to get code completion, static analysis and test your plugin alongside its dependencies inside the plugin sandbox (via ./gradlew runIde).
Plugins should avoid using other plugins' internal classes for stability reasons. If you wish to enable other plugins to use your plugin programmatically (ie. suppose you want to provide an API), then the IntelliJ Platform has the concept of so-called, Extension Points. These will allow you to define a concrete interface, or contract for other plugins to access the functionality of your plugin, without needing to know much about its source code. Using extension points has the added benefit of decoupling those plugins from any internal plugin refactoring.

Can I add maven dependencies that are packed as anything other than .jar?

I just produced my first parent-module project with maven, and successfully installed it. Can I add this project as a dependency in another project, only by referring to the parent? My Eclipse IDE complains that it can't find the parent.jar, but that is not a surprise, as it is packaged as parent.pom.
Question:
So is it possible to add a parent (.pom) dependency, and get all transitive dependencies for free, or do I have to add .jar's.
Bonus Question:
Is it possible to add dependencies to other packaging formats as well, like a war? I can't really figure out how that would work, or why I would need that at this point though. Just curious.
Disclaimer:
I'm still learning maven, and find the philosophy and theory of it to be great. However, there are so many pits and reefs that seems to pop out, and more than once, I struggle to see if I'm trying to do something impossible, or if there is another mistake in configurations I.E. Right now Eclipse says it can't find any of my .m2 referenced dependencies in this one particular project. I have no idea why, as other projects works fine. I am in other words trying to find the error, by checking one area at the time...
Answer: Yes, you can add different types such as pom, test-jar and so on. Jar is just the default
Bonus Answer: Yes, you can specify type war as well
The Maven-Guide defines the following types: "The current core packaging values are: pom, jar, maven-plugin, ejb, war, ear, rar, par."
Here is a example on how a POM is included:
<dependencies>
<dependency>
<groupId>com.my</groupId>
<artifactId>comm-group</artifactId>
<type>pom</type>
</dependency>
</dependencies>
This (the pom of comm-group) is oftenly used to group certain dependencies and include all of them using the type-pom.
Here is additional information on grouping: http://blog.sonatype.com/2009/10/maven-tips-and-tricks-grouping-dependencies/#.VFC7LR_JY8c Note that there are similar behaviours you could create using polymorphism.
I had my issues with maven when we migrated from Ant and i still have certain concernes on it (like were is the advantage of maven if 80% of our SWEs apply wrong scopes, types and so on leading to a massive drawback if they just 'need to add a fcking jar' as well as to refactorings lead by "maven gurus").
BUT: I can guarantee you that if you go throught http://maven.apache.org/pom.html completely you will aquire statisfieing results compared to ANT over time.
Update: I just ran into the case where my pom could not be included on the remote build server while it worked building it from inside Intellij Idea/ Eclipse. Type definition in my case had to be lowercase (e.g. 'pom' instead of 'POM').

Maven EAR compiling

I have this dependency (and many others like this) :
<dependency>
<groupId>jShrink</groupId>
<artifactId>jShrink</artifactId>
<version>3.0.2</version>
<scope>system</scope>
<systemPath>${project.basedir}/../kladr-ear/lib/jShrink-3.0.2.jar</systemPath>
</dependency>
So how to add this JAR to EAR/LIB folder with MAVEN while package?
Looking at the available Maven scopes, I would expect you to mark this as
<scope>compile</scope>
and upload the jar file to your repository (local or remote). Maven should give you the appropriate command line to perform that upload.
EDIT: As noted below, this scope is the default, and as such you could omit it.
Dependencies going into the EAR should generally be based on he dependencies needed for EJB modules. If you have an EJB module with library X on compile scope then that will trigger Maven to want to package the library in the ear that the EJB is part of. No need to manage any scopes at all in the ear pom.
If you don't have any EJBs and only WARs, I would not package libraries in the EAR but simply deploy them as part of the WAR (so they end up in WEB-INF/lib). Keep the web dependencies nice and contained per module, even if that means you get duplicates.
Also compile is the default scope, so you don't ever need to manually declare it unless you're doing it to override another scope set for the dependency.

Categories