Jar file and resource file returning null - java

I have a simple program that reads a text file (test.txt) line by line and prints each line to the console. In intellij it works just fine.
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.io.File;
public class testing {
public static void main(String[] args) {
testing main= new testing();
main.handleData("test.txt");
// handleData();
//System.out.println("hello world");
}
public void handleData(String fileName) {
System.out.println("Testing");
File file= new File(getClass().getResource(fileName).getPath());
try {
Scanner scanner = new Scanner(file);
while(scanner.hasNextLine()){
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
I am trying to build it with gradle and when i run the jar command java -jar out/artifacts/helloTestingWorld_jar/helloTestingWorld.jar I get an error saying the path is null
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.net.URL.getPath()" because the return value of "java.lang.Class.getResource(String)" is null
at testing.handleData(testing.java:22)
at testing.main(testing.java:12)
My build.gradle file looks like this
plugins {
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
jar {
manifest {
attributes "Main-Class": "src.main.java.testing"
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
test {
useJUnitPlatform()
}
My resource folder is marked as the resource root and my java folder that contains my main class is marked as the source root. I am thinking that I might have to add the text file as a dependency in the jar file?
I have had a look at all of the other suggestions on here and the all lead to the same result. I have tried rebuilding the project from scratch and still the same result.
I have also tried using InputStream instead of File
InputStream in = getClass().getResourceAsStream(fileName);
When I use InputStream I get this error
Exception in thread "main" java.lang.NullPointerException
at java.base/java.io.Reader.<init>(Reader.java:168)
at java.base/java.io.InputStreamReader.<init>(InputStreamReader.java:76)
at java.base/java.util.Scanner.<init>(Scanner.java:566)
at testing.handleData(test.java:23)
at testing.main(test.java:10)

Related

Adding a External Folder to the ClassPath in a runnable jar

I have a maven project where I create a jar with dependencies using maven assembly plugin. I also have an external config file ( conf.properties ) that is required by the jar to work properly.
My project structure is like this:
|- abc.jar
|- config
|-conf.properties
How can I add this config folder to the classpath of the jar file? I tried to do this using -cp command and manipulating class-path property in MANIFEST.MF file but no luck so far.
Does anyone know a way to do this?
This is how I tested (sorry, no maven)!
Main class:
package cfh.sf.Chamika;
import java.util.ResourceBundle;
public class ABC {
public static void main(String[] args) {
var bundle = ResourceBundle.getBundle("conf");
System.out.println(bundle.getString("test"));
}
}
Manifest file, note empty line at end (entries must end with a newline (CR, LF or CRLF)):
Manifest-Version: 1.0
Main-Class: cfh.sf.Chamika.ABC
Class-Path: config/
Directory structure
dist/
abc.jar
config/
conf.properties
Content of conf.properties:
test = OK, it is working!
Executed with
java -jar abc.jar
Alternative, not using ResourceBundle:
package cfh.sf.Chamika;
import java.io.IOException;
public class ABC {
public static void main(String[] args) {
try (var input = ClassLoader.getSystemResourceAsStream("conf.properties")) {
int ch;
while ((ch = input.read()) != -1) {
System.out.print((char) ch);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}

getting null from getResourceAsStream when using Travis-CI and Gradle

In my gradle project, it has a resources directory. Using the static getResourceAsStream, I access a file from that directory. This works in Eclipse or when running gradle from the command line locally. But when running in Travis-CI, it is not able to find the file.
I have tried using the "Thread.currentThread().getContextClassLoader().getResourceAsStream" and "LenPEGUtil.class.getClassLoader().getResourceAsStream", but each of those either made it stop working locally, or still failed in Travis-CI.
the build.gradle with the source sets
sourceSets{
main{
java {
srcDir 'src'
}
resources {
srcDir 'resources'
}
}
test{
java{
srcDir 'test'
}
resources{
srcDir 'testResources'
}
}
}
The java code reading the file:
private static BufferedImage lenna;
static {
try {
InputStream lennaInputStream = LenPEGUtil.class.getResourceAsStream("/lenna.png");
setLenna(ImageIO.read(lennaInputStream));
} catch (Exception e) {
e.printStackTrace();
}
}
Locally this works. But in Travis-CI I get an exception:
jjcard.jlenpeg.lenPEGWriterTest STANDARD_ERROR
java.lang.IllegalArgumentException: input == null!
at javax.imageio.ImageIO.read(ImageIO.java:1348)
at jjcard.jlenpeg.LenPEGUtil.<clinit>(LenPEGUtil.java:96)
at jjcard.jlenpeg.lenPEGWriterTest.setup(lenPEGWriterTest.java:23)

How to sync Java source and target between Gradle and Eclipse?

Gradle has source Compatability and the targetCompatability variables that can be set. Eclipse has JDK compliance, generated class files comapatability, and source compatibility.
Is there any way to automagically set one from the other? Ideally, the Gradle stuff would be set from the Eclipse stuff.
edit: these things appear to be stored in: org.eclipse.jdt.core.prefs
edit2: they look like:
D:\ray\dev\conradapps\printg>cat .settings\org.eclipse.jdt.core.prefs
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8
i can make it work as follows, but it's a hack :)
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
plugins {
id 'java-library'
id 'application'
id 'distribution'
}
repositories {
jcenter()
}
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
dependencies {
testImplementation 'junit:junit:4.12'
}
String myMainClass='p.Main'
jar {
manifest {
attributes(
'Main-Class': myMainClass
)
}
}
application {
mainClassName = myMainClass
}
class Hack {
static String[] hack() throws IOException {
System.out.println("Working Directory = "+System.getProperty("user.dir"));
String dir="./.settings";
String name="org.eclipse.jdt.core.prefs";
File file=new File(dir,name);
String[] strings=new String[3];
for(int i=0;i<strings.length;i++)
strings[i]="";
if(file.exists()) System.out.println(file.toString()+" exists.");
else return strings;
List<String> lines=new ArrayList<>();
try {
if(usePath) {
Path path=FileSystems.getDefault().getPath(dir,name);
lines=java.nio.file.Files.readAllLines(path);
} else {
BufferedReader bufferedReader=new BufferedReader(new FileReader(file));
for(String line=bufferedReader.readLine();line!=null;line=bufferedReader.readLine())
lines.add(line);
bufferedReader.close();
}
int index;
for(String line:lines) {
if(line.startsWith("org.eclipse.jdt.core.compiler.compliance")) {
index=line.indexOf("=");
if(index>0) {
System.out.println("compliance: "+line.substring(index+1));
strings[0]=line.substring(index+1);
}
}
if(line.startsWith("org.eclipse.jdt.core.compiler.source=1.8")) {
index=line.indexOf("=");
if(index>0) {
System.out.println("source: "+line.substring(index+1));
strings[1]=line.substring(index+1);
}
}
if(line.startsWith("org.eclipse.jdt.core.compiler.codegen.targetPlatform")) {
index=line.indexOf("=");
if(index>0) {
System.out.println("target: "+line.substring(index+1));
strings[2]=line.substring(index+1);
}
}
}
} catch(Exception e) {
System.out.println("caught: "+e);
}
return strings;
}
public static void main(String[] args) throws IOException {
hack();
}
static boolean usePath;
}
println("java version is: ${JavaVersion.current()}")
String[] strings=Hack.hack();
if(strings[1]!="") {
println 'setting source'
sourceCompatibility = strings[1]
}
if(strings[2]!="") {
println 'setting target'
targetCompatibility = strings[2]
}
Yes. If you want Gradle to give your configuration to Eclipse, basically, as of Gradle 5.1.1, just add:
sourceCompatibility = '1.7'
targetCompatibility = '1.8'
to your build.gradle file. Note that until java 10 the enumeration was 1.8,1.9,1.10 but from Java 11 and future versions the enumeration is 11, 12, etc. Check the Gradle docs.
If you stumble upon this answer: For me, with Gradle 5.0, the java version works with or without quotes (either 1.8 or '1.8') and this is specified in the latest version of the javadocs. It also worked both when added inside and outside of compileJava{}. I tested this on a multiproject build.
I am not sure about the Eclipse to Gradle configuration transfer. Isn't it supposed to go the other way around though? Gradle is the central configuration tool that configures the build process and whatever IDE you are using (you, or your collaborator). Even if it is possible, the Gradle does manipulate the .classpath and other Eclipse files. So to be sure, if it was a crucial point, I would prefer to add the configuration to the Gradle and let that deal with Eclipse or any other IDE's files.

okhttp NoClassDefFoundError when running project

I've created the build.gradle file which looks like this:
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
compile 'com.squareup.okhttp3:okhttp:3.12.1'
}
then I have a simple helloWorld class which uses the sample code from the okhttp website:
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class helloWorld{
public static void main(String[] args){
System.out.println("Hello World!");
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("www.google.com")
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println("SUCCESSS---->"+response.body().string());
} catch (IOException e){
System.out.println("......error thrown");
}
}
}
When I try to run the compiled class file I get the following error:
Exception in thread "main" java.lang.NoClassDefFoundError: okhttp3/OkHttpClient at helloWorld.main(helloWorld.java:6)
Caused by: java.lang.ClassNotFoundException: okhttp3.OkHttpClient 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)
... 1 more
If I remove the lines
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("www.google.com")
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println("SUCCESSS---->"+response.body().string());
} catch (IOException e){
System.out.println("......error thrown");
}
then the class runs correctly. So I think that maybe the jar isn't being imported correctly?
To be clear, the project is being compiled correctly, the issue only occurs if I try to run the compiled class file.
Gradle doesn't include the dependencies when creating the jar by default.
You can use shadow to create a jar with all dependencies https://github.com/johnrengelman/shadow
like this:
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '4.0.4' . <----- add shadow dependency
}
repositories {
mavenCentral()
}
dependencies {
compile 'com.squareup.okhttp3:okhttp:3.12.1'
}
when you build, shadow will add another file named {your_project}-shadow.jar in your build directory or something similar. Execute that and you should see it run.

Gradle NoClassDefFoundError when running jar

I'm trying to set up a Gradle project with some Velocity functions in it.
So far I have the following files:
src/main/java/com/veltes/velotest.java:
package com.veltes;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import java.io.*;
public class velotest {
public static void main(String[] args) {
try {
VelocityEngine ve = new VelocityEngine();
ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
ve.init();
VelocityContext context = new VelocityContext();
context.put("name", "World");
Template t = ve.getTemplate("com/veltes/velotest.vm");
StringWriter writer = new StringWriter();
t.merge(context, writer);
System.out.println(writer.toString());
File logFile = new File("C:/users/xxxx/Desktop/velotest.html");
try {
writeFile(logFile, t, context);
}
catch (IOException io) {
}
} catch (Exception e) {
}
}
private static void writeFile(File logFile, Template t, VelocityContext context) throws IOException {
Writer logWriter;
logWriter = new BufferedWriter(new FileWriter(logFile));
try {
t.merge(context, logWriter);
}
catch (ResourceNotFoundException rnfe) {
}
catch (ParseErrorException pee) {
}
catch (MethodInvocationException mie) {
}
catch (Exception e) {
}
logWriter.flush();
logWriter.close();
}
}
build.gradle:
group 'velocitytest'
version '1.0-SNAPSHOT'
apply plugin: 'groovy'
apply plugin: 'java'
sourceCompatibility = 1.5
repositories {
mavenCentral()
mavenLocal()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.11'
testCompile group: 'junit', name: 'junit', version: '4.11'
compile 'velocity:velocity:1.4'
}
Now, when I run gradle assemble and gradle build everything is fine, but when I try to run the project (same for running the built jar in build/libs/ and for running the velotest class in IntelliJ), I get the following error:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/collections/ExtendedProperties
at org.apache.velocity.runtime.RuntimeInstance.< init >(RuntimeInstance.java:183)
at org.apache.velocity.app.VelocityEngine.(VelocityEngine.java:60)
at com.veltes.velotest.main(velotest.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.collections.ExtendedProperties
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)
... 8 more
It's a bit strange that there is no jar in build/tmp/
Does anyone of you knows a solution?
You need to create a runnable jar if you want to be able to run it.
You can use shadojar plugin or extend the jar task to pack the runtime deps into an artifact.
jar {
archiveName = 'Name.jar'
manifest {
attributes 'Main-Class': 'your.main.class',
'Class-Path': configurations.runtime.files.collect { "lib/$it.name" }.join(' '),
'Implementation-Version': project.version
}
from(configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) {}
}
For intelliJ problem:
apply plugin: 'idea'
Then run gradle idea task, this will refresh .iws .ipr .iml files in your project and sync the classpaths. Or if you use intelliJ support (which is not yet ideal) try to refresh it there. I think in version 2017.1.3 the gradle integration is a bit better.
Adding
from(configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) {} to build.gradle file fixed it for me, like this:
jar {
manifest {
attributes(
'Main-Class': 'gradle22.Library'
)
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}

Categories

Resources