Maven reduce bundles size by excluding unneeded dependencies in bulk? - maven

The project I'm working on has dependencies on a few well known and big libraries. Things are working well, the transitive dependencies are playing nice with each other, for now. But unfortunately the total bundle size is around 100 megs.
I'm not sure if this is too large or not, but is there a way in maven to effectively remove dependencies, without making pom.xml very verbose and long?
And pointers to help me in the right direction would be awesome!

You can remove dependencies by declaring exclusions. The question I would post to first though is .. why do you want to remove them?
Unless you know that the dependencies are not needed, it might not make sense to try to remove them. Especially not if there are no issues e.g. in terms of application performance or startup times.
The Maven Dependency Plugin as well as the Eclipse integration have tools that allow you to understand the dependencies better with tools like the dependency:tree goal or the Dependnecy Hierarchy view of the POM.
Don't try to fix something that is not broken.
BUT ..
if you really know what is needed at runtime and use the tooling from Maven and M2e you can potentially remove a lot of bulk of your final artifact. However you will have to configure it in the pom using dependency exclusions. Newer Maven versions even allow patterns being used.
Another thing you can do is use a tool like proguard that removes all unused classes from the final artifact. This can be considerably complex but also VERY effective.
It will really be up to you to find the right balance between effort and benefit of outcome.

Related

Sharing gradle dependencies: version catalog vs plugin

Gradle is working on new features for sharing dependency versions between projects that will provide central locations (settings.gradle, libs.versions.toml) for declaring common dependencies.
It's already possible (and easy) to share a dependencies block through a plugin, so what is the downside of the plugin approach to sharing dependencies, versus the new version catalogs and dependency bundles? What do these new features improve upon?
This is not a thorough answer. However, let me share what I think makes a difference. We need to keep in mind that Gradle is built around developer productivity and making builds as fast as possible.
Centralizing common dependency declaration makes sense to be supported out of the box. Currently, there is a good chance that when you look at different Gradle projects, each one of them may implement a different approach to this. Cédric Champeau iterates over some existing pattern in this blog. Having a standard solution makes it easier to get started as developer. Cédric further states
Long story short: the presence of a catalog makes discoverability and maintenance easier, but it doesn’t remove any of the flexibility that Gradle offers. We’re thinking about ways to enforce that all direct dependencies are declared via a catalog in the future.
Declaring dependencies in libs.versions.toml allows Gradle to skip build script compilation when dependency versions are changed. This is significantly faster than changing the same in a script plugin. As a side-effect of declaring dependencies in libs.versions.toml, we may see third-party tooling that update dependencies automatically in the future.

Reducing Maven Dependencies

I am using Maven to build my project. There are lot of dependencies which are in provided and runtime scope. The build size is large and I want to remove the unwanted dependencies. So is there any way in which I can check which dependencies are unwanted.
The best way to minimize dependencies to the ones you really need is to not embed that dependencies. If you simply use the maven-bundle-plugin on your code it will create Import-Package statements for the code you really need. This might even give you a good hint on what dependencies you might be able to exclude for embedding.
In general in OSGi the goal should be to not have that many dependencies in the first place. If you use libraries with extensive dependencies then your should question the quality of these.

Maven exclude philosophy

What maven Excluded dependencies was invented for?
Should I care to exclude any dependencies other then for fixing libraries conflicts?
What would good maven project architecture look like:
tend to exclude as much as possible
or as minimum as needed?
I don't know the reasons for the design but I've seen it used in the following cases:
I had a library which had junit as a compile time dependency so JUnit code leaked into my production code.
I had a library which uses log4j. Since I'm using slf4j, I used dependency exclusion to get rid of the hardwired logging framework and used a slf4j-log4j bridge instead so I could ultimately log to logback.
In another case, I was using only some features of a framework and didn't need all the dependencies. Since they weren't optional in the first place, I used exclusions to keep my classpath lean and clean.
General rules:
Use it to get rid of things that break your build
Get rid of things that you're replacing with something else
Get rid of things that you know you don't need (optional)
If none of the rules apply, leave the dependency alone; chances are that the immediate dependency might change over time and suddenly, it will need some dependency that seemed superfluous before and you code will unexpectedly break.
In addition to Aaron's answer:
An exclusion is usually needed when the provider of the dependency did something wrong (i.e. did not make a dependency optional where he should have, included an actual logging backend - as opposed to api - or used the wrong scope).
The one exception is logging frameworks. See Aaron's answer for that.
So no, do only exclude dependencies if you have a specific reason to exclude them.

How to run plugin on maven dependency

I'm setting up a (java) maven project that depends on a library (Jettison, among others) that is in the Maven repo. Jettison, in turn, depends on stax. I need to run a tool (Jar Jar Links) on stax (to change the namespace). How do I alter the rules for a transitive dependency in a maven project? My transitive dependencies are being included in my target folder using the copy-dependencies goal (I assume this is how things are usually done). I assume that this is the point where the plugin would be run on the transitively-generated artifact.
Extra question: I don't need this at this point but how would I go about altering the source in the transitive dependency? I can get the jar of the source with mvn dependency:sources but, from there, I'm not sure what the right approach is.
Victory!
Seems at least two people are even more clueless about Maven than me so let me explain what I'm doing before I report the fix at the bottom of this post (spoiler alert: it looks to be a bug in JarJar).
Android uses Java but its missing a lot of the java core (specifically, javax classes). The Android DEX compiler (which converts .jars to Android .dex files) won't even allow you to compile things in the java.* or javax.* namespace because it'll (usually) break stuff. However, in some (many) cases, there are routines that you might want to include -- specifically because they are used by existing libraries. The most legendary is StAX, which is why Google posted an example of how to include it here in the Dalvik repo's wiki. The example uses JarJar... with ant. Transitive dependencies are not really an issue when you aren't using a repo so they are not addressed in the wiki.
I was able to get JarJar to run on my source with Maven but without changing the namespaces in the dependencies (and transitive dependencies), that's worthless. Hence my question.
I thought that the copy-dependencies plugin might be useful for... copying the dependencies and running a transforming plugin in the process. Copying dependencies is mentioned as a step in the official "Maven in 5 minutes" doc so it seemed like a good start but maybe the the people who wrote the official docs don't know how to use it :-) . Either way, it it didn't help -- there is no simple way I could see to transform the jars as it copies.
Using the verbose spew from Maven, I was able to see that Jar Jar was in fact processing my jars properly... and then throwing out the result. It would have packaged the converted classes from the transitive dependencies in my artifact with the rest of my code but, instead, it "Excluded" them. Jar Jar parameters are basically undocumented and most of the tags aren't even listed in the docs but all of the examples I could find use a section with wild-cards that tell it what classes to hold onto. At least I thought (think?) that's what the section is for. Instead, it seems to randomly throw out stuff. Basically, the section is busted. For example, I had:
<keep>
<pattern>com.example.**</pattern>
</keep>
...thinking that this would keep classes that began with com.example. Wrong. It keeps whatever the hell it wants. I tried a million things in that spot until one worked:
<keep>
<pattern>*.**</pattern>
</keep>
This only keeps the classes I wanted -- the classes it updated and the originals of the ones that it didnt touch. Note that ** doesn't even work. This is version 1.8 of the JarJar plugin (the version most poms Ive found use).
Back to work.

Freezing transitive dependencies on maven release to get build fully reproducible

A problem that relates to basic maven concepts:
Once released I would like to have a guarantee that the project build is fully reproducible. So all project and plugin dependencies, including transitive one, should be always resolved the same way.
Unfortunately it is not the case, if dependencies are expressed in terms of version ranges. It can happen that even though direct dependencies of a project are set (using versions:use-releases), the transitive dependencies can still be resolved in some other way in the future.
How to address the problem? Is there a known solution?
I was thinking (just an idea), about creating a plugin, which on release time would dump all dependencies of the project to a separate file, and then once building in the future, the dependencies read from the file would take precedence over the standard way maven uses to resolve dependencies. But I'm afraid that there is no plugin api for that. So it would require some hacking, which I would like to avoid. Is there another way?
Thanks,
Lukasz
Freeze artifacts versions using <dependencyManagement>. Even if you don't use version ranges (as you said), but rather 3rd party libs (your dependencies) do, your <dependencyManagement> will have higher priority in specifying version of any artifacts.
The simple solution is: Do not use version-ranges. This is bad practice cause it will result in the described problems.

Resources