Apache CLI, Executable jar, classLoader().getResource() - java

My goal is to use Apache CLI with an executable jar file to read in a text file, perform string manipulations, and then write to a CSV. You would execute the tool in the terminal like this:
$ java -jar my-tool-with-dependencies.jar -i input.txt -o output.csv
I've written tests for this functionality and those tests are passing. The test input text file is located in src/test/resources/. The following test is passing:
#Test
public void testWordockerWriteCsvFileContents() {
// Make sure the csv file contains contents
Wordocker w = new Wordocker();
String intext = "/textformat/example_format.txt";
String outcsv = "/tmp/foo.csv";
w.writeCsvFile(intext, outcsv);
try {
Reader in = new FileReader(outcsv);
Iterable<CSVRecord> records = CSVFormat.DEFAULT.parse(in);
for (CSVRecord record : records) {
assertTrue(record.toString().length() > 0);
}
} catch(FileNotFoundException e){
assertTrue(false);
} catch(IOException e) {
assertTrue(false);
}
File file = new File(outcsv);
if (file.exists()) {
file.delete();
}
}
We I compile my jar files with dependencies using mvn clean compile assembly:single then I raise the following FileNotFoundException:
// Get file from resources folder
URL resourceURL = ParseDoc.class.getClassLoader().getResource(fileName);
if (resourceURL == null) {
throw new FileNotFoundException(fileName + " not found");
}
file = new File(resourceURL.getFile());
This leads me to believe that there is an issue with where ParseDoc.class.getClassLoader().getResource(fileName); is looking for the file. I'm aware of related questions which have been asked. Related questions are the following:
Strange behavior of Class.getResource() and ClassLoader.getResource() in executable jar
What is the difference between Class.getResource() and ClassLoader.getResource()?
MyClass.class.getClassLoader().getResource(“”).getPath() throws NullPointerException
this.getClass().getClassLoader().getResource(“…”) and NullPointerException
getResourceAsStream returns null
None of these questions appear to ask about how to use an executable jar with Apache CLI. I think the basic issue is that the filepath given by my command line argument cannot be found by URL resourceURL = ParseDoc.class.getClassLoader().getResource(fileName);.
Please let me know what you think. Thank you for your time.

I'm posting this as answer as well after discussion via comments:
Classloader.getResource() is only fetching files that are packaged as part of the Jar-file or located in the classpath-folders.
For reading a normal file you would use something like your first example, i.e. FileReader or FileInputStream or simply pass a java.io.File depending on what the library that you are trying to use supports.

Related

Ensuring a text file is included in a JAR

My apologies if this is a duplicate, I've been searching around and haven't found anything that works.
I've been trying export a project as a JAR file that includes reading information from a text file. After doing some research, I changed my reader from FileReader to InputStreamReader, using CLASSNAME.class.getClassLoader().getResourceAsStream("textFile.txt"). (I also understand that it should work without the getClassLoader() method involved) However, getResourceAsStream("textFile.txt") returns null, throwing a NullPointerException when I try to read it using a BufferedReader.
From what I've read, this is because my text file isn't actually in the JAR. Yet when I attempt to do so I still get a NullPointerException. I've also tried adding the folder with the files to the build path, but that doesn't work either. I'm not sure how to check if the files are actually in the JAR and, if not, how to get them in the JAR so they can be found and properly read.
For reference, I currently use Eclipse Neon on a MacBook Air and here is my code that tries, but fails, to read the text file:
public static void addStates(String fileName) {
list.clear();
try {
InputStream in = RepAppor.class.getClassLoader().getResourceAsStream("Populations/" + fileName);
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
/*
* NOTE: A Leading slash indicates the absolute root of the directory, which is on my system
* Don't use a leading slash if the root is relative to the directory
*/
String line;
while(!((line = reader.readLine()) == null)) {
list.add(line);
}
reader.close();
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "The file, " + fileName + ", could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
} catch (NullPointerException n) {
JOptionPane.showMessageDialog(null, "Could not find " + fileName + ".\nNull Pointer Exception thrown", "Error", JOptionPane.ERROR_MESSAGE);
}
}
Thank you for your consideration and I appreciate and welcome any feedback you might have.
There are a number of ways to check the contents of a .jar file.
Most IDEs have a “Files” section where you can simply expand a .jar file, as if it were a directory.
If your have the JDK’s bin subdirectory in your execution path, you can use the jar command in a terminal:
jar tf /Users/AaronMoriak/repappor.jar
Every .jar file is actually a zip file with a different extension (and one or more Java-specific special entries). So, any command that handles zip files will work on .jar files.
Since you’re on a Mac, you have access to the Unix unzip command. In a terminal, you can simply do this:
unzip -v /Users/AaronMoriak/repappor.jar
(The -v option means “view but don’t extract.”)
If your .jar file has a lot of entries, you can limit the output of the above command:
unzip -v /Users/AaronMoriak/repappor.jar | grep Populations
Your code comment about a leading slash is not quite correct. However, if you remove the getClassLoader() part, the comment is somewhat more correct:
// Change:
// RepAppor.class.getClassLoader().getResourceAsStream
// to just:
// RepAppor.class.getResourceAsStream
// Expects 'Populations' to be in the same directory as the RepAppor class.
InputStream in = RepAppor.class.getResourceAsStream("Populations/" + fileName);
// Expects 'Populations' to be in the root of the classpath.
InputStream in = RepAppor.class.getResourceAsStream("/Populations/" + fileName);

Is there a way to get the file path of the .java file executed or compiled?

In Python the global variable __file__ is the full path of the current file.
System.getProperty("user.dir"); seems to return the path of the current working directory.
I want to get the path of the current .java, .class or package file.
Then use this to get the path to an image.
My project file structure in Netbeans looks like this:
(source: toile-libre.org)
Update to use code suggested from my chosen best answer:
// read image data from picture in package
try {
InputStream instream = TesseractTest.class
.getResourceAsStream("eurotext.tif");
bufferedImage = ImageIO.read(instream);
}
catch (IOException e) {
System.out.println(e.getMessage());
}
This code is used in the usage example from tess4j.
My full code of the usage example is here.
If you want to load an image file stored right next to your class file, use Class::getResourceAsStream(String name).
In your case, that would be:
try (InputStream instream = TesseractTest.class.getResourceAsStream("eurotext.tif")) {
// read stream here
}
This assumes that your build system copies the .tif file to your build folder, which is commonly done by IDEs, but requires extra setup in build tools like Ant and Gradle.
If you package your program to a .jar file, the code will still work, again assuming your build system package the .tif file next to the .class file.
Is there a way to get the file path of the .java file executed or compiled?
For completeness, the literal answer to your question is "not easily and not always".
There is a round-about way to find the source filename for a class on the callstack via StackFrameElement.getFileName(). However, the filename won't always be available1 and it won't necessarily be correct2.
Indeed, it is quite likely that the source tree won't be present on the system where you are executing the code. So if you needed an image file that was stashed in the source tree, you would be out of luck.
1 - It depends on the Java compiler and compilation options that you use. And potentially on other things.
2 - For example, the source tree can be moved or removed after compilation.
Andreas has described the correct way to solve your problem. Make sure that the image file is in your application's JAR file, and access it using getResource or getResourceAsStream. If your application is using an API that requires a filename / pathname in the file system, you may need to extract the resource from the JAR to a temporary file, or something like that.
public class Main {
public static void main(String[] args) throws Exception {
System.out.println(getPackageParent(Main.class, false));
}
public static String getPackageParent(Class<?> cls, boolean include_last_dot)
throws Exception {
StringBuilder sb = new StringBuilder(cls.getPackage().getName());
if (sb.lastIndexOf(".") > 0)
if (include_last_dot)
return sb.delete(sb.lastIndexOf(".") + 1, sb.length())
.toString();
else
return sb.delete(sb.lastIndexOf("."), sb.length()).toString();
return sb.toString();
}
}

In Java, why does class.getResource("/path/to/res"); not work in the runnable jar when copying files to the system?

I have been working on a project that requires the user to "install" the program upon running it the first time. This installation needs to copy all the resources from my "res" folder to a dedicated directory on the user's hard drive. I have the following chunk of code that was working perfectly fine, but when I export the runnable jar from eclipse, I received a stack trace which indicated that the InputStream was null. The install loop passes the path of each file in the array list to the export function, which is where the issue is (with the InputStream). The paths are being passed correctly in both Eclipse and the runnable jar, so I doubt that is the issue. I have done my research and found other questions like this, but none of the suggested fixes (using a classloader, etc) have worked. I don't understand why the method I have now works in Eclipse but not in the jar?
(There also exists an ArrayList of File called installFiles)
private static String installFilesLocationOnDisk=System.getProperty("user.home")+"/Documents/[project name]/Resources/";
public static boolean tryInstall(){
for(File file:installFiles){
//for each file, make the required directories for its extraction location
new File(file.getParent()).mkdirs();
try {
//export the file from the jar to the system
exportResource("/"+file.getPath().substring(installFilesLocationOnDisk.length()));
} catch (Exception e) {
return false;
}
}
return true;
}
private static void exportResource(String resourceName) throws Exception {
InputStream resourcesInputStream = null;
OutputStream resourcesOutputStream = null;
//the output location for exported files
String outputLocation = new File(installFilesLocationOnDisk).getPath().replace('\\', '/');
try {
//This is where the issue arises when the jar is exported and ran.
resourcesInputStream = InstallFiles.class.getResourceAsStream(resourceName);
if(resourcesInputStream == null){
throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file.");
}
//Write the data from jar's resource to system file
int readBytes;
byte[] buffer = new byte[4096];
resourcesOutputStream = new FileOutputStream(outputLocation + resourceName);
while ((readBytes = resourcesInputStream.read(buffer)) > 0) {
resourcesOutputStream.write(buffer, 0, readBytes);
}
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
} finally {
//Close streams
resourcesInputStream.close();
resourcesOutputStream.close();
}
}
Stack Trace:
java.lang.Exception: Cannot get resource "/textures\gameIcon.png" from Jar file.
All help is appreciated! Thanks
Stack Trace:
java.lang.Exception: Cannot get resource "/textures\gameIcon.png" from Jar file.
The name if the resource is wrong. As the Javadoc of ClassLoader.getResource(String) describes (and Class.getResourceAsStream(String) refers to ClassLoader for details):
The name of a resource is a /-separated path name that identifies
the resource.
No matter whether you get your resources from the File system or from a Jar File, you should always use / as the separator.
Using \ may sometimes work, and sometimes not: there's no guarantee. But it's always an error.
In your case, the solution is a change in the way that you invoke exportResource:
String path = file.getPath().substring(installFilesLocationOnDisk.length());
exportResource("/" + path.replace(File.pathSeparatorChar, '/'));
Rename your JAR file to ZIP, uncompress it and check where did resources go.
There is a possibility you're using Maven with Eclipse, and this means exporting Runnable JAR using Eclipse's functionality won't place resources in JAR properly (they'll end up under folder resources inside the JAR if you're using default Maven folder names conventions).
If that is the case, you should use Maven's Assembly Plugin (or a Shade plugin for "uber-JAR") to create your runnable JAR.
Even if you're not using Maven, you should check if the resources are placed correctly in the resulting JAR.
P.S. Also don't do this:
.getPath().replace('\\', '/');
And never rely on particular separator character - use java.io.File.separator to determine system's file separator character.

Java: get absolute path of project

I'm trying to run a exe file in path outside of the current package. My code.java file that runs it is in
%Workspace_path%\Project\src\main\java\com\util\code.java
However the directory of where the exe is
%Workspace_path%\Project\src\main\resources\program.exe
If possible, it seems like the best solution here would be to get the absolute path of the Project then append "src\main\resources\" to it. Is there a good way to do this or is there an alternative solution?
I'm using Eclipse, but it would great if it could be used in other IDEs too. Thanks for any help.
The de facto approach to solving this is to bundle the EXE as a classpath resource. It seems you have arranged for this already.
When working with classpath resources, a mature program should not assume that the resource is in the filesystem. The resources could be packaged in a JAR file, or even in a WAR file. The only thing you can trust at that point is the standard methods for accessing resources in Java, as hinted below.
The way to solve your problem, then, is to access the resource contents using the de facto standard of invoking Class.getResourceAsStream (or ClassLoader.getResourceAsStream), save the contents to a temporary file, and execute from that file. This will guarantee your program works correctly regardless of its packaging.
In other words:
Invoke getClass().getResourceAsStream("/program.exe"). From static methods, you can't call getClass, so use the name of your current class instead, as in MyClass.class.getResourceAsStream. This returns an InputStream.
Create a temporary file, preferably using File.createTempFile. This returns a File object identifying the newly created file.
Open an OutputStream to this temp file.
Use the two streams to copy the data from the resource into the temp file. You can use IOUtils.copy if you're into Apache Commons tools. Don't forget to close the two streams when done with this step.
Execute the program thus stored in the temporary file.
Clean up.
In other words (code snippet added later):
private void executeProgramFromClasspath() throws IOException {
// Open resource stream.
InputStream input = getClass().getResourceAsStream("/program.exe");
if (input == null) {
throw new IllegalStateException("Missing classpath resource.");
}
// Transfer.
OutputStream output = null;
try {
// Create temporary file. May throw IOException.
File temporaryFile = File.createTempFile(getClass().getName(), "");
output = new FileOutputStream(temporaryFile);
output = new BufferedOutputStream(output);
IOUtils.copy(input, output);
} finally {
// Close streams.
IOUtils.closeQuietly(input);
IOUtils.closeQuietly(output);
}
// Execute.
try {
String path = temporaryFile.getAbsolutePath();
ProcessBuilder processBuilder = new ProcessBuilder(path);
Process process = processBuilder.start();
process.waitFor();
} catch (InterruptedException e) {
// Optional catch. Keeps the method signature uncluttered.
throw new IOException(e);
} finally {
// Clean up
if (!temporaryFile.delete()) {
// Log this issue, or throw an error.
}
}
}
Well,in your context,the project root is happen to be the current path
.
,that is where the java.exe start to execute,so a easy way is:
String exePath="src\\main\\resources\\program.exe";
File exeFile=new File(".",exePath);
System.out.println(exeFile.getAbusolutePath());
...
I tested this code on Eclipse,It's ok. I think is should work on different ide.
Good Luck!

Output file generated in Eclipse but not in jar file

I have a simple parsing program that takes a few files and combines them. It then generates a few output files to the working directory. When I run this program in eclipse it generates all the required output files. However, when I run it using a jar generated in eclipse it only creates two of the three output files. It makes me think something is wrong in how the jar file is generated but can't seem to find any answers to this.
I've tried updating some of the java, it was written using java 5 I believe. I just changed the Vectors to ArrayLists and the FileOutputStream to FileWriter.
I had to download javax.mail to get the required libraries and added those jar files to the java6 library I was using in Eclipse. I've tried deleting the classes and generating new classes. I tried to check the permissions on the jar file to make sure that I had access with it. I guess I am just not sure where to start.
I've also tried packing this as a jar file and not as a runnable jar file because it gave me more options on what to include. However, I could not run this type of jar file even though it was an executable. I've recreated the jar file numerous times without any luck.
There were quite a few problems people had with UTF-8 not displaying properly in a jar file but being fine in eclipse. However their jar files were generating the text files where as mine just does not generate one.
Update: Interestingly if I move the block of code to its own class and run it as a separate jar it will work. So the solution for now is to have two jar files.
This is the code for the ungenerated file:
private static void parseCRNOnly() {
try {
//file to write to
File new_file = new File("CRNOnlyClean.txt");
FileWriter out = new FileWriter(new_file);
//file to read from
File file = new File("CRNOnly.txt");
FileReader reader = new FileReader(file);
BufferedReader buf = new BufferedReader(reader);
try{
String str;
String temp = "\r";
String nl = "\r\n";
String tab = "\t";
str = buf.readLine();
while (str != null && !str.isEmpty()) {
StringTokenizer tokenizer = new StringTokenizer(str," \t");
int column = 0;
while(tokenizer.hasMoreTokens()) {
column++;
temp = tokenizer.nextToken();
if(column == 8){
break;
}
out.write(temp);
out.write(tab);
}
out.write(nl);
str = buf.readLine();
}
out.close();
} catch(IOException e0){
System.out.println("Error Reading From CRNOnly.txt");
JOptionPane.showMessageDialog(null, "Error Reading From CRNOnly.txt");
}
} catch(FileNotFoundException e1){
System.out.println("File CRNOnly.txt Not Found");
JOptionPane.showMessageDialog(null, "File CRNOnly.txt Not Found");
} catch (IOException e) {
System.out.println("Error Reading from FileWriter");
JOptionPane.showMessageDialog(null, "Error Reading from FileWriter");
}
For some reason the jar file was not including some old libraries that I had to add from javax.mail, so I tried making a jar file using netbeans instead and it worked. Netbeans didn't pack the javax.mail libraries into the jar file either but included them in a file with the jar file. Therefore to use the jar file this file must be in the directory as well.

Categories