Issues with getResource() after maven build - java

I am trying to load a file from the resources folder, but I can't get it to work after build/packaging
URL databaseURL = this.getClass().getClassLoader().getResource("blacklisted.words");
List<String> blacklistedWordsDatabase = Files.readAllLines(Paths.get(databaseURL.getPath()), StandardCharsets.UTF_8);
This works perfectly when I run my code from the IDE, but after mvn package I run java -jar target/project-0.0.1-jar-with-dependencies.jar and get
java.nio.file.NoSuchFileException file:/var/www/project/target/project-0.0.1-jar-with-dependencies.jar!/blacklisted.words
But checking the archive blacklisted.words is clearly in the root folder of the jar... Any tips on what I might be doing wrong here?

You're using Files.readAllLines which expects a real file / path. This will work in an "unpacked" environment like when you're testing in IDE or running mvn test / mvn exec, but won't work with JAR, where files are packed inside an archive. There are no files and paths!
What you can do instead is to get an InputStream of a packed resource and use it:
try (InputStream resource = this.getClass().getClassLoader().getResource("blacklisted.words")) {
List<String> blacklistedWordsDatabase = new BufferedReader(
new InputStreamReader(
resource,
StandardCharsets.UTF_8
)
).lines()
.collect(Collectors.toList());
}

Related

Executable Jar : Issue in accessing pom while executing jar

I am trying to create an executable jar for my Maven-TestNG project. My ultimate target is to execute code in any machine just by jar files. My plan is to put all required/dependency jar files in a folder and pass the folder as -classpath.
As an executable jar needs a Main class, I created a main class and performing the goals by using InvocationRequest . As part of my goal, my TestNG.xml will be executed.
InvocationRequest request = new DefaultInvocationRequest();
File file = new File("pom.xml");
File fileWithPath = new File(file.getAbsolutePath());
request.setPomFile(new File(fileWithPath));
request.setGoals(Collections.singletonList("clean verify"));
DefaultInvoker invoker = new DefaultInvoker();
invoker.setMavenHome(new File(System.getenv("M2_HOME")));
try {
invoker.execute(request);
} catch (MavenInvocationException e) {
e.printStackTrace();
}
Above code is working fine when I execute it from Eclipse.
But once I do packaging and try to execute Java -cp lib/* com.test.package.runner.InvokerClass from C:\TestProject by using , it is showing as C:\TestProject\lib\pom.xml is not found. I understood that pom.xml wont present on jar level, Instead it will be under C:\TestProject\lib\TestProject.jar!\META-INF\maven\com.test.package\TestProject\pom.xml.
So how can I get the path of pom.xml from META-INF folder during run-time?
I tried setting pom.xml path in different ways as follows. But nothing worked well. Any suggestions please?
Most of the time, I got nullpointer exception as it couldn't find the path.
URL path = InvokerClass.class.getClass().getClassLoader().getResource("pom.xml");
URL path = InvokerClass.class.getClass().getClassLoader().getResource("./pom.xml");
URL path = InvokerClass.class.getClass().getClassLoader().getResource("META-INF/maven/com.adp.aca.test/aca-test-regression/pom.xml");
URL path = InvokerClass.class.getClass().getClassLoader().getResource("./META-INF/maven/com.adp.aca.test/aca-test-regression/pom.xml");
You can prepare the path using the .. in the path.
It will give you the parent directory location from where you are at.
Updated
Suppose you folder structure is as
C:/my-proj/java/Main.java
C:/my-proj/META-INF/pom.xml
Then if you want to find the pom.xml. You can do something like below
String str = System.getProperty("user.dir");
System.out.println(str+"/../META-INF/pom.xml");
Try this.

How to load a JAR stored inside your application resources in a URLClassLoader

I'm trying to load a JAR file in a URLClassLoader. This JAR file is stored in the resources of my project, and its working fine with the following code when I'm running my project using maven:
new URLClassLoader(
new URL[]{MyClass.class.getClassLoader().getResource("dependencies/dependency.jar")},
ClassLoader.getSystemClassLoader().getParent()
);
However, when I build the project using mvn clean install and I then try to run the generate JAR using java -jar myapp.jar it seems like the dependency.jar is not loaded. The dependency.jar file is properly stored inside the project JAR under dependencies/dependency.jar, but it's not read.
I assume that it cannot be loaded from inside a JAR file, but is their a workaround?
I think a solution would be to use getResourceAsStream, but I would then need to transform this stream into a URL.
If possible I'd like to use a solution that wouldn't involve a temporary file created to store the content of dependency.jar.
I believe that your problem is due to the fact that it cannot read a zip in zip, so you should copy your jar file into a temporary file and provide this temporary file to your URLClassLoader as next:
// Get the URL of my jar file
URL url = MyClass.class.getResource("/dependencies/dependency.jar");
// Create my temporary file
Path path = Files.createTempFile("dependency", "jar");
// Delete the file on exit
path.toFile().deleteOnExit();
// Copy the content of my jar into the temporary file
try (InputStream is = url.openStream()) {
Files.copy(is, path, StandardCopyOption.REPLACE_EXISTING);
}
// Create my CL with this new URL
URLClassLoader myCL = new URLClassLoader(
new URL[]{path.toUri().toURL()}, ClassLoader.getSystemClassLoader().getParent()
);

ClassName.class.getResourceAsStream returning Null

I migrated project from Eclipse to Android Studio.
App compiles fine, but it has a crash related to nekohtml library.
Inside HTMLEntities class
//filename = "res/HTMLlat1.properties"
final InputStream stream = HTMLEntities.class.getResourceAsStream(filename);
stream is always null. I tried to move file to the same folder as class and gave full path like this
filename = "/org/cyberneko/html/res/HTMLlat1.properties"
Any ideas?
You should use filename = "/org/cyberneko/html/res/HTMLlat1.properties" instead of filename = "/org.cyberneko.html/res/HTMLlat1.properties" or use relative paths. This could be explained in this way: jar (jar is an example, maybe you're running your code from *.class in some directory) is just some kind of file system with it's root ("/") and all the files in the packages are in some subdirectories and you should specify the paths for them.

File path is working in NetBeans but not in built JAR file

I have a file holding default information that I use to load the textFields of my application. I looked up how to get this built into my jar file when I build and I was told to put it in the source packages and it would be brought along, so I have done that.
File Structure:
Project
-Source Packages
-src
~Java Classes
-defaultFiles
~Defaults.txt
The code I am trying to use is this:
BufferedReader in;
try {
URL resourceURL = FuelProperties.class.getResource("/defaultFiles/Defaults.txt");
in = new BufferedReader(new FileReader(resourceURL.getPath()));
}
And this works perfectly when I run it through NetBeans but when I build the project and try to run it from the jar file it is not grabbing the file.
I have verified that the default file is being built and exists in the same file structure shown above.
If you can help me out with this I would be extremely grateful as I have no idea what is keeping this from working. Thanks.
You have to lookup in the classpath, not on the disk.
The API to use is :
URL resourceURL : this.getClass().getResource("relative path in the classpath");
Once you have the url you can open a stream, etc.
EDIT : in the main method, you of course need to replace
this.getClass()
by
ClassName.class
I found the answer after searching through a couple dozen questions. It turns out that you can only get a InputStream of the data within a file within your JAR not a File object like I was attempting to do.
(If you want the File object you just have to extract the files from the JAR in your program and then you have access to it.)
So the code that got my problem to work was simply replacing this:
URL resourceURL = FuelProperties.class.getResource("/defaultFiles/Defaults.txt");
in = new BufferedReader(new FileReader(resourceURL.getPath()));
With this:
in = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/defaultFiles/Defaults.txt")));
And now it is working both inside NetBeans and in the Built JAR file.

Maven exec:java : how to open and read a file in the resources directory?

here is the structure of my project.
proj
---src
----main
----java
----Main.java
----resources
----res.txt
I am using m2eclipse plugin with Eclipse.
In Main.java, I have
File f = new File("res.txt"); System.out.println(f.getAbsolutePath());
When I run mvn exec:java, the path got printed out is "...\proj\res.txt". How can I make it look for the resource file in "...\proj\target\classes" directory?
EDIT:
Thanks for the answers to my original question. I have a follow-up questions:
So basically what I want to do is to have the Main class read the "res.txt" and then generate a new "newres.txt" to the resources directory so that I can package this new "newres.txt" to the jar file in the package phase later. Currently I mounted this exec:java to the prepare-package phase. How should I create this "newres.txt" in the resources directory without a hard-coded absolute path or depending on the directory structure of Maven?
I guess I will answer my own question, Thread.currentThread().getContextClassLoader().getResourceAsStream()
works the best for me, especially when the project produces a jar dependency for another web project.
Figure I'd add to the answers.
You can also use:
InputStream file = ClassLoader.getSystemResourceAsStream("res.txt");
Try
InputStream IS = Main.class.getResourceAsStream("res.txt");
to access the content of res.txt. Pay attention to the encoding of your text file (beware of defaults). If your maven project is set on UTF-8 for example, make sure res.txt is encoded in UTF-8 too, otherwise, you'll get funny errors at runtime.
When run from eclipse, res.txt is created in/reader from the folder where eclipse is started. Hence the output.
If you want to make the code look at the file in a specific folder, which is present in your classpath, then you should try using getResourceAsStream() method.
Alternately you can specify the absolute path of the file.
Here is anther solution:
String str = "target/classes/res.txt";
InputStream is = new FileInputStream(new File(str));
If you exec java in root folder, and you resource will compile to target/classes folder, you can write you code like this.

Categories