Find unused gradle runtime dependencies - java

I am in the process of updating a large set of legacy java applications. The current builds use ant with its dependencies coming from each project's lib directory. The dependencies are part of each project and checked in to source control. The purpose of the updates is to convert the builds to gradle and use a maven repository for dependency management. For simplicity, the legacy builds copied dependencies from other project's lib directories using an ant construct like:
<fileset dir="../anotherlegacyproject/lib">
<include name="**/*.jar" />
</fileset>
This approach is used for both compile and runtime dependencies. While this works, it leads to bloat of the deployment artifacts because of the many jars included which are not necessary at runtime. For example, many of the projects include junit, hamcrest, and jaxb-xjc in the runtime even though they are only used for compile or test. Most of the projects create both a zip file and an RPM containing deployable applications. I would like to only include the required runtime dependencies in the RPMs.
I am trying to determine a reliable method for identifying these unused runtime dependencies.
I have reviewed the post: How to find\remove unused dependencies in gradle but this does not address unused runtime dependencies. I have tried using the following gradle plugins, none of which identify unused runtime dependencies:
github.com/nebula-plugins/gradle-lint-plugin
github.com/wfhartford/gradle-dependency-analyze
docs.gradle.org/current/userguide/jdepend_plugin.html
I am open to any solution that will reliably work, but my preferences would be in this order:
An existing gradle plugin
Code that could be incorporated into the build.gradle file or groovy code packaged as a plugin
Anything else

Recently we migrated some project from 1.6 to 1.8, needed to find out unused jar. I was trying to use some plugin and tool but could not solve my issue.
Over stack overflow I got some insight to fix this issue. Below are the steps to find out unused jar in project.
First you need to take latest EAR/WAR of your module. Suppose you
module name is CheckDependency, so take latest EAR/WAR and get it
out lib folder.
Now in eclipse replace lib folder of your module with new one, which
taken from EAR/WAR.
Eclipse go to your module and select Gradle(STS)[This should be
installed in your machine]-->Hit Task Quick
Launcher-->Select projectTattleTaleReport It will generate
all report in you target folder with name tattleReport

Related

Unable to launch a JavaFX application which uses Apache POI

I have a JavaFX application that works as expected. I need to use Apache POI to read and write excel files. The following are the steps I have taken:
Added the required dependency
implementation 'org.apache.poi:poi-ooxml:5.2.3'
Added the module to module-info.java
requires org.apache.poi.ooxml;
Tried to use the library within a function:
#FXML
private void downloadTemplate() {
XSSFWorkbook workbook = new XSSFWorkbook();
}
All this is fine with no issues. However when I try to run the application, I get the following two errors (interchanging)
> Task :Start.main() FAILED
Error occurred during initialization of boot layer
java.lang.module.FindException: Module SparseBitSet not found, required by org.apache.poi.ooxml
and
> Task :Start.main() FAILED
Error occurred during initialization of boot layer
java.lang.module.FindException: Module commons.math3 not found, required by org.apache.poi.ooxml
I can however, clearly see both libraries under 'external libraries'
I am using IntelliJ Community Edition 2022.1.2 and running the project using Java 17.0.1. Any help would be highly appreciated.
SparseBitSet is an automatic module, it has no module-info of its own (probably commons-math3 is as well), and is without an Automatic-Module-Name entry in its manifest.
Gradle puts libraries without a module-info.class or an Automatic-Module-Name in their manifest on the class path, not the module path, so they won't be treated as modules, and the module finder won't find them.
You can:
hack the gradle build to allow the modules to be found. (I don't use Gradle so I have no specific advice on how to do that other than referring to the documentation).
Hack the library jar which you want to be treated as a module to include a module-info.class or an Automatic-Module-Name in its manifest.
Or, switch to maven, which automatically places automatic modules on the module path.
The easiest way to do this, IMO, is to create a new JavaFX project in Idea, then add the required dependencies as maven dependencies and add your code.
Or, as swpalmer suggests in the comments, request that library maintainers update their codebase to make their libraries modular.
And, when you run your app, make sure all jars are on the module path, not the class path.
Or, make your app non-modular by removing the module-info.java from it, then manually place the JavaFX modules on the module-path and add them with the --add-modules switch.
FAQ
Are you SURE that automatic modules are put on the class path by Gradle?
From the Gradle documentation section Building Modules for the Java Module System:
To tell the Java compiler that a Jar is a module, as opposed to a
traditional Java library, Gradle needs to place it on the so called
module path. It is an alternative to the classpath, which is the
traditional way to tell the compiler about compiled dependencies.
Gradle will automatically put a Jar of your dependencies on the module
path, instead of the classpath, if these three things are true:
java.modularity.inferModulePath is not turned off
We are actually building a module (as opposed to a traditional
library) which we expressed by adding the module-info.java file.
(Another option is to add the Automatic-Module-Name Jar manifest
attribute as described further down.)
The Jar our module depends on is itself a module, which Gradles
decides based on the presence of a module-info.class — the compiled
version of the module descriptor — in the Jar. (Or, alternatively, the
presence of an Automatic-Module-Name attribute the Jar manifest)
It is the third point that is key. Java can treat a library with no module-info.class and no Automatic-Module-Name in the Jar manifest as an automatic module if it is on the module path. However, Gradle will by default, only place libraries which fulfill one of those two conditions on the module path.
Using jewelsea's answer above, I have been able to solve the problem. I am posting the answer here to help anyone else who encounters the problem in future.
So, the overall problem is, as said in the answer above, both SparseBitSet and commons-math3 are automatic modules with no module-info of their own. The solution that worked for me was to convert them into the modules expected by the project. Here are the steps I took:
Use a gradle plugin 'extra-java-module-info'. The github page didn't show how to import it to a normal gradle file so here it is:
plugins {
id 'org.gradlex.extra-java-module-info' version '1.0'
}
Note the names that your application expects for the modules. In my case, from the error messages thrown, they were 'SparseBitSet' and 'commons-math3'
Locate the said libraries on the sidebar under 'external libraries' and note the 'jar' file names. In my case, they were 'commons-math3-3.6.1.jar' and 'SparseBitSet-1.2.jar'.
Add a section 'extraJavaModuleInfo' to your gradle files and use the parameters as follows: module('jar file name', 'name expected by your project', 'jar version'), as shown in the blue rectangle in the image above.
extraJavaModuleInfo {
module('commons-math3-3.6.1.jar', 'commons.math3', '3.6.1')
module('SparseBitSet-1.2.jar', 'SparseBitSet', '1.2')
}
That's it. Try to sync and run your project. Thanks jewelsea.

Generate Eclipse launch configurations with Gradle

We use Eclipse to launch and debug our Java GWT project. We have a couple of eclipse launch configuration files with many classpath entries needed for running the project.
Previously we used ant to build our project and had all our dependencies present on our local storage, with all classpaths pointing to libraries explicitly for project as well as launch configuration classpaths.
Once we migrated over to Gradle 6.6 and used public repositories to download our dependencies, almost everything worked smoothly - all our project classpaths were generated correctly by the gradle build. However, our eclipse launch configurations still contain the old hardcoded classpaths and I'm unable to find a way to generate these launch configurations using those gradle configured classpaths. As a result we've had to maintain the dependencies on our local filesystem so that the configurations have access to them in order to debug and run our code.
Is there a way Gradle can be used to generate these launch configurations so that we can get rid of the libraries on our filesystem and rely on whatever it pulls from the repositories during a build?
Any suggestions or workarounds will be appreciated.
It you're using Eclipse's built-in Gradle tooling (aka, Buildship) and let Eclipse generate your launch configuration, it will automatically be in sync with the project's build path, which is automatically kept in sync with the dependencies declared in your Gradle build file.
Here is an example Gradle project I just created, where I added some dependencies and the launch configuration "just works." The project is named "lib," notice under Classpath Entries is "Project Dependencies"; that's where Eclipse's Gradle tooling maintains and syncs with what's in the build.gradle file.
Here's what the project's Build Path looks like if I inspect it:
Whenever you add a dependency to build.gradle, it will automatically be included there; the launch configuration(s) then reference the project dependencies so the launches are also always in sync.
You might need to delete your existing launch configurations and let Eclipse generate new ones to get this.

Resolving dependencies in Eclipse for Fitnesse

My goal is to run unit tests in fitnesse.responders.run.slimResponder for testing my DataFlex SlimRunner implementation. So I downloaded the Fitnesse source code, and made it a new Java project in Eclipse. I was able to compile it by selecting Run As Ant Build (2) on the build.xml file. But in order to resolve include errors in the Problems view in Eclipse, I ended up manually adding dozens of external JARs by hand. I found that Maven/Ivy had apparently downloaded the jars as part of the Ant build. But somehow these were not added to the Java Build Path.
It seems reasonable to me to assume that there should be an easier way to set up the Java Build Path than to add the JAR files manually, since build.xml apparently contains all this information already. What am I missing?
The Fitnesse Readme.md mentions using Apache Ivy for dependency management. Download IvyDE from Eclipse Marketplace, and set it up (use the ivy.xml that is part of the Fitnesse source code).

How to specify dependency on tomcat libraries with gradle

A bit of background about my knowledge level: I'm currently trying to learn how to build a project with gradle. So far I don't have much experience with build tools (almost none). I do understand the general idea and have seen ant and maven files before but never written them myself. Until now I just used other peoples build scripts or configured my builds using Eclipse. So I might be on a completely wrong track here, if so please point me in the correct direction.
Also, I sadly don't have much experience building jars by hand.
I have an Eclipse project which I want to compile into a jar. Required library jars are on my local file system. I managed to make gradle use these via
dependencies {
compile fileTree(dir:'lib', include:'*.jar')
}
in addition I have my source files in src/main/java and just use apply plugin: 'java' in the build.gradle file. Trying to build the project with gradle build seems to do the right thing, as far as I can tell.
However, the library is supposed to be used in a web project running on a tomcat and makes use of some libraries that are supplied by tomcat, as far as I understand. E.g. I'm using javax.servlet.http.HttpServletRequest.
The project works fine in Eclipse, but there I have the tomcat library added to my Eclipse build path. When I check in Eclipse I can see that javax.servlet.http.HttpServletRequest is part of the servlet-api.jar which is part of the Tomcat library.
Now, when I build the project I get build errors because the java compiler cannot find the class because I didn't specify the servlet-api.jar in the dependencies. I guess I could download it somehow (or learn how to specify it as an external dependency to make gradle download it from some repository) but I am not sure whether that would be correct.
Is there a way to tell gradle to use the same library that Eclipse uses? Or some other general way to tell it about all the tomcat jars, the same way I can simply add the complete Tomcat library in Eclipse?
Or do I actually need another copy of these jars somehow and have to specify each one individually?
Also, do I need to add these library jars to my build-result library jar? As far as I know I need to add any jar I depend on to the resulting jar as well. But then again, I have read somewhere that some libraries are supplied by tomcat itself so they would have to be part of any war deployed on it.
I'm afraid, I'm confused by the combination of how to build a jar-file to be used in a war-file to be deployed on a tomcat using gradle and I don't know from which of these parts my problems originate. I hope someone reading this can help me untangle my thoughts and point me in the right direction or at least tell me how to add the jars included in the Tomcat library to my gradle dependencies.
With Gradle, whenever you add files or directories as dependencies, they are not treated as full-fledged artifacts (with group, name and version), but rather as simple files containing classes. It means that Gradle will not perform any conflicts resolutions on them, or pull transitive dependencies.
For you, just to get started, I recommend just to add tomcat dependency. Make sure it is the same version as the one in Eclipse.
apply plugin: 'war'
repositories {
mavenCentral()
}
dependencies {
providedCompile 'org.apache.tomcat:tomcat-catalina:7.0.47'
}
Also, look into Eclipse Integration Gradle project as a long-term solution.

How to add dependencies from Ivy to the Eclipse PDE Target Platform?

I can add in external OSGi bundles into my PDE project and I can get them to show up in the Target Platform (although I need to perform a reload once I have added a new one), I do this with a "lib" directory in each project.
As my project has become more sophisticated I require some Spring bundles, and I looked to Ivy (the Ivy DE plugin) to manage the dependencies for me. This did a great job of finding all the bundles that were needed which get added to the classpath.
The problem is that I also need to add them to the target platform and I cannot find a way to get the target platform to look at the Ivy dependencies.
A possible workaround is to copy the Ivy dependencies into my lib directory. I'm very new to Ivy and cannot see a way to get it to copy dependencies to a local directory (to do that would seem to defy the point of Ivy). I also looked at adding the Ivy cache to the target platform (a crazy idea) but Eclipse couldn't find all the bundles because of the cache's directory structure.
Ivy is primarily designed to be embedded within an ANT build. In such a case you could use the retrieve task to decide where to save files within your build workspace:
<ivy:retrieve pattern="${dist.dir}/WEB-INF/lib/[artifact].[ext]" conf="runtime"/>
I'm not an expert on the ivy Eclipse plugin and whether this functionality is available for pure Eclipse builds.

Categories

Resources