Including Local Jars In Groovy - java

I'm attempting to use "HTTPBuilder" within my simple Groovy script. When I use '#Grab' to import the dependency, everything works fine. Though, I'd like to keep the jar within a different directory and import it using the classLoader function. I've copied the 'http-builder-0.7.jar' that '#Grab' placed into my grapes directory and pasted it into the same directory my Groovy script is running (on Windows). I then comment out the '#Grab' statement and include the classLoader, but get this error:
org.codehaus.groovy.control.MultipleCompilationErrorsException:
startup failed: C:\Groovy Scripts\test.groovy: 9: unable to resolve
class HTTPBuilder
Any ideas why the classLoader wouldn't be working in the script? I printed out the path of the jar when importing with '#Grab' and it's definitely using the one within the grape directory. If I uncomment the '#Grab' statement, it works again. Here's the small script...
//#Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.7')
this.getClass().classLoader.rootLoader.addURL(new File("http-builder-0.7.jar").toURL())
//return new File(groovyx.net.http.HTTPBuilder.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
def http = new HTTPBuilder('http://httpbin.org/get')

As mentioned, you would be wise to use another method, such as Gradle's application plugin.
However, this is one way to do what you're asking.
First, to get the jar and all dependencies, consider the following Gradle build.gradle script:
apply plugin: 'java'
dependencies {
compile 'org.codehaus.groovy.modules.http-builder:http-builder:0.7'
}
repositories {
jcenter()
}
clean {
doLast {
ant.delete(dir: 'runtime')
}
}
task getDeps(type: Copy) {
from sourceSets.main.runtimeClasspath
into 'runtime/'
doFirst {
ant.delete(dir: 'runtime')
ant.mkdir(dir: 'runtime')
}
}
If you run gradle getDeps, it will write all of the jars into runtime.
Then, in a Unix terminal (for example), you could set the classpath with this (using wildcard syntax from Java 6+, and assuming the path is the same runtime as above):
export CLASSPATH=.:"/user/foo/some/path/runtime/*"
In the same terminal, this will work:
import groovyx.net.http.*
def http = new HTTPBuilder('http://httpbin.org/get')
println "Ready."

Related

Compile a groovy script with all it's dependencies which are managed by gradle and then run it as a standalone application via the command line

I have a simple groovy script with a single java library dependency:
package com.mrhacki.myApp
import me.tongfei.progressbar.ProgressBar
class Loading {
static void main(String[] arguments) {
List list = ["file1", "file2", "file3"]
for (String x : ProgressBar.wrap(list, "TaskName")) {
println(x)
}
}
}
I'm using gradle to manage the dependencies of the project. The gradle configuration for the project is pretty straightforward too:
plugins {
id 'groovy'
}
group 'com.mrhacki'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.11'
compile 'me.tongfei:progressbar:0.7.2'
}
If I run the script from the Intellij IDE, the script is executed as expected.
What I would like to do now is to compile the script with this dependency into one single .jar file, so I can distribute it as such, and run the application from any filesystem path, as the script logic will be dependent on the path from which the execution was called.
I've tried with a few gradle fat jars examples out there but none have worked for me since the .jar file is constantly throwing Could not find or load main class Loading when I try it to run.
If anyone would be so kind to give a hint or to show an example of a gradle task that would do a build that fits my described needs I would be very gratefull.
I'm aware of the groovy module Grape with the #Grab annotation too, but I would leave that as a last resort since I don't want the users to wait for the dependencies download, and would like to bundle them with the app.
I'm using groovy 2.5.6 and gradle 4.10 for the project
Thanks
You can simply create the fat-jar yourself, without any extra plugin, using the jar Task. For a simple/small project like yours, it should be straightforward :
jar {
manifest {
// required attribute "Main-Class"
attributes "Main-Class": "com.mrhacki.myApp.Loading"
}
// collect (and unzip) dependencies into the fat jar
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
EDIT : pleas take other comments into consideration: if you have more that one external lib you might have issues with this solution, so you should go for a solution with "shadow" plugins in this case.

How to make gradle download dependencies without actually building things

On a new environment gradle build takes quite a while because all dependencies have to be downloaded.
Is there a way to only download dependencies in order to speed up the following build?
That way we could for example already prefill a CI build environment.
Edit: Updated for Gradle 6+.
Some notes:
This new approach downloads jars into a folder, and then deletes the folder. So the result of having the jars in the Gradle cache is a side-effect.
It currently uses jars configured for the main source-set but could be generalized.
Even though it is neither efficient nor elegant, it can be useful if you actually want the jars (and transitive dependencies): simply comment-out the deletion of the runtime folder.
This solution can be handy when you want the jars (and transitive dependencies), as you simply have to comment-out deleting the folder.
Consider this build.gradle (as an arbitrary, concrete example):
apply plugin: 'java'
dependencies {
implementation 'org.apache.commons:commons-io:1.3.2'
implementation 'org.kie.modules:org-apache-commons-lang3:6.2.0.Beta2'
}
repositories {
jcenter()
}
task getDeps(type: Copy) {
from sourceSets.main.runtimeClasspath
into 'runtime/'
doFirst {
ant.delete(dir: 'runtime')
ant.mkdir(dir: 'runtime')
}
doLast {
ant.delete(dir: 'runtime')
}
}
Example run:
$ find /Users/measter/.gradle/caches -name "commons-io*1.3.2.jar"
$ gradle getDeps
$ find /Users/measter/.gradle/caches -name "commons-io*1.3.2.jar"
/Users/measter/.gradle/caches/modules-2/files-2.1/commons-io/commons-io/1.3.2/[snip]/commons-io-1.3.2.jar
I've found ./gradlew dependencies (as suggested by this user) to be very handy for Docker builds.
You can create a custom task that resolves all the configurations( in doing so, it will also download the dependencies without building the project)
task downloadDependencies {
doLast {
configurations.findAll{it.canBeResolved}.each{it.resolve()}
}
}
Run command ./gradlew downloadDependencies
My answer will favor the gradle plugins and built-in tasks.
I would use "gradle assemble" in the command-line.
It is a minor version of "gradle build".
This way, you may reduce the time of your preparations before running or building anything.
Check the link bellow for the documentation:
https://docs.gradle.org/current/userguide/java_plugin.html#lifecycle_tasks
In general, what is my recipe when I clone a new repository:
-gradle assemble
-do some coding
-gradle run (and basically test until done)
-gradle build (to make distributable files)
note: this last step may have adicional configurations for .jar files as outputs (depends on you).

How to create a debian distro for a java project using gradle

I see someone has asked this question before. I would have loved to have seen the answer, but it was removed. At the risk of getting down-voted like that post..., I really need help with this, as I've spent a few days on it already and I'm thoroughly at a loss...
I have a java project that's fairly mature. We're preparing to go from alpha phase into a beta release. As a part of that, we want to release installable packages with a proper app with an icon, etc. Creating a (dmg) package for distribution on Mac was extremely easy using the macAppBundle gradle plugin and it works beautifully. I'm now attempting to address distribution on Linux. Ideally, the setupbuilder plugin would be the way to go, but there's a bug that's preventing me from creating a .deb or .rpm package. I submitted the bug to the developer and am currently trying to work around the issue by following this blog post.
I am running an Ubuntu 16.04.3 vm in VirtualBox on my Mac and I can successfully create a working executable by running gradle debianPrepareappname. But when I try to run gradle debian to create the .deb file, the build always fails (currently with this error:).
Process 'command 'debuild'' finished with non-zero exit value 255
When I run debuild manually, I see the following:
debuild: fatal error at line 679:
found debian/changelog in directory
/home/username/appname/build/debian/appname
but there's no debian/rules there! Are you in the source code tree?
No rules file is getting created by gradle. I know that the rules file is basically a makefile... and I'm not very familiar with makefiles in general, let alone creating .deb distros. I know makefiles do compilations and copy files to places in the system, but I don't know what needs to be done to create a .deb file or where things need to go. I mean, the necessary components are there and they work:
appname/build/debian/appname/debian/{bin,lib}
The bin has the working executable and the lib has all the necessary jar files. I just don't know what I need to do in the gradle build script to create the .deb file. Here's what I've got in the gradle build file (I've omitted the macAppBundle and setupbuilder stuff that's just vestigial in there right now, just to keep it simple):
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'
defaultTasks "clean", "fatJar", "eclipse"
version = getVersionName()
sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
mavenCentral()
}
dependencies {
compile 'com.miglayout:miglayout-swing:5.0'
compile 'com.googlecode.plist:dd-plist:1.3'
compile 'org.freehep:freehep-graphicsio:2.4'
compile 'org.freehep:freehep-graphicsio-pdf:2.4'
compile 'org.freehep:freehep-graphicsio-ps:2.4'
compile 'org.freehep:freehep-graphicsio-svg:2.4'
compile 'org.freehep:freehep-graphics2d:2.4'
compile 'org.swinglabs.swingx:swingx-autocomplete:1.6.5-1'
}
sourceSets {
main {
java {
srcDir 'src/main/java/'
}
}
}
task fatJar(type: Jar) {
manifest {
attributes 'Main-Class':'com.placeholder.appname'
}
baseName = project.name + '-all'
from {configurations.compile.collect {it.isDirectory() ? it : zipTree(it)}}
with jar
}
def getVersionName() {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim()
}
String applicationVersionFull = getVersionName()
task debianClean(type: Delete) {
delete 'build/debian'
}
tasks.addRule("Pattern: debianPrepare<distribution>") { String taskName ->
if (taskName.startsWith("debianPrepare")) {
task(taskName, dependsOn: [installDist, debianClean]){
String debianDistribution = (taskName - "debianPrepare").toLowerCase()
String debianApplicationVersionFull = getVersionName()
doLast {
copy {
from rootProject.files("build/install/appname")
into rootProject.file("build/debian/appname")
}
copy {
from rootProject.files("gradle/debian/debian")
into rootProject.file("build/debian/appname/debian")
}
}
}
}
}
task debian { // depends on debianPrepare*
doLast {
exec {
workingDir rootProject.file("build/debian/appname")
commandLine "debuild -i -us -uc -b".split()
}
}
}
Everything I've read says this is supposed to be really easy with gradle. The macAppBundle was definitely very easy - it was like 5 lines of code. I barely had to read anything to figure it out and it creates a dmg that has an executable with an icon and everything. I just copied & edited the example in the macAppBundle readme. setupbuilder looked similarly easy, if not for the bug I encountered. Is there a similar example out there for building .deb packages for java projects that doesn't use setupbuilder? I've tried a couple other plugins with no success. I've been googling and I can't find anything straightforward other than the blog post I mentioned. I eventually would like to apply an icon to the executable and other niceties, but first thing is to just get it to build. So why does the rules file not get created? That seems like a good place to start.
I think what you're missing is a "debian" directory with all the related files already present. If you look at syncany's repo https://github.com/syncany/syncany/tree/74c737d871d21dff5283edaac8c187a42c020b20/gradle/debian/debian on github from the blog post you mentioned, you'll see he has 8 files.
At the end of the day, debuild is just bundling a set of files up into an installer. They all have to be there to begin with. His scripts don't create any of these files, just modify some such as the changelog.

Trouble moving tasks from build.gradle into separate .gradle file be reapplied

Running into problems extracting tasks from a build.gradle file to then be applied, back into the app/root build.gradle file. The compiler can resolve MarkupBuilder and JsonSlurper fine but cannot resolve the following: import org.apache.commons.lang.StringEscapeUtils.
I've tried adding it as a dependency within the newly created script and also within the app and project levels.
'org.apache.commons.lang:commons-lang:3.5'
The error is below
Could not compile script '/project/app/newscript.gradle'.
startup failed:
> script '/project/app/newscript.gradle': 18: unable to resolve class org.apache.commons.lang.StringEscapeUtils
# line 18, column 1.
import org.apache.commons.lang.StringEscapeUtils
^
1 error
Am I doing something wrong or is this not possible? Would I need to include the script in a different way than apply script: newscript.gradle or another plugin within the newscript.gradle?
A Gradle script is basically a Groovy file. Which in turn gets compiled into JVM bytecode, similar to Java classes. So when compiling a script with an import, the imported classes must be on the classpath. Some classes like the MarkupBuilder are available by default (included either by Groovy or Gradle).
You have to add something like this to be able to use the classes in your script:
import org.apache.commons.lang.StringEscapeUtils;
buildscript {
repositories {
mavenCentral();
}
dependencies {
classpath 'org.apache.commons.lang:commons-lang:3.5'
}
}
The buildscript closure will add the library on the classpath of the Gradle script and you should be able to use its classes.

Gradle exclude java class from lib replaced by own class to avoid duplicate

In Android Studio, there is a specific file (src/org/luaj/vm2/lib/jse/JavaMethod.java) that I need to overwrite from a package that is pulled in via Gradle (dependencies {compile 'org.luaj:luaj-jse:3.0.1'}).
I copied the file into my source directory with the exact same path and made my changes to it. This was working fine for an individual JUnit test case that was using it. It also looks like it is working for a normal compile of my project (unable to easily confirm at the moment).
However, when I try to run all my tests at once via a configuration of ProjectType="Android Tests", I get Error:Error converting bytecode to dex:
Cause: com.android.dex.DexException: Multiple dex files define Lorg/luaj/vm2/lib/jse/JavaMethod$Overload;.
Is there a specific task or command that I need to add to my Gradle file to make sure the project selects the file in my local source directory? I tried the Copy task and the sourceSets->main->java->exclude command, but neither seemed to work (I may have done them wrong). I also tried the "exclude module/group" directive under "compile" from this post.
The non-default settings for the Run/Debug Confirmation:
Type=Android Tests
Module=My module
Test: All in package
Package: "test"
All my JUnit test cases are in the "test" package.
Any answer that gets this to work is fine. If not Gradle, perhaps something in the android manifest or the local source file itself.
[Edit on 2016-07-24]
The error is also happening on a normal compile when my android emulator is running lower APIs. API 16 and 19 error out, but API 23 does not.
issue: when linking your app the linker finds two versions
org.luaj:luaj-jse:3.0.1:org.luaj.vm2.lib.jse.JavaMethod and
{localProject}:org.luaj.vm2.lib.jse.JavaMethod
howto fix: tell gradle to exclude org.luaj:luaj-jse:3.0.1:org.luaj.vm2.lib.jse.JavaMethod from building
android {
packagingOptions {
exclude '**/JavaMethod.class'
}
}
I have not tried this with "exclude class" but it works for removing duplicate gpl license files a la "COPYING".
If this "exclude" does not work you can
download the lib org.luaj:luaj-jse:3.0.1 to the local libs folder,
open jar/aar with a zip-app and manually remove the duplicate class.
remove org.luaj:luaj-jse:3.0.1 from dependencies since this is now loaded from lib folder
I am not completely sure I understand your problem; however, it sounds like a classpath ordering issue, not really a file overwrite one.
AFAIK, gradle does not make a 'guarantee' on the ordering from a 'dependencies' section, save for that it will be repeatable. As you are compiling a version of file that you want to customize, to make your test/system use that file, it must come earlier in the classpath than the jar file it is duplicated from.
Fortunately, gradle does allow a fairly easy method of 'prepending' to the classpath:
sourceSets.main.compileClasspath = file("path/to/builddir/named/classes") + sourceSets.main.compileClasspath
I don't know enough about your system to define that better. However, you should be able to easily customize to your needs. That is, you can change the 'compile' to one of the other classpath (runtime, testRuntime, etc) if needed. Also, you can specify the jarfile you build rather than the classes directory if that is better solution. Just remember, it may not be optimal, but it is fairly harmless to have something specified twice in the classpath definition.
This is rather convoluted but it is technically feasible. However it's not a single task as asked by the poster:
Exclude said dependency from build.gradle and make sure it's not indirectly included by another jar (hint: use ./gradlew dependencies to check it)
create a gradle task that downloads said dependency in a known folder
unpack such jar, remove offending .class file
include folder as compile dependency
If it's safe to assume that you're using Linux/Mac you can run a simple command line on item 3, it's only using widely available commands:
mkdir newFolder ; cd newFolder ; jar xf $filename ; rm $offendingFilePath
If you don't care about automatic dependency management you can download the jar file with curl, which I believe to be widely available on both linux and mac.
curl http://somehost.com/some.jar -o some.jar
For a more robust implementation you can substitute such simple command lines with groovy/java code. It's interesting to know that gradle can be seen as a superset of groovy, which is arguable a superset of java in most ways. That means you can put java/groovy code pretty much anywhere into a gradle.build file. It's not clean but it's effective, and it's just another option.
For 4 you can have something along either
sourceSets.main.java.srcDirs += ["newFolder/class"]
at the root level of build.gradle, or
dependencies {
. . .
compile fileTree(dir: 'newFolder', include: ['*.class'])
. . .
This is what I ended up adding after Fabio's suggestion:
//Get LUAJ
buildscript { dependencies { classpath 'de.undercouch:gradle-download-task:3.1.1' }}
apply plugin: 'de.undercouch.download'
task GetLuaJ {
//Configure
def JARDownloadURL='http://central.maven.org/maven2/org/luaj/luaj-jse/3.0.1/luaj-jse-3.0.1.jar' //compile 'org.luaj:luaj-jse:3.0.1'
def BaseDir="$projectDir/luaj"
def ExtractToDir='class'
def ConfirmAlreadyDownloadedFile="$BaseDir/$ExtractToDir/lua.class"
def JarFileName=JARDownloadURL.substring(JARDownloadURL.lastIndexOf('/')+1)
def ClassesToDeleteDir="$BaseDir/$ExtractToDir/org/luaj/vm2/lib/jse"
def ClassNamesToDelete=["JavaMethod", "LuajavaLib"]
//Only run if LuaJ does not already exist
if (!file(ConfirmAlreadyDownloadedFile).exists()) {
//Download and extract the source files to /luaj
println 'Setting up LuaJ' //TODO: For some reason, print statements are not working when the "copy" directive is included below
mkdir BaseDir
download {
src JARDownloadURL
dest BaseDir
}
copy {
from(zipTree("$BaseDir/$JarFileName"))
into("$BaseDir/$ExtractToDir")
}
//Remove the unneeded class files
ClassNamesToDelete=ClassNamesToDelete.join("|")
file(ClassesToDeleteDir).listFiles().each {
if(it.getPath().replace('\\', '/').matches('^.*?/(?:'+ClassNamesToDelete+')[^/]*\\.class$')) {
println "Deleting: $it"
it.delete()
}
}
}
}
I'll upload a version that works directly with the jar later.
Another solution if we got then source jar:
task downloadAndCopy {
def downloadDir = "${buildDir}/downloads"
def generatedSrcDir = "${buildDir}/depSrc"
copy {
from(configurations.detachedConfiguration(dependencies.add('implementation', 'xxx:source')))
file(downloadDir).mkdirs()
into(downloadDir)
}
println("downloading file into ${downloadDir}")
fileTree(downloadDir).visit { FileVisitDetails details ->
if (!details.file.name.endsWith("jar")) {
println("ignore ${details.file.name}")
return
}
println("downloaded ${details.file.name}")
def srcFiles = zipTree(details.file).matching {
include "**/*.java"
exclude "**/NeedEclude*java"
}
srcFiles.visit {FileVisitDetails sourceFile ->
println("include ${sourceFile}")
}
copy {
from(srcFiles)
into(generatedSrcDir)
}
}
}
and remember to add depSrc to srcDirs
android {
sourceSets {
`main.java.srcDirs = ['src/main/java', "${buildDir}/depSrc"]
}
}

Categories

Resources