I am new to obfuscation and trying to figure out how to obfuscate a java application created using gradle. The idea is that the runnable jar created after the gradle build is obfuscated. Here is the gradle file
plugins {
// Apply the java plugin to add support for Java
id 'java'
// Apply the application plugin to add support for building a CLI application.
id 'application'
}
repositories {
mavenCentral()
}
dependencies {
// This dependency is used by the application.
implementation 'com.google.guava:guava:29.0-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.13'
}
application {
// Define the main class for the application.
mainClassName = 'com.abc.gradle.hello.App'
}
jar {
manifest {
attributes 'Main-Class': 'com.abc.gradle.hello.App'
}
}
Finally I could achieve this with the following steps
Create a runnable jar with all dependent libraries copied to a directory "dependencies" and add the classpath in the manifest.
task createJar(type: Jar) {
println("Cleaning...")
clean
manifest {
attributes('Main-Class': 'com.abc.gradle.hello.App',
'Class-Path': configurations.default.collect { 'dependencies/' +
it.getName() }.join(' ')
)
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it)
}
} with jar
println "${outputJar} created"
}
Copy the dependencies
task copyDepends(type: Copy) {
from configurations.default
into "${dependsDir}"
}
Obfuscate the library with Proguard
task proguard(type: proguard.gradle.ProGuardTask) {
println("Performing obfuscation..")
configuration 'proguard.conf'
injars "${outputJar}"
outjars "${buildDir}/libs/${rootProject.name}_proguard.jar"
libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
libraryjars "${dependsDir}"
}
Here is the complete build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'net.sf.proguard:proguard-gradle:6.0.3'
classpath 'net.sf.proguard:proguard-base:6.0.3'
}
}
plugins {
id 'java'
id 'application'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.slf4j:slf4j-api:1.7.30'
implementation 'ch.qos.logback:logback-classic:1.2.3'
implementation 'ch.qos.logback:logback-core:1.2.3'
testImplementation 'junit:junit:4.13'
}
def outputJar = "${buildDir}/libs/${rootProject.name}.jar"
def dependsDir = "${buildDir}/libs/dependencies/"
def runnableJar = "${rootProject.name}_fat.jar";
task copyDepends(type: Copy) {
from configurations.default
into "${dependsDir}"
}
task createJar(type: Jar) {
println("Cleaning...")
clean
manifest {
attributes('Main-Class': 'com.abc.gradle.hello.App',
'Class-Path': configurations.default.collect { 'dependencies/' +
it.getName() }.join(' ')
)
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
} with jar
println "${outputJar} created"
}
task proguard(type: proguard.gradle.ProGuardTask) {
println("Performing obfuscation..")
configuration 'proguard.conf'
injars "${outputJar}"
outjars "${buildDir}/libs/${rootProject.name}_proguard.jar"
libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
libraryjars "${dependsDir}"
}
Proguard.conf
-keep public class * {
public * ;
}
Gradle commands to obfuscate
gradle createJar
gradle copyDepends
gradle proguard
Related
I am using spring-boot version 1.5.12 and gradle 5.2.1.
I have added Sentry library in my project as mentioned in its documents like below :
implementation 'io.sentry:sentry:1.7.23'
Gradle downloads the related jarFile in : \.gradle\caches\modules-2\files-2.1\io.sentry\sentry\1.7.23
and also I can see it in my IDE in External Libraries part
then I configed it in my local environment and everything worked fine and I could see my Exception reports in Sentry dashboard.
but after that when I wanted to create a version of project for my production environment, the build operation ended successfully but this new library is not in the folder(which is the result of my build operation named Distribution) which contains myPro.jar and other libraries that I use , I faced the Exception :
Exception in thread "main" java.lang.NoClassDefFoundError: io/sentry/Sentry
at ir.anarestan.ipc.boot.Boot.initSentry(Boot.java:22)
at ir.anarestan.ipc.boot.Boot.main(Boot.java:17)
Caused by: java.lang.ClassNotFoundException: io.sentry.Sentry
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)
... 2 more
here is my build.gradle file :
group 'pc-server'
version '0.2.1-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'groovy'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
<some other dependecies>
implementation 'io.sentry:sentry:1.7.23'
}
apply plugin: 'application'
mainClassName = 'ir.anarestan.ipc.boot.Boot'
jar {
manifest {
attributes 'Main-Class': mainClassName,
'Class-Path': configurations.runtime.files.collect {"$it.name"}.join(' ')
}
}
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Gradle Jar File PC',
'Implementation-Version': version,
'Main-Class': 'ir.anarestan.ipc.boot.Boot'
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
ext.distributionDir= "${rootDir}/dist/core"
ext.mainClassName = 'ir.anarestan.ipc.boot.Boot'
jar {
manifest {
attributes("Implementation-Title": project.name,
"Implementation-Version": version,
"Main-Class": mainClassName,
"Class-Path": configurations.compile.collect { it.getName() }.join(' '))
}
destinationDir = file("$distributionDir")
}
task copyLibs(type: Copy){
from configurations.compile
into "$distributionDir"
}
task copyConfig(type: Copy){
from "$projectDir/src/main/resources/"
into "$distributionDir"
}
task distribution(dependsOn: ['copyLibs', 'copyConfig', 'jar']){
}
and I face Exception here :
#SpringBootApplication
#EnableMongoRepositories(basePackages = {"ir.anarestan.ipc.repository.mongodb"})
public class Boot {
public static void main(String[] args) {
SpringApplication.run(Boot.class, args);
Sentry.init();
}
}
any help would be appreciated, it has take me lots of time!!!
The problem occurred, In eclipse, the project was refreshed by right-clicked project > Gradle > Refresh Gradle Project
I have tried different approaches, downloading \gradle-4.3.1 file, and by going window > preferences > local installation directory > c:\gradle-4.3.1
Gradle Project structure
apply plugin: 'java'
repositories {
mavenCentral()
}
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "com.github.jengelman.gradle.plugins:shadow:6.1.0"
}
}
apply plugin: "com.github.johnrengelman.shadow"
task fatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'com.XXXX.webservices.samples.Controller'
}
baseName = 'XXXXX'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
jar {
baseName = 'XXXX'
from('src/main') {
include 'resources/*.properties'
}
manifest {
attributes 'Main-Class': 'com.netsuite.webservices.samples.Controller'
}
}
dependencies {
compile fileTree(dir: 'lib', includes: ['suitetalk-*0.jar'])
compile 'axis:axis:1.4'
runtime 'commons-httpclient:commons-httpclient:3.1'
runtime 'org.slf4j:log4j-over-slf4j:1.7.6'
runtime 'org.slf4j:slf4j-nop:1.7.25'
compileOnly 'com.google.code.findbugs:jsr305:3.0.1'
}
apply plugin: 'com.github.johnrengelman.shadow'
task runApplication(type: JavaExec) {
description "Builds and executes the sample application"
classpath = sourceSets.main.runtimeClasspath
main = "com.netsuite.webservices.samples.Controller"
standardInput = System.in
}
Gradle build file:
buildscript {
ext.kotlin_version = '1.2.41'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
group 'com.anivive'
version '0.1'
apply plugin: 'kotlin'
sourceSets {
main.java.srcDirs += 'src/main/kotlin/'
test.java.srcDirs += 'src/test/kotlin/'
}
repositories {
mavenLocal()
maven {
url "https://packagecloud.io/anivive/anivive/maven2"
}
mavenCentral()
}
dependencies {
compile 'anivive:Import_Utils:1.2.44'
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
jar {
baseName = 'XMLModelBuilder'
version = '0.1'
manifest {
attributes 'Main-Class': 'com.anivive.Main'
}
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
File structure:
src
- main
- kotlin
- com
- anivive
- Main.kt
Main.kt:
package com.anivive
import com.anivive.util.ExampleClass
object Main {
#JvmStatic
fun main(args: Array<String>) {
ExampleClass.exampleMethod()
}
}
So, I'm trying to build a runnable jar file that will call a single method from a class in the dependency 'anivive:Import_Utils:1.2.44'.
If I remove the dependency, and change my main method to just println("Test"), the jar will run fine via java -jar build/libs/jarFile.jar and it will print out Test.
However, with the dependency included and when trying to call a function from it, I will get Error: Could not find or load main class com.anivive.Main whenever I try to run the Jar file. I suspect it has something to do with hosting the jar file on packagecloud, but I'm surprised this is a problem, considering I can run the program just fine from IntelliJ.
What gives?
I have a project configured with Gradle and Kotlin. It's a command line utility and I would like to be able to run the generated jar from my terminal. However I get the following error:
Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
at com.autentia.impt.MainKt.main(Main.kt)
Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
My gradle configuration is as follows:
buildscript {
ext.kotlin_version = '1.2.20'
ext.junit_platform_version = '1.0.1'
ext.junit_version = '5.0.0'
ext.moshi_version = '1.5.0'
ext.jna_version = '4.5.0'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.junit.platform:junit-platform-gradle-plugin:$junit_platform_version"
}
}
version '1.0-SNAPSHOT'
apply plugin: 'kotlin'
apply plugin: 'application'
apply plugin: 'org.junit.platform.gradle.plugin'
mainClassName = 'com.autentia.impt.MainKt'
repositories {
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib"
implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.squareup.moshi:moshi:$moshi_version"
implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"
implementation "net.java.dev.jna:jna:$jna_version"
testImplementation("org.junit.jupiter:junit-jupiter-api:$junit_version")
testRuntime("org.junit.jupiter:junit-jupiter-engine:$junit_version")
}
sourceSets {
main.kotlin.srcDirs += 'src/main/kotlin'
}
task wrapper(type: Wrapper) {
gradleVersion = '4.4.1'
}
jar {
manifest {
attributes "Main-Class": mainClassName
}
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
The command I use to generate the jar is ./gradlew clean build and the command I use to run the jar is java -jar build/libs/impt-1.0-SNAPSHOT.jar.
I've tried following the official docs and also I've tried with these resources: 1, 2 and 3 without any luck.
Just change implementation configurations to compile. (At least for kotlin-stdlib)
The compile configuration is deprecated and should be replaced by implementation or api in Gradle plugin for Android, however for kotlin-gradle-plugin, I think you still need compile.
Alright I had the same problem and finally figured this out:
My main class name was Main.kt and this is what I had in my build.gradle file when I was getting the same error:
jar {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
manifest {
attributes(
'Class-Path': configurations.compile.collect { it.getName() }.join(' '),
'Main-Class': "play.Main"
)
}
}
Although I noticed when I create the jar artifact in Intellij, it uses play.MainKt as my Main-Class name (yeah, weird naming convention), then I changed my build.gradle file to this:
jar {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
manifest {
attributes(
'Class-Path': configurations.compile.collect { it.getName() }.join(' '),
'Main-Class': "play.MainKt"
)
}
}
and now I am not getting that error.
I converted simple Kitlon file into Library, the file is:
Display.kt:
package hello
fun main(args: Array<String>) {
println("Hello World!")
}
had been compiled into library using the command:
kotlinc Display.kt -d Display.jar
The output had been cross checked to be worked using the command:
kotlin -classpath Display.jar hello.DisplayKt
Then I moved it to folder src/main/resources, then tried calling it from another app, using the below code:
Hello.kt:
package hello
import hello.DisplayKt
fun main(args: Array<String>) {
println("Hi")
}
and defined the build.gradle file as below (tried to put all option I read about to solve my case):
// set up the kotlin-gradle plugin
buildscript {
ext.kotlin_version = '1.1.2-2'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
// apply the kotlin-gradle plugin
apply plugin: "kotlin"
// add kotlin-stdlib dependencies.
repositories {
mavenCentral()
}
dependencies {
//dependencies from a remote repositor
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
//local file
compile files('src/main/resources/Display.jar')
compile fileTree(dir: 'src/main/resources', include: '*.jar')
}
jar {
manifest {
//Define mainClassName as: '[your_namespace].[your_arctifact]Kt'
attributes ('Main-Class': 'hello.HelloKt', "Implementation-Title": "Gradle",
"Implementation-Version": 1)
}
// NEW LINE HERE !!!
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
sourceSets {
main {
java {
srcDirs = ['src/kotlin']
}
resources {
srcDirs = ['src/resources']
}
}
}
but, after running gradle build command, I got the below error:
Unresolved reference: DisplayKt
Note: I'm very very new to JAVA/KOTLIN and GRADLE
I found the answer, the full path of the function is hello.main