I'm trying to use log4j in my java core console application. In the documentation there is sample code on how to add Gradle dependency for log4j:
https://logging.apache.org/log4j/2.x/maven-artifacts.html
dependencies {
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.14.1'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.14.1'
}
I pasted it to my build.gradle file and wrote sample code for running the code.
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Program {
private static final Logger logger = LogManager.getLogger("HelloWorld");
public static void main(String[] args) {
logger.error("Just a test error entry");
}
}
along with that I also added manifest in order to run the code from the console.
jar {
manifest {
attributes "Main-Class": "Program"
}
}
If I build it with Gradle - everything is built successfully. But if I try to run java -jar from my build directory then I got
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/logging/log4j/LogManager
at Program.<clinit>(Program.java:5)
Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.LogManager
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:636)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:182)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:519)
... 1 more
I found 1 workaround: modify to jar{} section:
jar {
manifest {
attributes "Main-Class": "Program"
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
and it started working, I even try to zip .jar file and saw that there is log4j dependency added.
But what is I don't want transitive dependency on log4j? If I set implementation group instead of compile group it doesn't work again. Is there any complete example of using log4j?
On top of that, one thing to point, while it is not working with java -jar <.jar>, for some reason it still works in Intellij idea. Could someone explain me why?
The problem you are encountering is caused by a problem in the application's classpath, when you launch the application (cf. What is a classpath and how do I set it?).
Gradle has many options to help you with setting the classpath right. Since you don't mention any Gradle version, I'll use version 7.1 in the examples. Assume you have this build.gradle file in a project named appName:
plugins {
id 'application'
}
dependencies {
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.14.1'
runtimeOnly group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.14.1'
}
application {
mainClass = 'com.example.App'
}
(log4j-core is not necessary to compile your code, so it is in the runtimeOnly dependency configuration, cf. dependency configurations)
Manually setting the classpath
If you generate a plain JAR file (execute the jar task and look in build/libs) you can execute the application with:
java -cp log4j-api-2.14.1.jar:log4j-core-2.14.1.jar:appName.jar com.example.App
(I assume all jars are in the current directory; the path separator is system specific, on Windows it is ;)
This becomes easily difficult to get right, therefore the application Gradle plugin generates a shell and a batch script to help you: execute the installDist task and look into build/install/appName (or you can use the distZip or distTar tasks for a compressed version of this folder). Setting up the correct command is reduced to calling:
bin/appName
Hardcoding the classpath in the Manifest
You can also hardcode the dependencies needed in the JARs manifest file. In Gradle you can do it with:
jar {
manifest {
attributes(
'Main-Class': 'com.example.App',
'Class-Path': configurations.runtimeClasspath.collect { it.name }.join(' ')
)
}
}
to obtain a manifest file with all the information needed to start the application:
Main-Class: pl.copernik.gradle.App
Class-Path: log4j-core-2.14.1.jar log4j-api-2.14.1.jar
Notice the usage of runtimeClasspath, which is a configuration that contains both the runtimeOnly and implementation dependency configurations needed to start the application.
If the dependency jars are in the same folder as appName.jar, you can run the application with:
java -jar appName.jar
Spring Boot loader
If you want to have all dependencies in a single JAR, you can use the Spring Boot Gradle plugin instead of the application plugin:
plugins {
id 'java'
id 'org.springframework.boot' version '2.5.3'
}
The bootJar task will produce an appName.jar resembling in its structure the appName.zip file produced by the distZip file. However the shell/batch scripts are replaced with Java code, so you can call:
java -jar appName.jar
Fat Jar
What you did in your question with:
jar {
manifest {
attributes "Main-Class": "com.example.App"
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
is usually called fat JAR. Basically it is produced by unzipping your jar file and all dependencies into a single folder and zipping it back together.
This works, but has some disadvantages: e.g. you might lose licence information on the dependencies (which might be a licence violation) and you will not be able to replace the dependencies with new versions without recompiling. See this question for an example of an absurdly large fat jar.
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) }
}
}
I have set up an OpenJDK 12 project in IntelliJ (2019.2) using the built-in Gradle support. To design the GUI I'm using JavaFX 12. I have followed and read the setup guide several times, I have no issues running the program in my IDE, the issue is when I try to build a .jar file for distribution that I run into problems. I have not been able to find a solution that works so far and I've searched QUITE a lot, nearly tearing my hair out over this. Currently when i try to run my jar file with java -jar "jarName".jar I get the following error:
Exception in thread "main" java.lang.NoClassDefFoundError: javafx/application/Application
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:802)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:700)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:623)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at com.CAM.Starter.main(Starter.java:6)
Caused by: java.lang.ClassNotFoundException: javafx.application.Application
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 10 more
I have tried moving my main class to a separate one that doesn't extend Application, which is what gives the above error. Without moving my Main class I get a different error.
My build.gradle looks like this currently:
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.8'
}
group 'ClassicAddonManager'
version '0.2'
sourceCompatibility = 11
repositories {
mavenCentral()
}
javafx {
version = "12.0.2"
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile 'net.sourceforge.htmlunit:htmlunit:2.13'
compile group: 'com.google.code.gson', name: 'gson', version: '2.7'
compile group: 'net.lingala.zip4j', name: 'zip4j', version: '1.2.4'
}
jar {
manifest {
attributes 'Main-Class': 'com.CAM.Starter'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
If I have my main method extending Application then I get an error stating that my main class could not be found even though I can see it is present in the generated .jar file.
All I'm trying to do, is to generate a file that a friend with no knowledge of programming could run. Ideally, a file that they could run without having to install Java first. I know it should be possible to do this, but Gradle is new to me so I'm not sure if that is what is causing all this headache. Is there potentially an easier way to deploy? Especially given that this is a solo-project?
EDIT
I have tried the modular part of the guide. Doing that I have over 100 error when attempting to build. They are all something in the vein of:
javafx.graphicsEmpty reads package org.xml.sax from both xml.apis and java.xml
If you want to do a fat jar using Gradle but not a shadow plugin, usually you will do:
jar {
manifest {
attributes 'Main-Class': 'com.CAM.Starter'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
However, there is an important fact: compile is deprecated, and the JavaFX plugin uses implementation by default.
As a consequence, configuration.compile might be empty, or at least, it won't contain the JavaFX classes.
The solution is to check the runtimeClasspath configuration, as we will have all the classes there, and we can make the fat jar:
jar {
manifest {
attributes 'Main-Class': 'com.CAM.Starter'
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
This is explained in the OpenJFX docs, https://openjfx.io/openjfx-docs/#modular, section non-modular projects, subsection gradle.
Once you have your fat jar, you can run it with:
java -jar yourFatJar.jar
Note that double-clicking on it won't work, as explained in detail here, so it can be convenient to create a small batch instead.
A better solution is to do a modular project and use jlink to create a runtime image (it also includes a launcher script). You can distribute this image to other users that don't have even JDK installed. With gradle, you can just include the 'org.beryx.jlink' plugin, like in this sample.
And you can also use the early version of jpackage to create and distribute an installer.
I've problem with JXBrowser license file. I've followed https://jxbrowser-support.teamdev.com/docs/quickstart/gradle-config.html official guide to use with Gradle and this is my current build.Gradle
Build seem normal and no any error.
plugins {
id 'java'
}
sourceSets {
main {
java {
srcDir 'src'
}
}
test {
java {
srcDir 'test'
}
}
}
jar {
manifest {
attributes 'Main-Class': 'main.Main'
}
}
version '1.0'
sourceCompatibility = 1.8
repositories {
mavenCentral()
maven { url = 'http://maven.teamdev.com/repository/products' }
}
ext {
jxBrowserVersion = '6.22'
}
dependencies {
compile group: 'org.postgresql', name: 'postgresql', version: '42.2.5'
compile "com.teamdev.jxbrowser:jxbrowser-cross-platform:${jxBrowserVersion}"
compile 'com.maxmind.geoip2:geoip2:2.12.0'
compile files("$rootDir/license.jar")
testCompile group: 'junit', name: 'junit', version: '4.12'
}
Error happens when it run I'm not sure my build.Gradle is wrong or something here is error
Exception in thread "main" java.lang.NoClassDefFoundError: com/teamdev/jxbrowser/chromium/PermissionHandler
at main.Main.main(Main.java:8)
Caused by: java.lang.ClassNotFoundException: com.teamdev.jxbrowser.chromium.PermissionHandler
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 1 more
I suppose you see this exception when you run your Java application through an executable JAR file.
This exception indicates that you didn't include JxBrowser JAR files into your application class path. I see that you include JxBrowser JAR files as compile dependencies into your build.gradle. It's OK during build, but not for production.
Please make sure that you add JxBrowser JAR files into the classpath of your application. For example, you can download the required JAR files, put them into some directory, and configure the Class-Path attribute in your JAR file as show in the Oracle's tutorial.
Or you can use the Gradle plugin that includes all dependencies into a single fat JAR. In this case you don't need to configure Java app classpath.
Now it's work using JDK & JRE < 10
I am actually Trying to use OpenCV in Java based Gradle Project. Since, OpenCV needs native library and Jar File for execution. I am trying to wrap native library and Jar together using gradle, but I am facing errors in doing so.
When I try to run project, project is not able to find native library for opencv jar and giving me below error
Exception in thread "main" java.lang.UnsatisfiedLinkError: no opencv_java340 in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867) at java.lang.Runtime.loadLibrary0(Runtime.java:870) at java.lang.System.loadLibrary(System.java:1122) at Library.(Library.java:9)
Although, I know how to set native library manually in Gradle project but I am not sure how to do it via Gradle and wrap native library in fat jar. Here is my build.gradle
// Apply the java-library plugin to add support for Java Library
apply plugin: 'java-library'
// In this section you declare where to find the dependencies of your project
repositories {
// Use jcenter for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
}
configurations {
// configuration that holds jars to include in the jar
openCVLibs
}
dependencies {
openCVLibs fileTree(dir: 'libs', include: '*.jar')
openCVLibs fileTree(dir: 'libs', include: '*.so')
configurations.compile.extendsFrom(configurations.openCVLibs)
}
jar {
from {
configurations.openCVLibs.collect { it.isDirectory() ? it : zipTree(it) }
}
manifest {
attributes('Implementation-Title': project.name,
'Implementation-Version': project.version)
}
}
have also included link of sample eclipse project
So Here is edit
Based on #kerry's comment I tried to crate mvn artifact following openCV Maven, but now I am facing following error while creating mvn build
[ERROR] Failed to execute goal
org.codehaus.mojo:properties-maven-plugin:1.0.0:read-project-properties
(set-arch-properties) on project opencv: Properties could not be
loaded from File: /media/nitish/8EE47633E4761E21/opencv-3.4.0/build/build.properties -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to
execute goal
org.codehaus.mojo:properties-maven-plugin:1.0.0:read-project-properties
(set-arch-properties) on project opencv: Properties could not be
loaded from File: /media/nitish/8EE47633E4761E21/opencv-3.4.0/build/build.properties
There is no build.properties file present in build folder. Since build folder is created by maven task only, so build.properties file should be created by maven only.
Following there is a working example of a build.gradle file. Make sure to read the comments and make changes when appropriate.
By running gradle fatJar you can create a working Java Jar of your application with OpenCV inside.
However, apart form including your OpenCV library in your Java Jar, you will need to load the OpenCV native file at the beginning of your code.
They are two ways to do that:
1) Load the file by providing the full path:
System.load("my/full/path/opencv.dll");
2) If your native file is located inside your Java Library Path:
System.loadLibrary("opencv");
Take notice that in the second case you only need to provide the name of your native file (without its extension).
The default Java Library Path depends on OS:
On Windows, it maps to PATH
On Linux, it maps to LD_LIBRARY_PATH
On OS X, it maps to DYLD_LIBRARY_PATH
If you want to set your own Java Library Path:
try {
System.setProperty("java.library.path","YOUR/PATH");
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
} catch (Exception ex) {
System.out.println("Failed to set Java Library Path: " + ex.getMessage);
}
build.gradle
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'application'
mainClassName = "YourMainClass" // You Main Class name
repositories{
mavenCentral()
}
task fatJar(type: Jar) {
manifest.from jar.manifest
classifier = 'all'
from {
configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) }
} {
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
exclude "build/libs/philipath/**"
}
with jar
}
artifacts {
archives fatJar
}
dependencies {
// Local libraries
compile fileTree('lib') // assuming there is a folder named 'lib' in your project root with your OpenCV jar inside
// other dependencies
}
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.