1

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?

Michel Feinstein
  • 13,416
  • 16
  • 91
  • 173

2 Answers2

2

Let me shed some light on the main point here. There are fundamentally two kinds of java artifacts:

  1. Applications, i.e. ears, wars, executable jars
  2. 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.

J Fabian Meier
  • 33,516
  • 10
  • 64
  • 142
  • Oh that makes lots of sense... So Maven could make things easier and have an option to say if the jar is an executable or a library, so we could avoid the hassle to use the Assembly plug-in – Michel Feinstein Dec 16 '18 at 10:49
0

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.

Antoniossss
  • 31,590
  • 6
  • 57
  • 99
  • The way you explain it is how I understood...that if something is declared as `compile` so it will come along with my `jar`. But currently I am generating a jar, with `compile` `scope` on all my dependencies, and NONE of them in being exported with my `jar`, which triggers a `java.lang.NoClassDefFoundError` when I run my code. So AFAICS I need to add the `Assembly plugin` in order to get my dependencies in my `jar`, am I wrong? – Michel Feinstein Dec 16 '18 at 00:41
  • It dependes on how are you building your jar. It should be done like that https://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven. OFC you have to use assembly plug (or others) for that. Including dependencies is not java compiler's feature. If we talk about javac purely, then tjhis scope has the same affect - including dependenci on **compilers classpath** – Antoniossss Dec 16 '18 at 00:46
  • Well, I understand that's not the compiler's job... But I thought it was Maven's job – Michel Feinstein Dec 16 '18 at 00:52
  • So why aren't my depencies into my jar? Lol – Michel Feinstein Dec 16 '18 at 01:22
  • But maven is nothing by itself but a plugin container roughtly speaking. Everything is done by plugins - ye even compilation is done by compiler plugin. I have no clue what is your problem here. – Antoniossss Dec 16 '18 at 01:24
  • *So why aren't my depencies into my jar? Lo* idk, you are not telling Maven to do it - simple. – Antoniossss Dec 16 '18 at 01:26
  • Well, I guess I expected it to have a default (overridable) behavior, including those plug-ins... So I wouldn't have to include the Assembly plug-in every time I wanted to export my depencies with my jar – Michel Feinstein Dec 16 '18 at 01:27
  • Your expectations are wrong here. That's it i guess. – Antoniossss Dec 16 '18 at 01:27
  • Well, I could bet I never used the Assembly plug-in before, I will check some old code of mine to figure this out, thanks – Michel Feinstein Dec 16 '18 at 01:28
  • Why would it be as default? Why do you think that everybody wants to do it as you do? What about plugins, servlets, all other stuff that strictly requires to NOT TO create fat jar. Actlually, there are more usecases for slimjars than fat jars. So i guess that is the default here - compile only. – Antoniossss Dec 16 '18 at 01:28
  • Well, I thought that if I declared `package` to `jar` and some depencies scope as `compile` and others as `provided`, so the ones declared as `compile` would be exported within the jar... I can't see why someone will declare something as `compile` and NOT wanting it into the jar – Michel Feinstein Dec 16 '18 at 01:30
  • `package` is only a phase. Besides you got all the classes packaged into jar right ?? FYI jar creation is not compilers feature neither. – Antoniossss Dec 16 '18 at 01:32
  • I thought if people wanted small jars, just declare everything as `provided` – Michel Feinstein Dec 16 '18 at 01:32
  • And here is where your problem lies. Besides fat jar is only 1 of packaging method. You can make assembly to create jar + lib directory for example. So again - why would fj be the default ?? ;) – Antoniossss Dec 16 '18 at 01:32
  • I see... Well, in my case I would like a lib folder inside a uber jar...so I guess I have plenty of reading to do... – Michel Feinstein Dec 16 '18 at 01:37
  • I have provided you the link onto how to configura assembly to create fatjar. https://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven. – Antoniossss Dec 16 '18 at 01:38
  • Lib dir inside jar? I don't think thats possible due to how JVM is looking up classes from jar. Directory structure must reflect packages. Assembly will repack your classes into single jar. – Antoniossss Dec 16 '18 at 01:39
  • Well, I need an uber jar (shade plug-in) to deploy into AWS lambda... One of their recommendations for making Java more efficient is to package the libs into a lib folder, so the whole thing isn't processed into the classpath in the first run – Michel Feinstein Dec 16 '18 at 01:40
  • but lib dir is not part of jar. And that is what I wrote lilbit earlier that it is possible do do jar + lib dir. – Antoniossss Dec 16 '18 at 01:41
  • AFAIK the jar is just a zip... So I thought this made sense, the JVM would unzip the jar and the lib folder will be there.... But I am really not sure about this – Michel Feinstein Dec 16 '18 at 01:41
  • No this does not make any sense. JVM does not unpack it. The fact that jars are zips is meaningless – Antoniossss Dec 16 '18 at 01:42
  • Hmm... So maybe there are referring to making a zip of lib folder + jar.... I will revisit this into their documentation – Michel Feinstein Dec 16 '18 at 01:42
  • Yep that is most probably what they mean. – Antoniossss Dec 16 '18 at 01:54