I have two resources folders.
src - here are my .java files
resources - here are my resources files (images, .properties) organized in folders (packages).
Is there a way to programmatically add another .properties file in that resources folder?
I tried something like this:
public static void savePropertiesToFile(Properties properties, File propertiesFile) throws IOException {
FileOutputStream out = new FileOutputStream(propertiesFile);
properties.store(out, null);
out.close();
}
and before that created:
new File("/folderInResources/newProperties.properties");
But it looks for that path on the file system. How can I force it to look in the resources folder?
EDIT: Let me say what is it about. I have a GUI application and I support 2 languages (2 .properties files in resources folder). Now I added a option that user can easily translate application and when he finishes I save that new .properties on a disk in some hidden folder and read it from there. But I was hoping I could save that new .properties files (new language) next to the current languages (resources folder). I have a static Messages class which knows how to load resources both from the disk and both the default ones in resources folder. But if user takes this .jar file on some other machine, he would't have that new languages since they are on disk on that computer, not inside .jar file.
Java 8 Solution
Path source = Paths.get(this.getClass().getResource("/").getPath());
Path newFolder = Paths.get(source.toAbsolutePath() + "/newFolder/");
Files.createDirectories(newFolder);
This will surely create new folder in resource folder. but you will find new folder in your target runtime.
which will be ProjectName/target/test-classes/newFolder. if you are running this code in test case. Other wise it would be in target/classes
Don't try to find new folder in your src/resources.
it will be surely in target/test-classes or target/classes.
As other people have mentioned, resources are obtained through a ClassLoader. What the two current responses have failed to stress, however, is these points:
ClassLoaders are meant to abstract the process of obtaining classes and other resources. A resource does not have to be a file in a filesystem; it can be a remote URL, or anything at all that you or somebody else might implement by extending java.lang.ClassLoader.
ClassLoaders exist in a child/parent delegation chain. The normal behavior for a ClassLoader is to first attempt to obtain the resource from the parent, and only then search its own resources—but some classloaders do the opposite order (e.g., in servlet containers). In any case, you'd need to identify which classloader's place for getting stuff you'd want to put stuff into, and even then another classloader above or below it might "steal" your client code's resource requests.
As Lionel Port points out, even a single ClassLoader may have multiple locations from which it loads stuff.
ClassLoaders are used to, well, load classes. If your program can write files to a location where classes are loaded from, this can easily become a security risk, because it might be possible for a user to inject code into your running application.
Short version: don't do it. Write a more abstract interface for the concept of "repository of resource-like stuff that I can get stuff from," and subinterface for "repository of resource-like stuff that I can get stuff from, but also add stuff from." Implement the latter in a way that both uses ClassLoader.getContextClassLoader().getResource() (to search the classpath) and, if that fails, uses some other mechanism to get stuff that the program may have added from some location.
Problem would be the classpath can contain multiple root directories so distinguishing which one to store would be hard without an existing file or directory.
If you have an existing file loaded.
File existingFile = ...;
File parentDirectory = existingFile.getParentFile();
new File(parentDirectory, "newProperties.properties");
Otherwise try an get a handle on a directory you know is unique in your resources directory. (Not sure if this works)
URL url = this.getClass().getResource("/parentDirectory");
File parentDirectory = new File(new URI(url.toString()));
new File(parentDirectory, "newProperties.properties");
Cut the main project folder of the compiled subfolders ("/target/classes", "target/test-classes") and you have the basic path to reconstruct your project folders with:
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
public class SubfolderCreator {
public static void main(String... args) throws URISyntaxException, IOException {
File newResourceFolder = createResourceSubFolder("newFolder");
}
private static File createResourceSubFolder(String folderName) throws URISyntaxException, IOException {
java.net.URL url = SubfolderCreator.class.getResource("/EXISTING_SUBFOLDER/");
File fullPathToSubfolder = new File(url.toURI()).getAbsoluteFile();
String projectFolder = fullPathToSubfolder.getAbsolutePath().split("target")[0];
File testResultsFolder = new File(projectFolder + "src/test/resources/" + folderName);
if (!testResultsFolder.exists()) {
testResultsFolder.mkdir();
}
return testResultsFolder;
}
}
The following code writes into the classes directory, along with the class files.
As others have noted, beware of overwriting class files. Best to put your new files into a separate directory; however, that directory needs to already exist. To create it, create a sub-directory within the resources in the source, perhaps containing an empty file. For example src\main\resources\dir\empty.txt.
public class WriteResource {
public static void main(String[] args) throws FileNotFoundException {
String thing = "Text to write to the file";
String dir = WriteResource.class.getResource("/").getFile();
//String dir = WriteResource.class.getResource("/dir").getFile();
OutputStream os = new FileOutputStream(dir + "/file.txt");
final PrintStream printStream = new PrintStream(os);
printStream.println(thing);
printStream.close();
}
}
This does the trick, but I'd be nervous about deploying this outside of a strictly controlled environment. I don't really like the idea of unauthorised persons writing to my classes directory!
Based on the below code you get or create a file and use it as a util method.
public static File getFileFromResource(String filePath) {
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
return new File(Objects.requireNonNull(classloader.getResource(filePath)).getFile());
}
Project structure
You should specify the relative path to file or folder. Usage:
getFileFromResource("application.properties");
Output:
File with path C:\Users\????\IdeaProjects\back-end\target\classes\application.properties
Related
So I have a project, and this is one of the demands:
You should have a class named Project3, containing a main method.
This program reads the levels information from a file whose name is
specified as a command-line parameter (The file should also be
relative to the class-path as described here:)
All the file names specified in the levels and block definition files
should be relative to the class path. The reason we want them to be
relative to the class path is that later we will be able to read the
files from inside a jar, something we can not do with regular File
references.
To get an input stream relative to the class path (even if it's inside
a jar), use the following:
InputStream is =
ClassLoader.getSystemClassLoader().getResourceAsStream("image.png");
The idea is to keep a folder with files(definitions and images) and
then add that folder to the class path when running the JVM:
java -cp bin:resources ... If you don't add the resources folder to
you class path you wont be able to load them with the command from
above.
When run without parameters, your program should read a default level
file, and run the game accordingly. The location of the default level
file should be hard-coded in your code, and be relative to the
classpath_.
When run without parameters, your program should read a default level file, and run the game accordingly. The location of the default level file should be hard-coded in your code, and be relative to the classpath_.
The part of the code that handles the input is:
public Void run() throws IOException {
LevelReader level = new LevelReader();
List<level> chosenLevels = new ArrayList<>();
if (args.length >= 1) {
File f = new File(args[0]);
if (f.exists()) {
chosenLevels = level.makeLevel(args[0]);
}
}
if (chosenLevels.size() == 0) {
game.runLevels(defaultLevels);
} else {
game.runLevels(chosenLevels);
}
return null;
}
So my question is:
An argument should be the full path of a file which means:
D:\desktop\level3.txt
Is it possible to read a file from every location on my computer?
Because right now I can do it only if my text file is in the
project's directory (not even in the src folder).
I can't understand the rest of their demands. What does is mean "should be hard-coded in your code, and be relative to the
classpath_." and why is it related to InputStream method(?)
I'm confused all over this.
Thanks.
A classpath resource is not the same as a file.
As you have correctly stated, the full path of a file is something like D:\desktop\level3.txt.
But if ever want to distribute your application so it can run on other computers, which probably won’t have that file in that location, you have two choices:
Ask the user to tell the program where to find the file on their computer.
Bundle the file with the compiled program.
If you place a non-.class file in the same place as .class files, it’s considered a resource. Since you don’t know at runtime where your program’s class files are located,¹ you use the getResource or getResourceAsStream method, which is specifically designed to look in the classpath.
The getResource* methods have the additional benefit that they will work both when you are developing, and when the program is packaged as a .jar file. Individual entries in a .jar file are not separate files and cannot be read using the File or FileInputStream classes.
If I understand your assignment correctly, the default level file should be an application resource, and the name of that resource is what should be hard-coded in your program. Something like:
InputStream is;
if (args.length > 0) {
is = new BufferedInputStream(
new FileInputStream(args[0]));
} else {
// No argument provided, so use program's default level data.
is = ClassLoader.getSystemClassLoader().getResourceAsStream("defaultlevel.txt");
}
chosenLevels = level.makeLevel(is);
¹ You may find some pages that claim you can determine the location of a running program’s code using getProtectionDomain().getCodeSource(), but getCodeSource() may return null, depending on the JVM and ClassLoader implementation, so this is not reliable.
To answer your first question, it doesn't seem like they're asking you to read from anywhere on disk, just from within your class path. So that seems fine.
The second question, "What does is mean 'should be hard-coded in your code, and be relative to the classpath'?". You are always going to have a default level file in your project directory. Define the path to this file as a String in your program and that requirement will be satisfied. It's related to the InputStream because the stream requires a location to read in from.
This question already has answers here:
java.io.FileNotFoundException: the system cannot find the file specified
(8 answers)
Closed 4 years ago.
I have a csv file in the same path as everything else. Now, when I try to create a File object:
public void getMenu() {
File fileMenu = new File("FastFoodMenu.csv");
try {
Scanner inputStream = new Scanner(fileMenu);
while (inputStream.hasNext()) {
String data = inputStream.next();
System.out.println(data);
}
} catch (FileNotFoundException ex) {
Logger.getLogger(FileHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
it throws a FileNotFoundException.
the absolute path to all files in the project is:
C:\Users\kenyo\Documents\NetBeansProjects\OrderFastFood\src\fastfoodorderingsystem
I also checked the name a couple of times. fileMenu.exists() returns false.
First, in your root/working directory (in your case it's the folder containing your project), create a folder called 'menus', here you can store all your menus (so you can play around with multi-file input).
Second, move your FastFoodMenu.csv file to that menus folder.
The FastFoodMenu.csv relative path should now look like this: OrderFastFood\menus\FastFoodMenu.csv.
Third, get your working directory from the System properties. This is the folder in which your program is working in. Then, get a reference (File object) to the menus folder.
Lastly, get a reference to the file in question inside the menu folder. When you get to multi-file reading (and at some point, multi-folder reading), you're gonna want to get the files inside the menu folder as a list so that's why I say to just get the menus folder as it's own reference (or just get the file without the isolated reference to the parent aka '\menus\').
So your code should really look like this:
public void getMenu() {
final File workingDir = File(System.getProperty("user.dir"));
final File menusDir = File(workingDir, "menus");
final File fastFoodMenu = File(menusDir, "FastFoodMenu.csv");
try {
final FileInputStream fis = new FileInputStream(fastFoodMenu);
final BufferedInputStream bs = new BufferedInputStream(fis);
while((l = bs.readLine()) != null) {
System.out.println(l);
}
} catch(FileNotFoundException e) {
System.out.println(e.getMessage());
e.printStackTrace()
}
}
This is all psuedocode but that should at least get you started. Make sure to use BufferedInputStream for efficiency, and when reading files, always pass them into FileInputStream's. It's much better than using the Scanner class. I should also mention that when creating a File object, you're not actually creating a file. What you're doing is your're creating an object, giving it the data you want it to have (such as whether it's a folder, and if it is, what child files/folders do you want it to have, whether it's protected or not, hidden or not, etc) before actually telling the system to create the file with everything else.
Your csv file is probably at the wrong place. You're just specifying the file name, which is a relative path.
Relative paths are always resolved against the working directory of your application, not against the directory where your source file(s) are.
To solve the issue, you can
move the files to the real working directory.
use an absolute path (not advisable!)
specify the folder of your data files as program argument or in a config file (in your working directory)
put the files somewhere into the classpath of your application and load them from there via classloader. Note that files that are in your classpath are usually packed with your application and hence not easily modifiable by the user, so this solution doesn't work if the file must be changed by the user.
I have two classes. One is intended to be a common library and another a client that consumes from the library.
The library is structured as below:
public class Library
{
public Library()
{
String pathToResourceA="src/main/resources/A.xls";// A.xls is present within resources
String key="apples";
Resource res= loadResourceBasedOnDoc(pathToResource,key);
...//process resource
}
}
It's corresponding test class
public class LibraryTest
{
#Test
public void testLibrary()
{
new Library();// works as expected- the test passes and the resource is loaded.//ie. A.xls is found in the right place
}
}
However, when I try to access the library from my client in the following manner
import packagename.Library
public class Client{
Library lib;
public Client()
{
lib= new Library();// throws a FileNotFoundException!
}
}
I get a FileNotFoundException. I'm guessing this is something to do with defining the right value to pathToResourceA in class A but cant figure out what it is. Any thoughts would be appreciated. Thanks!
Code for loadResourcesBasedonDoc
protected Resource loadResourceBasedOnDoc(String filename,String password)
{
InputStream in = null;
try {
in = new FileInputStream(filename);
//further in is processed...
The problem is that you are using relative file paths:
String pathToResourceA="src/main/resources/A.xls";// A.xls is present within resources
This is then processed in the loadResourceBasedOnDoc method as follows:
in = new FileInputStream(filename);
This call looks for the file (src/main/resources/A.xls) on the file system. Because the path is relative, it looks for the file in a directory that is relative to the current working directory. In your unit test, the current working directory is probably the directory from which the test is launched. Assuming that is the root of your project, the test likely finds A.xls on the file system within your project.
To solve this, I recommend using full pathnames in the loadResourceBasedOnDoc as follows:
String programLaunchDir = System.getProperty("user.dir")
in = new FileInputStream(programLaunchDir + File.separator + filename);
The user.dir system property should be the current working directory. So if you can enforce that the program is launched from that dir, you are all set. Otherwise, you might want to wrap you program in a batch file or shell script that passes the directory to the program.
I am using maven and the standard directory layout. So I have added a testdata.xml file in the src/test/resources folder, and I also added it as:
.addAsWebInfResource("testdata.xml", "testdata.xml")
in the deployment method, and I have confirmed that it is there. This will make the file appear in /WEB-INF/testdata.xml. Now I need to have a reference to this file in my code and I tried several different getClass().getResourceAsStream(...) and failing again and again so I need some advise now.
I need it for my DBUnit integration test. Is this not possible?
Option A) Use ServletContext.getResourceXXX()
You should have a Aquillarian MockHttpSession and a MockServletContext. E.g.:
#Test
public void myTest()
{
HttpSession session = new MockHttpSession(new MockServletContext());
ServletLifecycle.beginSession(session);
..testCode..
// You can obtain a ServletContext (will actually be a MockServletContext
// implementation):
ServletContext sc = session.getServletContext();
URL url = sc.getResource("/WEB-INF/testdata.xml")
Path resPath = new Path(url);
File resFile = new File(url);
FileReader resRdr = new FileReader(resFile);
etc...
..testCode..
ServletLifecycle.endSession(session);
}
You can create resource files & subdirectories in:
the web module document root - resources are accessible from the browser and from classes
WEB-INF/classes/ - resources are accessible to classes
WEB-INF/lib/*.jar source jar - accessible to classes
WEB-INF/lib/*.jar dedicated resource-only jar - accessible to classes
WEB-INF/ directly within directory - accessible to classes. This is what you are asking for.
In all cases the resource can be accessed via:
URL url = sc.getResource("/<path from web doc root>/<resourceFileName>");
OR
InputStream resIS = sc.getResourceAsStream("/<path from web doc root>/<resourceFileName>");
>
These will be packaged into the WAR file and may be exploded into directories on the deployed app server OR they may stay within the WAR file on the app server. Either way - same behaviour for accessing resources: use ServletContext.getResourceXXX().
Note that as a general principle, (5) the top-level WEB-INF directory itself is intended for use by the server. It is 'polite' not to put your web resources directly in here or create your own directory directly in here. Instead, better to use (2) above.
JEE5 tutorial web modules
JEE6 tutorial web modules
Option B): Use Class.getResourceXXX()
First move the resource out of WEB-INF folder into WEB-INF/classes (or inside a jar WEB-INF/lib/*.jar).
If your test class is:
com.abc.pkg.test.MyTest in file WEB-INF/classes/com/abc/pkg/test/MyTest.class
And your resource file is
WEB-INF/classes/com/abc/pkg/test/resources/testdata.xml (or equivalent in a jar file)
Access File using Relative File Location, via the Java ClassLoader - finds Folders/Jars relative to Classpath:
java.net.URL resFileURL = MyTest.class.getResource("resources/testdata.xml");
File resFile = new File(fileURL);
OR
InputStream resFileIS =
MyTedy.class.getResourceAsStream("resources/testdata.xml");
Access File Using full Package-like Qualification, Using the Java ClassLoader - finds Folders/Jars relative to Classpath:
java.net.URL resFileURL = MyTest.class.getResource("/com/abc/pkg/test/resources/testdata.xml");
File resFile = new File(fileURL);
OR
InputStream resFileIS =
MyTest.class.getResourceAsStream("/com/abc/pkg/test/resources/testdata.xml");
OR
java.net.URL resFileURL = MyTest.class.getClassLoader().getResource("com/abc/pkg/test/resources/testdata.xml");
File resFile = new File(fileURL);
OR
InputStream resFileIS =
MyTest.class.getClassLoader().getResourceAsStream("com/abc/pkg/test/resources/testdata.xml");
Hope that nails it! #B)
The way to access files under WEB-INF is via three methods of ServletContext:
getResource("/WEB-INF/testdata.xml") gives you a URL
getResourceAsStream gives you an input stream
getRealPath gives you the path on disk of the relevant file.
The first two should always work, the third may fail if there is no direct correspondence between resource paths and files on disk, for example if your web application is being run directly from a WAR file rather than an unpacked directory structure.
Today I was struggling with the same requirement and haven't found any full source sample, so here I go with smallest self contained test I could put together:
#RunWith(Arquillian.class)
public class AttachResourceTest {
#Deployment
public static WebArchive createDeployment() {
WebArchive archive = ShrinkWrap.create(WebArchive.class).addPackages(true, "org.apache.commons.io")
.addAsWebInfResource("hello-kitty.png", "classes/hello-kitty.png");
System.out.println(archive.toString(true));
return archive;
}
#Test
public void attachCatTest() {
InputStream stream = getClass().getResourceAsStream("/hello-kitty.png");
byte[] bytes = null;
try {
bytes = IOUtils.toByteArray(stream);
} catch (IOException e) {
e.printStackTrace();
}
Assert.assertNotNull(bytes);
}
}
In your project hello-kitty.png file goes to src/test/resources. In the test archive it is packed into the /WEB-INF/classes folder which is on classpath and therefore you can load it with the same class loader the container used for your test scenario.
IOUtils is from apache commons-io.
Additional Note:
One thing that got me to scratch my head was related to spaces in path to my server and the way getResourceAsStream() escapes such special characters: sysLoader.getResource() problem in java
Add this class to your project:
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
public class Init {
private static final String WEB_INF_DIR_NAME="WEB-INF";
private static String web_inf_path;
public static String getWebInfPath() throws UnsupportedEncodingException {
if (web_inf_path == null) {
web_inf_path = URLDecoder.decode(Init.class.getProtectionDomain().getCodeSource().getLocation().getPath(), "UTF8");
web_inf_path=web_inf_path.substring(0,web_inf_path.lastIndexOf(WEB_INF_DIR_NAME)+WEB_INF_DIR_NAME.length());
}
return web_inf_path;
}
}
Now wherever you want to get the full path of the file "testdata.xml" use this or similar code:
String testdata_file_location = Init.getWebInfPath() + "/testdata.xml";
I have this issue of accessing a file in one of the parent directories.
To explain, consider the following dir structure:-
C:/Workspace/Appl/src/org/abc/bm/TestFile.xml
C:/Workspace/Appl/src/org/abc/bm/tests/CheckTest.java
In the CheckTest.java I want to create a File instance for the TestFile.xml
public class Check {
public void checkMethod() {
File f = new File({filePath value I want to determine}, "TestFile.xml");
}
}
I tried a few things with getAbsolutePath() and the getParent() etc but was getting a bit complicated and frankly I think I messed it up.
The reason I don't want to use "C:/Workspace/Appl/src/org/abc/bm" while creating the File instance is because the C:/Workspace/Appl is not fixed and in all circumstances will be different at runtime and basically I don't want to hard-code.
What could be the easiest and cleaner way to achieve this ?
Thank you.
You should load it from Classpath in this case.
In your CheckTest.java, try
FileInputStream fileIs = new FileInputStream(CheckTest.class.getClassLoader().getResourceAsStream("org/abc/bm/TestFile.xml");
Use System.getProperty to get the base dir or you set the base.dir during application launch
java -Dbase.dir=c:\User\pkg
System.getProperty("base.dir");
and use
System.getProperty("file.separator");
What could be the easiest and cleaner way to achieve this ?
For accessing static resources use:
URL urlToResource = this.getClasS().getResource("path/to/the.resource");
If the resource is expected to change, write it to a sub-directory of user.home, where it is easy to locate later.
First of all, you can't get a reference to the source file path on runtime.
But, you can access the resrources included at your classpath (where you complied .class files will be).
Normally, your compiler will copy the xml file included at your srouce directory into the build directory, so at last, you could end up having something like this:
C:/Workspace/Appl/classes/org/abc/bm/TestFile.xml
C:/Workspace/Appl/classes/org/abc/bm/tests/CheckTest.class
Then, with your classpath pointing to the compiled classes root dir, you get the resources from this directory, using the ClassLoader.getResource method (or the equivalent Class.getResource() method).
public class Check {
public void checkMethod() {
java.net.URL fileURL=this.getClass().getResource("/org/abc/bm/tests/TestFile.xml");
File f=new File( fileURL.toURI());
}
}
One could do this:
String pathOfTheCurrentClass = this.getClass().getResource(".").getPath();
File file = new File(pathOfTheCurrentClass + "/..", "Testfile.xml");
or
String pathOfTheCurrentClass = this.getClass().getResource(".").getPath();
File filePath = new File(pathOfTheCurrentClass);
File file = new File(filePath.getParent(), "Testfile.xml");
But as Tomas Naros points out this gives you the file located in the build path.
Did you try
URL some=Test.class.getClass().getClassLoader().getResource("org/abc/bm/TestFile.xml");
File file = new File(some.getFile());