Manage javaagent dependencies in a Java project built with Gradle - java

For systems that require a javaagent (say, OpenTelemetry) the docs often start with "download the agent JAR from this URL and add it to your command line". In a world where library dependencies are handled quite well using Maven Central, with stable versioning etc., the "download a JAR" approach seems primitive and insecure by comparison.
What is the best practice for acquiring javaagent libraries in a project built with Gradle? Is "download this jar" really the current state of the art?
I'm specifically interested in OpenTelemetry right now. If there's an answer (eg. a Gradle plugin) that only works for OpenTelemetry, I'm all ears.

From what I have done research, there is one gradle plugin available specifically for attaching a maven dependency as javaagent.
Quoting from plugin github repository:
This Gradle plugin tightly integrates with the Gradle application plugin to make instrumenting your application build by Gradle easy! Simply register the javaagent-application plugin and then specify the javaagent you would like to attach in the dependencies block
Example usage with otel java agent can found in the same repository here
plugins {
id("com.ryandens.javaagent.example.java-application-conventions")
id("com.ryandens.javaagent-otel-modification")
id("com.ryandens.javaagent-application")
}
dependencies {
otel("io.opentelemetry.javaagent:opentelemetry-javaagent:1.13.1")
otelExtension("io.opentelemetry.contrib:opentelemetry-samplers:1.13.0-alpha")
otelInstrumentation(project(":custom-instrumentation", "shadow"))
}
application {
// Define the main class for the application.
mainClass.set("com.ryandens.javaagent.example.App")
applicationDefaultJvmArgs = listOf("-Dotel.javaagent.debug=true", "-Dotel.metrics.exporter=none")
}
/*
see https://github.com/johnrengelman/shadow/issues/713
Currently, tasks that consume the output of the extendedAgent shadowJar task need to be made aware of
the implicit dependency (https://docs.gradle.org/7.4.2/userguide/validation_problems.html#implicit_dependency)
due to an issue with the shadowJar plugin
*/
setOf(tasks.distTar, tasks.distZip).forEach {
it.configure {
dependsOn(tasks.extendedAgent)
}
}

Related

Build JAR with a dependency from the custom repository

I'm trying to build a JAR artifact which requires a custom dependency from the bintray repository, like:
https://dl.bintray.com/foo/bar
Is it possible to instruct the project that consumes such a library that it should resolve dependencies in the extra repository?
Or should I take another approach to provide it?
TL;DR
Build a fat jar including your dependencies.
Long Version
Yes, it is potentially possible to instruct a project that consumes your Gradle plugin to download plugin dependencies from a non-default repository. But it requires the user intervention which is probably not what you want to hear.
The following settings.gradle should retrieve your plugin from the Gradle Plugin Portal and resolve the dependencies from your Bintray repository:
pluginManagement {
repositories {
maven {
url 'https://dl.bintray.com/foo/bar'
}
gradlePluginPortal()
}
}
This is documented under Plugin Management.
Please note that I'm a bit vague in my answer since I never did something similar. What I can tell is that the spring-cloud-contract plugin does exactly this for snapshot versions.
IMHO, in your specific case, you get the best user experience by building a fat jar that includes your dependencies. A remarkable companion being the Gradle Shadow Plugin. It also features additional functionality for Gradle plugins, should you ever need them.

Gradle Plugin dependency

What is the exact dependency I need to develop a Gradle Plugin in Java? Ideally I would like to get it from a well-known repository such as Maven Central or similar.
I have a Maven project with a core functionality and I just added two extra plugins, one for Ant, one for Maven. They are already tested and working; easy! Now, I wanted to add a third module for a Gradle plugin to make this functionality also available from any Gradle project.
However, I can't find the exact dependencies I need to develop a Gradle plugin.
The Gradle docs (such as https://docs.gradle.org/current/userguide/java_gradle_plugin.html) are not very well written to say the least. They mention:
the gradleAPI() dependency
or the java-gradle-plugin dependency
But they are quite unclear... no group, no version (really?).
If anyone can enlighten me to where I can get these dependencies from, I would be very thankful.
Gradle's public and internal APIs, aka gradleApi(), are bundled with the Gradle distribution and not independently published and therefore not easily consumable by Maven builds. There's the pending epic #1156 (Ensure plugin cross-version compatibility by allowing a user to depend on gradlePublicApi()) that might help here.
Since Gradle plugins are best to be built with Gradle, a pragmatic solution is to invoke the Gradle build from Maven and attach the produced artifact to the Maven build. Andres Almiray (aalmiray) once described this in the blog post Running Gradle Inside Maven (Web Archive Link). He describes the following high level steps:
Create a new Maven module (e.g. gradle-plugin) and add attach it to the parent POM
In the POM of gradle-plugin add a dependency to your core module. Use the maven-dependency-plugin to store dependencies to the Maven build folder, e.g. target/dependencies.
Create the build.gradle, add a Maven repository that points to target/dependencies (step 2) and let it depend on the core module as well as gradleApi(). Implement the Gradle plugin.
Use the exec-maven-plugin to invoke the Gradle build.
Use the maven-resources-plugin to copy the Gradle built plugin jars to the standard Maven build folder.
Use the build-helper-maven-plugin to attach the copied jars to the Maven build.
Sample project to be found here (gradle-in-maven).
https://docs.gradle.org/current/userguide/custom_plugins.html#sec:custom_plugins_standalone_project
In here it is mentioned that it is gradleApi() and I know that this works (from experience). The localGroovy() on that page is only needed if your plugin code uses groovy (does not apply if you only use groovy in the build.gradle of your plugin).
java-gradle-plugin is a library that makes it a bit simpler to make plugins, it is not required though. I personally prefer using gradleApi only.
EDIT:
It appears I've misunderstood the question. Here are the steps to get gradleApi jar:
Create a Gradle project with your desired Gradle version.
Add implementation gradleApi() dependency.
Import/run the project once.
Go to your .gradle folder (located in home folder in Linux-based operating systems).
Open caches folder
Open the version folder you want, e.g. 6.0.1
Open generated-gradle-jars folder.
Copy the jar to wherever you want and use it.
For me the 6.0.1 jar is at ~/.gradle/caches/6.0.1/generated-gradle-jars/gradle-api-6.0.1.jar
Please note that I have not tested this, I know the jar is there but I haven't tried using it.

How to use a parent in Gradle across different projects? [duplicate]

At my work we use Maven. I am going to try gradle for the first time. We use a common parent pom for all project which has setting for commonly used maven plugins and few comon dependencies. Is there a similar option available in gradle?
My second question is regarding release management. We use maven release plugin, which works pretty good for us. Is there something similar available in Gradle?
To share stuff within multiple projects of the same build, use allprojects { ... }, subprojects { ... }, etc. Also, extra properties (ext.foo = ...) declared in a parent project are visible in subprojects. A common idiom is to have something like ext.libs = [junit: "junit:junit:4.11", spring: "org.springframework:spring-core:3.1.0.RELEASE", ...] in the top-level build script. Subprojects can then selectively include dependencies by their short name. You should be able to find more information on this in the Gradle Forums.
To share logic across builds, you can either write a script plugin (foo.gradle), put it up on a web server, and include it in builds with apply from: "http://...", or write a binary plugin (a class implementing org.gradle.api.Plugin), publish it as a Jar to a repository, and include it in builds with apply plugin: ... and a buildscript {} section. For details, see the Gradle User Guide and the many samples in the full Gradle distribution.
A current limitation of script (but not binary) plugins is that they aren't cached. Therefore, a build will only succeed if it can connect to the web server that's serving the plugin.
As to your second question (which should have been a separate question), there are a couple of third-party release plugins available, for example https://github.com/townsfolk/gradle-release.
The io.spring.dependency-management plugin allows you to use a Maven bom to control your build's dependencies:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "io.spring.gradle:dependency-management-plugin:0.5.3.RELEASE"
}
}
apply plugin: "io.spring.dependency-management"
Next, you can use it to import a Maven bom:
dependencyManagement {
imports {
mavenBom 'io.spring.platform:platform-bom:1.1.1.RELEASE'
}
}
Now, you can import dependencies without specifying a version number:
dependencies {
compile 'org.springframework:spring-core'
}
I think the best way to do things like maven parent pom is to to use gradle "apply from".
Something like this:
allprojects { // or: subprojects { ... }
apply from: "gradle/script/common.gradle"
}
The link and be a related path or an URL. Hope it helps.
Reference:
Import a Gradle script from the root into subprojects
Super POM, Parent POM type of hierarchy management in Gradle
I too wanted this type of feature, I have created a plugin to provide this here: https://github.com/boxheed/gradle-pater-build-plugin
You can convert the Parent pom content in to Gradle init file very easily.
Gradle init script provides same functionality as Maven super/parent pom. The basic difference is that you can call init script
Run time
As many as of them This gives us flexibility to change the init
script on run time but doubt of not tracking the changes.
You need to take repository, distribution management, profiling and other checks like findbugs, checkstyle etc in to init script.
The detail is huge, You can find complete information here by me.
http://www.scmtechblog.net/2015/12/how-to-migrate-parent-pom-from-maven-to.html
I have explained about gradle release plugin which is similar to maven release plugin.
to achive your goal you could apply the concept of 'multiproject build' explained in the gradel user guide here
Basically you can create an umbrella project which define a set of common configurations by creating a gradle.build file and a gradle.settings file.
The build file contains the properties, dependencies and plugins commons to all projects, the settings.gradle defines what subprojects inherits those configurations.
Moreover, to have an idea of the gradle plugin ecosystem you could check this source.
It is currently not possible, if you want the parent to be cached locally and stored in a Maven repository.
I have added feature request here:
http://forums.gradle.org/gradle/topics/support_for_gradle_parent_shared_between_projects_cached_locally

Gradle / Maven: How to use in-developement dependencies?

Context:
I'm working on a Gradle project which depends on external libraries, also created by me. Currently I was usign a simple dependecy on project(':lib').
But this is not enough anymore, I need to release and distribute libraries as standalone components, versionned and documented. I will install Apache Archiva and publish them to an internal maven repository, so I can depend explicitly on 'com.company:lib:1.0'.
Question:
During developement, I will work on libraries and on projects at the same time. How can I test my code without publishing the libraries ? My application which used to depend on project() will now depend on a specific version. But while developing, I would like to use the local code.
Do you know what is the best process to handle this ?
One way would be to add the dependency conditionally. So for your local builds (IDE) you want to build the dependency via source. Then you can distinguish your release builds by having your releases pass a param to a build.
dependencies {
if (project.hasProperty('release')) {
compile 'com.company:lib:1.0'
} else {
compile project(':lib')
}
}
Then in your release builds to use the lib from nexus:
$ gradle -Prelease=true clean build
If you want to build the project with the lib from inside the project:
$ gradle clean build
The correct answer to this question is available in Gradle's documentation (section 23.8.3.1), and is the following:
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute module("com.company:lib") with project(":lib")
substitute module("com.company:lib2") with project(":lib2")
}
}
Personally, I used the following code, which makes all -SNAPSHOT versions to be taken from local projects instead of remote repository (if a local project is available):
configurations.all {
resolutionStrategy.dependencySubstitution {
all { dependency ->
if (! dependency.requested.version.endsWith('SNAPSHOT'))
return
if (subprojects.find { p ->p.name == dependency.requested.module })
dependency.useTarget project(":" + dependency.requested.module)
}
}
}

Extra Gradle tasks in Spring project

Fairly new to Gradle. Newer to Spring. I understand the creation of Gradle tasks, how to compose them, and how to create Groovy plugins but it seems that there's more going on than what's in the project.
I've got a Spring REST service with a build.gradle file but I notice that once everything syncs, there are a TON of tasks in my Gradle plugin that I can't seem to find anywhere in the project. Such as: cleanEclipse, installApp, startScripts and many others.
Are these added by something more global? If so, what is it that defines the creation of all of these tasks?
IntelliJ IDEA 14.0.1
Gradle 2.2.1
Windows 7
The extra tasks that you see look like Gradle tasks are injected into the project by various plugins.
For example, the eclipse plugin includes tasks such as eclipse, cleanEclipse etc.
Here is a listing of all the standard Gradle plugins that come bundled with Gradle: https://www.gradle.org/docs/current/userguide/standard_plugins.html
Each of these has a list of tasks/properties that it injects into the project. Third party plugins would also do the same and their corresponding documentation should have information regarding these.
I think it can be added if others plugins are applied in your project, for example eclipse plugin or application plugin.

Categories