I'm using the approach from Gradle - extract file from depended jar to extact a .so file from inside a native JAR.
configurations {
special
}
dependencies {
special('org.jogamp.jogl:jogl-all:2.3.2:natives-linux-i586')
}
task extract(type: Copy) {
from({ zipTree(configurations.special.singleFile) })
include 'natives/linux-i586/*.so'
into "$buildDir/extracted"
}
This works fine, however it appears to break compilation of code that depends on org.jogamp.jogl:jogl-all:2.3.2, the non-native Java part.
TestJogl.java:1: error: package com.jogamp.opengl does not exist
import com.jogamp.opengl.GL;
The compilation fails if the project is built with clean extract build but not clean build
I've simplified the code to
import com.jogamp.opengl.GL;
public class TestJogl {
private GL gl;
}
and corresponding build.gradle
apply plugin: "java"
dependencies {
compile "org.jogamp.jogl:jogl-all:2.3.2"
}
I've isolated this issue to the usage of "flatDir" repo. The exact same project compiles fine when using mavenCentral(). Note using a legacy corporate network without artifactory or direct Internet access.
allprojects {
repositories {
flatDir {
dirs "$rootProject.projectDir/local-repo"
// contains jogl-all-2.3.2-natives-linux-i586.jar
// jogl-all-2.3.2.jar
}
}
}
I've managed to work around the issue by changing the dependency to explicity specify #jar, which should be implicit
compile "org.jogamp.jogl:jogl-all:2.3.2#jar"
The same problem occurs in both single and multi project layouts.
My analysis: This is a bug in Gradle. Somehow when using flatDir Gradle gets confused and thinks that the dependency has been setup, but uses the native JAR instead of the Java JAR.
Questions: Am I doing something wrong? Is this a bug? Is there another way to workaround it?
Environment: Gradle 3.5, JDK 1.8u144
Related
I've tried everything by now, so I hope someone in here can tell me more...
Im trying to produce an executable .jar from a IntelliJ Gradle JavaFX project. I used the standard setup that IntelliJ provided, I changed the Gradle.build file however.
The new file I got from here: Non-Modular Gradle (openjfx.io)
I have a main class that has some basic code in it and a launcher class that does not extend Application and is specified as the Main class in the jar manifest.
For now I only use javafx.controls and basically everything is as the example they provided here.
When doing the ./gradlew jar command I get the error:
no module-info.java found
Which - as I understand - is not required if I use the Non-Modular approach?
However if I add it I get the error:
Entry module-info.class is a duplicate but no duplicate handling strategy has been set.
I tried every other option out there, all of them lead to either the 2. error or the jar was produced but not executable due to the fact that it can't find the Application class...
Any help is greatly appreciated.
I just want to point out that I've never really used Gradle before and have never formally learned any coding, but can fiddle my way around usually.
For the sake if it my build file:
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.9' // this is old right?
}
repositories {
mavenCentral()
}
dependencies {
/* uncomment for cross-platform jar: */
runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:win"
runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:linux"
runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:mac"
}
javafx {
version = "16"
modules = [ 'javafx.controls' ]
}
mainClassName = 'main.class.with.Code'
jar {
manifest {
attributes 'Main-Class': 'main.class.with.Launcher'
}
from {
// this is what causes the module duplicate error I think (at least it did in my other tries)
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
This is currently my build.gradle file (I removed unnecessary task references, which are not only working perfectly fine, but are irrelevant to the current question):
buildscript {
repositories {
jcenter()
maven {
name = 'forge'
url = 'https://files.minecraftforge.net/maven'
}
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:2.1-SNAPSHOT'
}
}
apply plugin: 'net.minecraftforge.gradle.forge'
apply plugin: 'java'
dependencies {
compile('some.library:here:1.2.3') // no extra modules
compile('other.library:here:4.5.6') // multiple modules
}
jar {
from project.configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
Currently I have a system which will automatically pack all the dependencies into my jar file. The main issue I have is that if another mod or file loads the same library before my mod, I will end up with a class loading problem, because I'll be loading the same class twice (one from another jar, and one from my own).
With that being said, is it possible to avoid such an issue? How would I realistically account for such a thing? Does forge perhaps have a method to control class loading to the extent that duplicate libraries will not be loaded, hence there will be no class conflicts?
Minecraft Forge supports a feature called shading to deal with this problem. When you shade a jar, it's integrated into your jar, but the package is renamed to something unique (along with all references to it from your code).
Source: https://github.com/MinecraftForge/ForgeGradle/blob/FG_1.2/docs/user-guide/shading.md
So I have a multi project setup that looks something like this
Root Project
--> common
--> project1
--> project2
--> 3rd_party_api
So common obviously contains a bunch of code shared across the other projects. Projects 1 and 2 are fine because they are wars and contain the common jar file as a dependency without any issues.
The problem I have is with my 3rd_party_api project. This is quite a small jar file that we will be delivering to other teams so that they can integrate with our code. Most of the java code required is contained in this project folder however there are 3 or 4 classes that are in the common project and need to be included in this library. Because it has to be standalone I need to wrap those classes in the jar file.
I have tried various iterations of srcDirs and source but I can't for the life of me figure out an easy way to do this.
As I've said I've looked at different approaches but my latest attempt looked a bit like this:
project(':api') {
defaultTasks 'jar'
apply plugin: 'java'
sourceSets {
main {
java {
srcDir 'src/main/java'
srcDir fileTree(dir: '../common/src/main/java').matching { include 'com/my/classes/**' }
}
}
dependencies {
compile project(':common')
}
}
This compiles successfully but the extra classes from common are not included in the jar.
Any help would be greatly appreciated.
Thanks Dirk,
Didn't quite work for me but got me thinking about other approaches. I eventually got it to do what I needed using a custom jar task. Something like the following:
defaultTasks 'lib'
dependencies {
compile project(':common')
}
task lib( type: Jar, dependsOn: classes) {
from sourceSets.main.output
from (project(':common').sourceSets.main.output) {
include 'com/myclasses/stuff/**'
include 'com/specificclass/MyClass.class'
}
}
maybe something like:
jar {
baseName = 'yourJarFileName'
from('path/to/your/dir/') {
include 'local/path/from/there/**/*.jar'
}
}
didn't checked this ... so don't blame me if it does not work out of the box ;)
other possibility would be to define your custom sourceSets ... but never tried this either.
I'm using gradle as the JavaFX plugin.
Everything works perfectly even after building and runnig the excecutable at distribution/, except with one class: CloseableHttpClient
For several purposes I create the following object like this:
CloseableHttpClient client = HttpClients.createDefault();
Running the program in the IDE is no problem, everything works fine. But if I build and try to run the .exe-File I get the following Throwable-StackTrace:
java.lang.NoClassDefFoundError: Could not initialize class org.apache.http.conn.ssl.SSLConnectionSocketFactory
at org.apache.http.impl.client.HttpClientBuilder.build(HttpClientBuilder.java:955)
at org.apache.http.impl.client.HttpClients.createDefault(HttpClients.java:58)
at ch.itcb.tools.lom.util.JsonSimpleUtil.http(JsonSimpleUtil.java:29)...
I really don't understand that. How can it be that just this class doesn't get found, but all my other classes do?
My build.gradle file:
apply plugin: 'java'
apply plugin: 'eclipse'
apply from: 'javafx.plugin'
sourceCompatibility = 1.8
version = '0.1'
jar {
manifest {
attributes 'Implementation-Title': 'LogoffManager',
'Implementation-Version': version
}
}
repositories {
mavenCentral()
}
dependencies {
compile fileTree(dir: 'lib', include: ['*.jar'])
compile 'ch.qos.logback:logback-classic:1.1.3'
compile 'org.apache.httpcomponents:httpclient:4.5.1'
compile 'com.googlecode.json-simple:json-simple:1.1'
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
}
test {
systemProperties 'property': 'value'
}
uploadArchives {
repositories {
flatDir {
dirs 'repos'
}
}
}
Please write a comment if you need more information. Thx.
it's a good question, which I came across just now while researching examples of the many ways Java developers can end up with class path fun :-)
I started with a minimal version of your build.gradle (including only what's directly relevant), specifically:
plugins {
id 'java'
}
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
jar {
manifest {
attributes 'Main-Class': 'com.oliverlockwood.Main'
}
}
dependencies {
compile 'org.apache.httpcomponents:httpclient:4.5.1'
}
My 'Main' class, in this context, uses your code example, i.e.:
package com.oliverlockwood;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
public class Main {
public static void main(String[] args) {
CloseableHttpClient client = HttpClients.createDefault();
}
}
At this stage, I can run gradle clean build followed by java -jar build/libs/33106520.jar (my project was named after this StackOverflow question) and I see this:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/impl/client/HttpClients
at com.oliverlockwood.Main.main(Main.java:8)
Caused by: java.lang.ClassNotFoundException: org.apache.http.impl.client.HttpClients
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
This is subtly different from your error, but before we dig and reproduce that, let me emphasise something: both this error and the one you're seeing are caused at runtime when the classloader is unable to find a class that it needs. There's quite a good blog post here with some more details about the difference between compile-time classpath and runtime classpaths.
If I run gradle dependencies I can see the runtime dependencies for my project:
runtime - Runtime classpath for source set 'main'.
\--- org.apache.httpcomponents:httpclient:4.5.1
+--- org.apache.httpcomponents:httpcore:4.4.3
+--- commons-logging:commons-logging:1.2
\--- commons-codec:commons-codec:1.9
I added these manually one-by-one to my runtime classpath. (For the record, this isn't generally considered good practice; but for the sake of the experiment, I copied these jars to my build/libs folder and ran with java -cp build/libs/33106520.jar:build/libs/* com.oliverlockwood.Main. Interestingly enough, this wasn't able to reproduce your exact problem. To recap:
Without org.apache.httpcomponents:httpclient available at runtime, then we fail because the HttpClients jar is not found.
With org.apache.httpcomponents:httpclient:4.5.1 available at runtime, then your problem does not manifest - and I note that the class your build fails to find (org.apache.http.conn.ssl.SSLConnectionSocketFactory) is part of this same Apache library, which is very suspicious indeed.
My suspicion is then that your runtime classpath contains a different version of the Apache httpclient library. Since there's a whole lotta versions out there, I'm not going to test every single combination, so I will instead leave you with the following advice.
If you want to fully understand the root cause of your issue, then identify exactly which jars (including their versions) are present in your error-case runtime classpath, including any jars that are packaged inside yours if you're creating a fat jar (more on this in point 3). It'd be great if you shared these details here; root cause analysis usually helps everyone to understand better :-)
Where possible, avoid using dependencies in the manner of compile fileTree(dir: 'lib', include: ['*.jar']). Managed dependencies based on a repository such as Maven or JCenter are much easier to work with consistently than dependencies in a random directory. If these are internal libraries that you don't want to publish to an open-source artifact repository, then it may be worth setting up a local Nexus instance or similar.
Consider producing a "fat jar" instead of a "thin jar" - this means that all runtime dependencies are packaged in the jar that you build. There's a good Shadow plugin for Gradle that I'd recommend - with this in place in my build.gradle, and running gradle clean shadow, I was able to run java -jar just fine without needing to manually add anything to my classpath.
For Spring boot users, this can be solved with one line of code. I am using Gradle/Kotlin, so:
id("org.springframework.boot") version "2.5.5"
inside the plugins {} section of your build.gradle.kts
For more information visit the Spring Boot Gradle Plugin Reference Guide.
For my case, I turned on my InteliJ after 3 months, got some runtime errors like noclassdeffounderror. I have to *** refresh gradle ***, then the errors are gone.
I've used the Intellij UI Designer to create forms for a project. Everything works fine when I'm building with idea as it handles compiling the forms for me, but as we recently switched to using Gradle for building it hasn't been possible to produce an executable jar file yet.
My google-fu has led me to several posts that explains that an ant script is needed to compile (eg link, link2, link3 ,and the one i ended on following: link4)
My project is a multi-module setup.
root build.gradle
subprojects {
apply plugin: 'java'
apply plugin: 'idea'
repositories {
mavenCentral()
}
}
supproject build.gradle
apply plugin:'application'
mainClassName = "dk.OfferFileEditor.OfferFileEditorProgram"
configurations {
antTask
}
dependencies {
compile 'org.json:json:20140107'
compile project(":Shared:HasOffers Api")
//dependencies for java2c
antTask files('../../lib/javac2-13.1.1.jar', '../../lib/asm4-all-13.1.1-idea.jar', '../../lib/forms_rt-13.1.1.jar')
antTask group: 'org.jdom', name: 'jdom', version: '1.1'
}
task compileJava(overwrite: true, dependsOn: configurations.compile.getTaskDependencyFromProjectDependency(true, 'jar')) {
doLast {
println 'using java2c to compile'
project.sourceSets.main.output.classesDir.mkdirs()
ant.taskdef name: 'javac2', classname: 'com.intellij.ant.Javac2', classpath: configurations.antTask.asPath
ant.javac2 srcdir: project.sourceSets.main.java.srcDirs.join(':'),
classpath: project.sourceSets.main.compileClasspath.asPath,
destdir: project.sourceSets.main.output.classesDir,
source: sourceCompatibility,
target: targetCompatibility,
includeAntRuntime: false
}
}
But even though the compilation is successfull, a Nullpointer exception is thrown the first time I try to access one of the fields the UI Designer created. So something is not being compiled correctly.
I'm probably missing some setting, but after unsuccesfully pouring several hours into forums and google I still haven't found any solution.
So I made this a lot more complicated than needs be.
To make it work you need to change two things in your project.
A setting in IDEA 13.1.5
Settings -> GUI Designer -> Generate GUI into: Java source code
This makes IntelliJ IDEA add 3 methods into the bottom of your forms:
$$$setupUI$$$()
$$$setupUI$$$()
$$$getRootComponent$$$()
If they are missing try recompiling your project after you change the setting.
Add the missing classes
Intellij has a jar called forms_rt.jar, and I found mine in {IntelliJ IDEA Root}\lib. And renamed it to "forms_rt-13.1.1.jar"
This needs to be included during compile time to your project. If you are using Gradle as I did you could copy it to {project root}/lib and add a flatfile repository like so:
repositories {
mavenCentral()
flatDir dirs: "${rootDir}/lib"
}
After that you need to include it in your project gradle file:
dependencies {
compile name: 'forms_rt', version: '13.1.1'
}
After that it should be possible to build it both in IntelliJ IDEA and Gradle.
IntelliJ IDEA 2019.1
I found this issue still exists. It's at least somehow documented now:
If your build actions are delegated to Gradle, GUI Designer will not generate Java source code.
So by disabling the according setting
Build, Execution, Deployment | Build Tools | Gradle | Runner | Delegate IDE build/run actions to gradle
I was able to build and run the project successfully. Note that I didn't need any other settings or additional libraries from the answers above. I let Generate GUI into be set to Binary class files.
The forms_rt library is in mavenCentral.
http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22forms_rt%22
Once you have configured IntelliJ to update the SourceCode it is sufficient to just add the library to the dependencies in your build.gradle.
dependencies {
compile 'com.intellij:forms_rt:7.0.3'
}
Idea 2019.2
It seems like IntelliJ changed the settings UI when updating from 2019.1 to 2019.2, as the menu entry mentioned by Tom isn't there anymore.
I got it fixed by setting Build and run using: to IntelliJ Idea. I also changed Run tests using: to IntelliJ Idea to avoid problems while testing.
Both settings are located under File | Settings | Build, Execution, Deployment | Build Tools | Gradle.
I figured out an updated version of the gradle build workaround for a new project - https://github.com/edward3h/systray-mpd/blob/master/build.gradle
Probably won't use the form designer again though.
These are the relevant parts:
repositories {
mavenCentral()
maven { url "https://www.jetbrains.com/intellij-repository/releases" }
maven { url "https://jetbrains.bintray.com/intellij-third-party-dependencies" }
}
configurations {
antTask
}
dependencies {
implementation 'com.jetbrains.intellij.java:java-gui-forms-rt:203.7148.30'
antTask 'com.jetbrains.intellij.java:java-compiler-ant-tasks:203.7148.30'
}
task compileJava(type: JavaCompile, overwrite: true, dependsOn: configurations.compile.getTaskDependencyFromProjectDependency(true, 'jar')) {
doLast {
project.sourceSets.main.output.classesDirs.each { project.mkdir(it) }
ant.taskdef name: 'javac2', classname: 'com.intellij.ant.Javac2', classpath: configurations.antTask.asPath
ant.javac2 srcdir: project.sourceSets.main.java.srcDirs.join(':'),
classpath: project.sourceSets.main.compileClasspath.asPath,
destdir: project.sourceSets.main.output.classesDirs[0],
source: sourceCompatibility,
target: targetCompatibility,
includeAntRuntime: false
}
}
The dependency versions for jetbrains libraries are found via https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html?from=jetbrains.org#using-intellij-platform-module-artifacts and https://www.jetbrains.com/intellij-repository/releases/