How to locate a file from user provided input in Java - java

I've got a program which reads in multiple json files and then does some analysis of the information contained in those files.
My project structure is laid out like :
/main
/java
/resources
/data
file1.json
file2.json
...
fileN.json
I'm attempting to give the user the ability to specify an alternate location if they have a different data set they want to analyze.
I'm using the following code to create an array of File objects:
ClassLoader loader = myClass.class.getClassLoader();
URL url = loader.getResource(location);
try {
String path = url.getPath();
return new File(path).listFiles();
} catch (NullPointerException e) {
throw new FileNotFoundException();
}
Note: I'm using myClass.class.getClassLoader() because I'm calling from a static method rather than from an instantiated object.
This works successfully when the location = "data". However if I pass in an absolute path to a different location (ex: location = "/Users/myuser/Desktop/data") that has the same data files in it I'm getting a NPE.
Is there a good way that will allow me to use the src/main/resources directory by default but allow my user to specify an absolute path to the data if they choose?

ClassLoader loader = myClass.class.getClassLoader();
URL url = loader.getResource(location);
The code above only works with files that exist in your class path.
Thus you can change it to read from src/main/resources directory by default and the absolute path given by the user by updating it to this:
try {
return new File(location).listFiles();
} catch (NullPointerException e) {
throw new FileNotFoundException();
}

It's very simple:
ClassLoader cl = myClass.class.getClassLoader();
URL url = cl.getResource(location);
if (url == null) {
//the location does not exist in class path
return new File(location).listFiles();
} else {
return new File(url.getPath()).listFiles();
}
But I think that the better way is:
private File[] readFile(String userLocation) {
if(userLocation == null || userLocation.isEmpty()) {
// user do not specify the path
return new File(myClass.class.getResource("data").getPath()).listFiles();
} else {
return new File(userLocation).listFiles();
}
}

Related

Can Anyone tell me why my IFile always returns null?

I have a plugin. Within this plugin I have a view that creates some markers so that I can open the file and navigate automatically to a selected line. However where as this method has worked for me previously. it ceases to now? it only ever returns null as my IFile.
Here is my method of creating the markers NOTE: That this method is not located within the controlling class of the FXML file. it is located in another external file.
public static String openAbsoluteFileInEclipseEditor(String absoluteLocationP, int lineNumberP) {
File absolute = new File(absoluteLocationP);
if(absolute.isFile() && absolute.exists()) {
if(Globals.testing) {
try {
Desktop.getDesktop().open(absolute);
return null;
} catch (IOException e) {
ErrorHandling.reportErrors(e);
return "";
}
}else {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
IWorkbenchPage page = window.getActivePage();
IWorkspace workspace = ResourcesPlugin.getWorkspace();
try {
if(lineNumberP != 0) {
IPath location = Path.fromOSString(absolute.getAbsolutePath());
System.out.println("location " + location);
IFile iFile = workspace.getRoot().getFileForLocation(location);
System.out.println("iFile " + iFile);
IMarker marker = iFile.createMarker(IMarker.TEXT);
marker.setAttribute(IMarker.LINE_NUMBER, lineNumberP);
IDE.openEditor(page, marker);
marker.delete();
}else {
IFileStore fileStore = EFS.getLocalFileSystem().getStore(absolute.toURI());
IDE.openEditorOnFileStore( page, fileStore );
}
return null;
} catch (PartInitException e) {
ErrorHandling.reportErrors(e);
return "";
} catch (CoreException e) {
ErrorHandling.reportErrors(e);
return "";
}
}
}else {
return "File not found";
}
}
Here are the two prints values that you can see in the middle of the method.
location C:/SoftwareAG_Workspaces/workspace105/HOSPITAL/Natural-Libraries/HOSPITAL/Programs/XX021P01.NSP
iFile null
Can anyone point out to me why it might no longer work and why its only returning nulls? and if possible could you suggest an alternate method that will work? the file does exist within in that location I have made sure of that.
Thanks in advance.
The Javadoc for getFileForLocation says:
This method returns null when the given file system location is not
under the location of any existing project in the workspace.
So is that location in the current workspace, and in a valid project?
The Javadoc also says:
The result will also omit resources that are explicitly excluded from
the workspace according to existing resource filters.
So check any resource filters.
Finally the Javadoc says:
Warning: This method ignores linked resources and their children.
Since linked resources may overlap other resources, a unique mapping
from a file system location to a single resource is not guaranteed.
To find all resources for a given location, including linked
resources, use the method findFilesForLocation.
So check for linked resources

What's the best practice for accessing a config file adjacent to my jar? [duplicate]

I'm trying to get the location a Runnable JAR file is run from.
I tried doing
try {
String path = new java.io.File(".").getCanonicalPath();
} catch (IOException e) {
e.printStackTrace();
}
But that returns:
C:\Users\Kevin\Desktop/server/Server
while the JAR file is located at
C:\Users\Kevin\Desktop
I also tried doing
return new file(Server.class.getProtectionDomain().getCodeSource().getLocation().getPath());
But that returns:
C:\Users\Kevin\Desktop\server.jar/server/Server
So basicly I want the path of the JAR file without the filename and not the ClassPath.
Any way of doing this?
Update
Since it doesn't work in certain test-cases, I'll update the answer.
The correct way to do this should be with ClassLoader:
File jarDir = new File(ClassLoader.getSystemClassLoader().getResource(".").getPath());
System.out.println(jarDir.getAbsolutePath());
Tested on various classpaths, the output was correct.
Old answer
This should work
File f = new File(System.getProperty("java.class.path"));
File dir = f.getAbsoluteFile().getParentFile();
String path = dir.toString();
It works for me, my program is in:
C:\Users\User01\Documents\app1\dist\JavaApplication1.jar
And it returns
C:\Users\User01\Documents\app1\dist
Herewith my version of computing the jar directory
/**
* Compute the absolute file path to the jar file.
* The framework is based on http://stackoverflow.com/a/12733172/1614775
* But that gets it right for only one of the four cases.
*
* #param aclass A class residing in the required jar.
*
* #return A File object for the directory in which the jar file resides.
* During testing with NetBeans, the result is ./build/classes/,
* which is the directory containing what will be in the jar.
*/
public static File getJarDir(Class aclass) {
URL url;
String extURL; // url.toExternalForm();
// get an url
try {
url = aclass.getProtectionDomain().getCodeSource().getLocation();
// url is in one of two forms
// ./build/classes/ NetBeans test
// jardir/JarName.jar froma jar
} catch (SecurityException ex) {
url = aclass.getResource(aclass.getSimpleName() + ".class");
// url is in one of two forms, both ending "/com/physpics/tools/ui/PropNode.class"
// file:/U:/Fred/java/Tools/UI/build/classes
// jar:file:/U:/Fred/java/Tools/UI/dist/UI.jar!
}
// convert to external form
extURL = url.toExternalForm();
// prune for various cases
if (extURL.endsWith(".jar")) // from getCodeSource
extURL = extURL.substring(0, extURL.lastIndexOf("/"));
else { // from getResource
String suffix = "/"+(aclass.getName()).replace(".", "/")+".class";
extURL = extURL.replace(suffix, "");
if (extURL.startsWith("jar:") && extURL.endsWith(".jar!"))
extURL = extURL.substring(4, extURL.lastIndexOf("/"));
}
// convert back to url
try {
url = new URL(extURL);
} catch (MalformedURLException mux) {
// leave url unchanged; probably does not happen
}
// convert url to File
try {
return new File(url.toURI());
} catch(URISyntaxException ex) {
return new File(url.getPath());
}
}
I had to mess around a lot before I finally found a working solution.
It is possible that the jarLocation comes with a prefix like file:\ or jar:file\, which can be removed by using String#substring().
URL jarLocationUrl = MyClass.class.getProtectionDomain().getCodeSource().getLocation();
String jarLocation = new File(jarLocationUrl.toString()).getParent();
If you know
file(Server.class.getProtectionDomain().getCodeSource().getLocation().getPath());
returns
C:\Users\Kevin\Desktop\server.jar/server/Server
And you know your Jar name is server.jar or for matter any .jar file, your intention is to get C:\Users\Kevin\Desktop , straight forward way is to do string manipulation.
With the retrieved output, tokenize the string based on File.separator and construct the path (by concatenating the strings with File.separator in between) until you get a token which contains .jar

How do I get a Java resource as a File?

I have to read a file containing a list of strings. I'm trying to follow the advice in this post. Both solutions require using FileUtils.readLines, but use a String, not a File as the parameter.
Set<String> lines = new HashSet<String>(FileUtils.readLines("foo.txt"));
I need a File.
This post would be my question, except the OP was dissuaded from using files entirely. I need a file if I want to use the Apache method, which is the my preferred solution to my initial problem.
My file is small (a hundred lines or so) and a singleton per program instance, so I do not need to worry about having another copy of the file in memory. Therefore I could use more basic methods to read the file, but so far it looks like FileUtils.readLines could be much cleaner. How do I go from resource to file.
Apache Commons-IO has an IOUtils class as well as a FileUtils, which includes a readLines method similar to the one in FileUtils.
So you can use getResourceAsStream or getSystemResourceAsStream and pass the result of that to IOUtils.readLines to get a List<String> of the contents of your file:
List<String> myLines = IOUtils.readLines(ClassLoader.getSystemResourceAsStream("my_data_file.txt"));
I am assuming the file you want to read is a true resource on your classpath, and not simply some arbitrary file you could just access via new File("path_to_file");.
Try the following using ClassLoader, where resource is a String representation of the path to your resource file in your class path.
Valid String values for resource can include:
"foo.txt"
"com/company/bar.txt"
"com\\company\\bar.txt"
"\\com\\company\\bar.txt"
and path is not limited to com.company
Relevant code to get a File not in a JAR:
File file = null;
try {
URL url = null;
ClassLoader classLoader = {YourClass}.class.getClassLoader();
if (classLoader != null) {
url = classLoader.getResource(resource);
}
if (url == null) {
url = ClassLoader.getSystemResource(resource);
}
if (url != null) {
try {
file = new File(url.toURI());
} catch (URISyntaxException e) {
file = new File(url.getPath());
}
}
} catch (Exception ex) { /* handle it */ }
// file may be null
Alternately, if your resource is in a JAR, you will have to use Class.getResourceAsStream(resource); and cycle through the file using a BufferedReader to simulate the call to readLines().
using a resource to read the file to a string:
String contents =
FileUtils.readFileToString(
new File(this.getClass().getResource("/myfile.log").toURI()));
using inputstream:
List<String> listContents =
IOUtils.readLines(
this.getClass().getResourceAsStream("/myfile.log"));

Get location of JAR file

I'm trying to get the location a Runnable JAR file is run from.
I tried doing
try {
String path = new java.io.File(".").getCanonicalPath();
} catch (IOException e) {
e.printStackTrace();
}
But that returns:
C:\Users\Kevin\Desktop/server/Server
while the JAR file is located at
C:\Users\Kevin\Desktop
I also tried doing
return new file(Server.class.getProtectionDomain().getCodeSource().getLocation().getPath());
But that returns:
C:\Users\Kevin\Desktop\server.jar/server/Server
So basicly I want the path of the JAR file without the filename and not the ClassPath.
Any way of doing this?
Update
Since it doesn't work in certain test-cases, I'll update the answer.
The correct way to do this should be with ClassLoader:
File jarDir = new File(ClassLoader.getSystemClassLoader().getResource(".").getPath());
System.out.println(jarDir.getAbsolutePath());
Tested on various classpaths, the output was correct.
Old answer
This should work
File f = new File(System.getProperty("java.class.path"));
File dir = f.getAbsoluteFile().getParentFile();
String path = dir.toString();
It works for me, my program is in:
C:\Users\User01\Documents\app1\dist\JavaApplication1.jar
And it returns
C:\Users\User01\Documents\app1\dist
Herewith my version of computing the jar directory
/**
* Compute the absolute file path to the jar file.
* The framework is based on http://stackoverflow.com/a/12733172/1614775
* But that gets it right for only one of the four cases.
*
* #param aclass A class residing in the required jar.
*
* #return A File object for the directory in which the jar file resides.
* During testing with NetBeans, the result is ./build/classes/,
* which is the directory containing what will be in the jar.
*/
public static File getJarDir(Class aclass) {
URL url;
String extURL; // url.toExternalForm();
// get an url
try {
url = aclass.getProtectionDomain().getCodeSource().getLocation();
// url is in one of two forms
// ./build/classes/ NetBeans test
// jardir/JarName.jar froma jar
} catch (SecurityException ex) {
url = aclass.getResource(aclass.getSimpleName() + ".class");
// url is in one of two forms, both ending "/com/physpics/tools/ui/PropNode.class"
// file:/U:/Fred/java/Tools/UI/build/classes
// jar:file:/U:/Fred/java/Tools/UI/dist/UI.jar!
}
// convert to external form
extURL = url.toExternalForm();
// prune for various cases
if (extURL.endsWith(".jar")) // from getCodeSource
extURL = extURL.substring(0, extURL.lastIndexOf("/"));
else { // from getResource
String suffix = "/"+(aclass.getName()).replace(".", "/")+".class";
extURL = extURL.replace(suffix, "");
if (extURL.startsWith("jar:") && extURL.endsWith(".jar!"))
extURL = extURL.substring(4, extURL.lastIndexOf("/"));
}
// convert back to url
try {
url = new URL(extURL);
} catch (MalformedURLException mux) {
// leave url unchanged; probably does not happen
}
// convert url to File
try {
return new File(url.toURI());
} catch(URISyntaxException ex) {
return new File(url.getPath());
}
}
I had to mess around a lot before I finally found a working solution.
It is possible that the jarLocation comes with a prefix like file:\ or jar:file\, which can be removed by using String#substring().
URL jarLocationUrl = MyClass.class.getProtectionDomain().getCodeSource().getLocation();
String jarLocation = new File(jarLocationUrl.toString()).getParent();
If you know
file(Server.class.getProtectionDomain().getCodeSource().getLocation().getPath());
returns
C:\Users\Kevin\Desktop\server.jar/server/Server
And you know your Jar name is server.jar or for matter any .jar file, your intention is to get C:\Users\Kevin\Desktop , straight forward way is to do string manipulation.
With the retrieved output, tokenize the string based on File.separator and construct the path (by concatenating the strings with File.separator in between) until you get a token which contains .jar

How can I access a folder inside of a resource folder from inside my jar File?

I have a resources folder/package in the root of my project, I "don't" want to load a certain File. If I wanted to load a certain File, I would use class.getResourceAsStream and I would be fine!! What I actually want to do is to load a "Folder" within the resources folder, loop on the Files inside that Folder and get a Stream to each file and read in the content... Assume that the File names are not determined before runtime... What should I do? Is there a way to get a list of the files inside a Folder in your jar File?
Notice that the Jar file with the resources is the same jar file from which the code is being run...
Finally, I found the solution:
final String path = "sample/folder";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
if(jarFile.isFile()) { // Run with JAR file
final JarFile jar = new JarFile(jarFile);
final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
while(entries.hasMoreElements()) {
final String name = entries.nextElement().getName();
if (name.startsWith(path + "/")) { //filter according to the path
System.out.println(name);
}
}
jar.close();
} else { // Run with IDE
final URL url = Launcher.class.getResource("/" + path);
if (url != null) {
try {
final File apps = new File(url.toURI());
for (File app : apps.listFiles()) {
System.out.println(app);
}
} catch (URISyntaxException ex) {
// never happens
}
}
}
The second block just work when you run the application on IDE (not with jar file), You can remove it if you don't like that.
Try the following.
Make the resource path "<PathRelativeToThisClassFile>/<ResourceDirectory>" E.g. if your class path is com.abc.package.MyClass and your resoure files are within src/com/abc/package/resources/:
URL url = MyClass.class.getResource("resources/");
if (url == null) {
// error - missing folder
} else {
File dir = new File(url.toURI());
for (File nextFile : dir.listFiles()) {
// Do something with nextFile
}
}
You can also use
URL url = MyClass.class.getResource("/com/abc/package/resources/");
The following code returns the wanted "folder" as Path regardless of if it is inside a jar or not.
private Path getFolderPath() throws URISyntaxException, IOException {
URI uri = getClass().getClassLoader().getResource("folder").toURI();
if ("jar".equals(uri.getScheme())) {
FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap(), null);
return fileSystem.getPath("path/to/folder/inside/jar");
} else {
return Paths.get(uri);
}
}
Requires java 7+.
I know this is many years ago . But just for other people come across this topic.
What you could do is to use getResourceAsStream() method with the directory path, and the input Stream will have all the files name from that dir. After that you can concat the dir path with each file name and call getResourceAsStream for each file in a loop.
I had the same problem at hands while i was attempting to load some hadoop configurations from resources packed in the jar... on both the IDE and on jar (release version).
I found java.nio.file.DirectoryStream to work the best to iterate over directory contents over both local filesystem and jar.
String fooFolder = "/foo/folder";
....
ClassLoader classLoader = foofClass.class.getClassLoader();
try {
uri = classLoader.getResource(fooFolder).toURI();
} catch (URISyntaxException e) {
throw new FooException(e.getMessage());
} catch (NullPointerException e){
throw new FooException(e.getMessage());
}
if(uri == null){
throw new FooException("something is wrong directory or files missing");
}
/** i want to know if i am inside the jar or working on the IDE*/
if(uri.getScheme().contains("jar")){
/** jar case */
try{
URL jar = FooClass.class.getProtectionDomain().getCodeSource().getLocation();
//jar.toString() begins with file:
//i want to trim it out...
Path jarFile = Paths.get(jar.toString().substring("file:".length()));
FileSystem fs = FileSystems.newFileSystem(jarFile, null);
DirectoryStream<Path> directoryStream = Files.newDirectoryStream(fs.getPath(fooFolder));
for(Path p: directoryStream){
InputStream is = FooClass.class.getResourceAsStream(p.toString()) ;
performFooOverInputStream(is);
/** your logic here **/
}
}catch(IOException e) {
throw new FooException(e.getMessage());
}
}
else{
/** IDE case */
Path path = Paths.get(uri);
try {
DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);
for(Path p : directoryStream){
InputStream is = new FileInputStream(p.toFile());
performFooOverInputStream(is);
}
} catch (IOException _e) {
throw new FooException(_e.getMessage());
}
}
Another solution, you can do it using ResourceLoader like this:
import org.springframework.core.io.Resource;
import org.apache.commons.io.FileUtils;
#Autowire
private ResourceLoader resourceLoader;
...
Resource resource = resourceLoader.getResource("classpath:/path/to/you/dir");
File file = resource.getFile();
Iterator<File> fi = FileUtils.iterateFiles(file, null, true);
while(fi.hasNext()) {
load(fi.next())
}
If you are using Spring you can use org.springframework.core.io.support.PathMatchingResourcePatternResolver and deal with Resource objects rather than files. This works when running inside and outside of a Jar file.
PathMatchingResourcePatternResolver r = new PathMatchingResourcePatternResolver();
Resource[] resources = r.getResources("/myfolder/*");
Then you can access the data using getInputStream and the filename from getFilename.
Note that it will still fail if you try to use the getFile while running from a Jar.
As the other answers point out, once the resources are inside a jar file, things get really ugly. In our case, this solution:
https://stackoverflow.com/a/13227570/516188
works very well in the tests (since when the tests are run the code is not packed in a jar file), but doesn't work when the app actually runs normally. So what I've done is... I hardcode the list of the files in the app, but I have a test which reads the actual list from disk (can do it since that works in tests) and fails if the actual list doesn't match with the list the app returns.
That way I have simple code in my app (no tricks), and I'm sure I didn't forget to add a new entry in the list thanks to the test.
Below code gets .yaml files from a custom resource directory.
ClassLoader classLoader = this.getClass().getClassLoader();
URI uri = classLoader.getResource(directoryPath).toURI();
if("jar".equalsIgnoreCase(uri.getScheme())){
Pattern pattern = Pattern.compile("^.+" +"/classes/" + directoryPath + "/.+.yaml$");
log.debug("pattern {} ", pattern.pattern());
ApplicationHome home = new ApplicationHome(SomeApplication.class);
JarFile file = new JarFile(home.getSource());
Enumeration<JarEntry> jarEntries = file.entries() ;
while(jarEntries.hasMoreElements()){
JarEntry entry = jarEntries.nextElement();
Matcher matcher = pattern.matcher(entry.getName());
if(matcher.find()){
InputStream in =
file.getInputStream(entry);
//work on the stream
}
}
}else{
//When Spring boot application executed through Non-Jar strategy like through IDE or as a War.
String path = uri.getPath();
File[] files = new File(path).listFiles();
for(File file: files){
if(file != null){
try {
InputStream is = new FileInputStream(file);
//work on stream
} catch (Exception e) {
log.error("Exception while parsing file yaml file {} : {} " , file.getAbsolutePath(), e.getMessage());
}
}else{
log.warn("File Object is null while parsing yaml file");
}
}
}
Took me 2-3 days to get this working, in order to have the same url that work for both Jar or in local, the url (or path) needs to be a relative path from the repository root.
..meaning, the location of your file or folder from your src folder.
could be "/main/resources/your-folder/" or "/client/notes/somefile.md"
Whatever it is, in order for your JAR file to find it, the url must be a relative path from the repository root.
it must be "src/main/resources/your-folder/" or "src/client/notes/somefile.md"
Now you get the drill, and luckily for Intellij Idea users, you can get the correct path with a right-click on the folder or file -> copy Path/Reference.. -> Path From Repository Root (this is it)
Last, paste it and do your thing.
Simple ... use OSGi. In OSGi you can iterate over your Bundle's entries with findEntries and findPaths.
Inside my jar file I had a folder called Upload, this folder had three other text files inside it and I needed to have an exactly the same folder and files outside of the jar file, I used the code below:
URL inputUrl = getClass().getResource("/upload/blabla1.txt");
File dest1 = new File("upload/blabla1.txt");
FileUtils.copyURLToFile(inputUrl, dest1);
URL inputUrl2 = getClass().getResource("/upload/blabla2.txt");
File dest2 = new File("upload/blabla2.txt");
FileUtils.copyURLToFile(inputUrl2, dest2);
URL inputUrl3 = getClass().getResource("/upload/blabla3.txt");
File dest3 = new File("upload/Bblabla3.txt");
FileUtils.copyURLToFile(inputUrl3, dest3);

Categories

Resources