On Windows I have just converted my application installer from Izpack to JPackage and because of the structure created by JPackage I had to make some code changes to allow my application to find various config files, basically they are copied from app folder to C:\Users\Username\Appdata.... on first start (but bit more complicated than that).
I am now using JPackage on Linux, and the application copies config files from app to $HOME/.appname. However whereas on Windows app is a subfolder relative to launcher with Linux its now in ../lib/app so the code doesnt work
So I have to make some linux specific changes, but it got me wondering am I doing something very wrong here in order for me to have to make these changes ?
One way to load configuration files is using the classloader: the files are packaged in a jar along with the rest of your application and you load them as a stream, not as a file:
Properties properties = new Properties();
InputStream stream = YourApplication.class.getResourceAsStream("/resources/conf.properties");
properties.load(stream);
The path (example) /resources/conf.properties is relative the .jar file where the file is bundled, so it does no depend on the installation folder.
The above example assumes that you're using a Properties file but this works for any resource you can load as a stream.
Related
I'm trying to develop a cross-platform application that works on Desktop and Android as well using JavaFX and Gluon.
At runtime my code creates a serialized file in my resource folder. I also need to read and write serialized data from/to it.
I managed to work it on desktop, but not on android. Because it have a different file structure I guess.
That is why I try to get the file path dynamically.
Existing resource files, which are created before runtime (and not modified) seems to works fine on both platform.
I tried with new File("src/main/resources/folder/file.ser").getAbsolutePath(); and by trying to access it from my root folder like this: getClass.getResources("/folder/file.ser").getPath();. Both of them works fine on desktop (Windows) but unfortunately Android does not find the file by file path.
An other problem could be that I should not create runtime files in the resource folder but then where should I?
Any idea how can I read and write runtime created files that works both on android and desktop?
(If the information is not enough to help me, I try to reproduce my code in a minimal form and provide further details.)
I think you are on a completely wrong track. Creating or writing to files in the resource folder does not work in general. The idea is that files in the resource folder get packaged into jar files or are otherwise bundled with an application and are not writable at runtime.
What you should do is to create an application folder when your program is launched for the first time. A common practice on desktop is for example to create an invisible folder ".myApp" in the users home directory. On other platforms like Android there are other platform specific naming and location rules, but the concept is the same. At first launch time you can also copy necessary resources from your resource folder into this application folder so that you can edit them at runtime.
Resource files with a path on the class path, could be packed in a jar and should be considered read-only, especially as resources might be cached in some cases. They are not File. They can be captured by URL, URI, Path. The paths are case-sensitive and the path separator is /.
Hence resources can only be used as a template, an initial file. They should be copied to a real File, outside the application.
Path path = Paths.get(System.getProperty("user.home"), ".myapp/file.ser");
Files.createDirectories(path.getParent());
if (Files.exists(path)) {
URL url = MyClass.class.getResource("/folder/file.ser");
Path template = Paths.get(url.toURI());
Files.copy(template, path);
}
Furthermore .ser, a serialized java object, is not a good idea. I would suggest XML
using JAXB with annotations. More readable, maintainable, versionable. No clash between development JRE at your place and deployed JRE at the client.
I deployed a war file onto a Tomcat 7 instance running on a remote Linux machine and I'm getting FileNotFoundExceptions.
One of the referenced jars in the project, which contains code that I did not write, uses several files (which I have included, but it is not finding). These files are located in the classes folder. It appears the classpath I have set for the project is being ignored by this jar. These files that it uses, e.g. .properties files are external to the jar.
Here is an example of how it is invoking the files:
FileOutputStream fos = new FileOutputStream("Key.ser");
I was getting these errors when developing the source project in Eclipse. I was able to configure the project to tell it where to find these files via Run Configurations -> Arguments -> Other but the exported .war file appears to not have this bundled with it, only the source project has it. Now I'm seeing them again when trying to deploy the application to Tomcat on another server via war file.
How do I configure the deployed jar file in the deployed Tomcat 7 webapp to find these files that the jar uses? I am loathe to change the code since I did not write it so am really hoping to avoid this.
I am able to get this to work on a local Tomcat 7 running on Windows instance integrated with Eclipse as explained earlier so I'm wondering if maybe this can be duplicated?
You will not be able to find the file by simply referencing the file name using FileOutputStream. You are correct to place the file in the 'WEB-INF/classes' directory, which will allow it to be located on the classpath.
To load the file, you need to load it as a classpath resource using something similar to this:
String classpathLocation = ""Key.ser"";
URL classpathResource = Thread.currentThread().getContextClassLoader().getResource(classpathLocation);
// Or if you want it as an inputstream:
InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(classpathLocation);
I developed and tested my program on Eclipse Indigo. No problem finding the properties file when run.
I created a runnable jar using Eclipse export.
The program cannot find the properties file when the program is run from the jar.
The properties file is not in the jar, it is in a subdirectory of the directory where the jar is deployed.
As noted above, this directory is on the classpath in the Eclipse run configuration.
C:/myApp/myApp.jar C:/myApp/properties/myApp.properties
props.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("myApp.properties"));
Do I just need to edit the Jar manifest to put the config directory on the classpath?
properties.load(this.getClass().getResourceAsStream(configFileSrc));
This should work.
In my case, configFileSrc = "/apiconfig/appconfig.properties"
I had to add staticresources dir in the build path.
Thanks
Yes. Make sure you specify the path of the config directory relative to the directory where the jar is located. Since you said above that the config directory is a sub directory of the jar location, this will probably just be
<configDir>
where <configDir> is your configuration directory name.
You will also need to use an absolute resource name for the lookup, by adding a slash to the properties file name:
props.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("/myApp.properties"));
This is because the relative name your specify will have the package path of the class appended to it, which you don't want.
Is there any specific reason you need to use the contextClassLoader? A simpler invocation would be to use the classLoader of the class containing the lookup. And, since the class can delegate to it's classLoader, you can simply say:
props.load(getClass().getResourceAsStream("/myApp.properties"));
Finally - have you considered putting the properties file in the jar itself? If it's not going to change at all, this would be a better option. You wouldn't need to specify the classPath in that case. Note that if you want, you could put it in a sub-directory in the jar , but you would have to change the resource path you looked for, and again use an absolute resource name
Here is some info on absolute and relative resource names:
http://docs.oracle.com/javase/1.5.0/docs/guide/lang/resources.html
Hope this helps!
The real problem here is that you need to make up your mind where your application is going to load the properties from when the application is deployed.
(How it works in Eclipse is kind of irrelevant ... unless you expect your users to install Eclipse to run your app!)
If you want it to load the properties from the JAR file, they need to be in the JAR file.
If you want it to load the properties from the file system, they need to be in the file system. That means you have two subsidiary issues:
Where in the file system will your application look? Bear in mind that this has to work for all of the OS platforms you support, and that different OSes have different conventions for where "configuration settings* should be stored.
Is there a default version of the properties file, and how will you install it?
If you want to load it from the CLASSPATH and not the JAR file, that means you cannot make your application an executable JAR. (Running as java -jar ... will ignore all attempts to set an external classpath.)
If you opt for a non-executable JAR file AND loading the properties from the classpath, you have more problems:
How do you set the classpath?
How do you install the properties file "alongside" the JAR file?
Are there security concerns with a (potentially) user writable directory on the application's classpath?
This is probably the worst option.
Once you have decided how (from where) your application needs to load its properties when deployed, the code for implementing your scheme is relatively straightforward. (Modulo the problems mentioned above.)
You could even combine a couple of options; e.g. look for the properties file in the file system and then fall back to a "default" properties file in the JAR file. Or you could make the file system location of the properties file a command line option or supply it via an environment variable or via -D and the system properties.
I'm currently setting up our project to deploy to users via java webstart instead of the current set up in which users run a .bat file from a shared network drive. When the application is run, it is passed an properties file argument which contains information such as database credentials which allows for the switching between different environments etc.
I would like to know if there is a way to specify this in the JNLP file and have webstart pull down the properties file from the webserver. I've spent a decent amount of time investigating this online and the only thing I could come up with was to simply specify the filename as an argument like so:
<application-desc main-class="Main">
<argument>example.properties</argument>
</application-desc>
and then include a separate link which let users download the properties file from the server. The issue with this is that if the JNLP file and the properties file arent downloaded to the same directory (which seems to be the default behaviour in IE) then the whole application falls over. Is there a way of bundling my properties file along with the other resources in the JNLP file or am I going about this in a completely incorrect fashion? Any help would be greatly appreciated!
You can set the properties in the .jnlp file itself instead of having a separate properties file.
http://www.coderanch.com/t/200871/JNLP-Web-Start/java/Properties-files-JNLP
Other solutions are:
Put the properties file inside the main jar or in a separate jar and add it as a resource.
Put location of the properties file as a property or as a main argument and download it from the program itself.
Looks like the jnlp is an XML file which includes a list of jar files to be placed in your classpath.
If you include a properties file in one of your jars then you can read it using this.getClass().getClassLoader().getResourceAsStream("mypropsname.properties").
Do all your users need the same properties file?
in my Java project I am using an H2 in-memory database, for which I have to load the JDBC driver when I initialize my application. I want/need to load the H2 .jar file dynamically, so I do the following:
String classname = "org.h2.Driver";
URL u = new URL("jar:file:libs/h2.jar!/");
URLClassLoader ucl = new URLClassLoader(new URL[] { u });
Driver d = (Driver) Class.forName(classname, true, ucl).newInstance();
DriverManager.registerDriver(new DriverShim(d));
When I put the H2 .jar file into a "libs" folder outside my Java source code folder (that is, in Eclipse, this "libs" directory is on the same level as the "src" folder), then this approach works fine. However, unfortunately I have to put this H2 .jar file into a folder within the source code folder tree, but below the main class folder.
For example, my Java package structure looks like this in Eclipse:
<project>/src/my/app/MyApp.java // main class of my application
<project>/src/my/app/sub/package/h2.jar // how to access this?
<project>/libs/h2.jar // loading from here works
I know this is stupid, but unfortunately I have to work with this strange setup. But what I don't know: how can I edit my Java code (listed above) in order to work with this setup?
EDIT: This has to work outside Eclipse as well, so adding the JAR file to the Java Build Path in Eclipse is no option for me.
EDIT2: I already tried to load "jar:file:my/app/sub/package/h2.jar!/", but that did not work for me.
Thanks in advance for all helpful ideas!
Kind regards, Matthias
In some frameworks referring to files inside JARs can be done using the classpath: prefix. I doubt URLClassLoader supports it natively, but it's worth a try (e.g. classpath:/my/app/sub/package/h2.jar). But since that doesn't work with URLClassLoader, here are other ways:
One way to do it would be to write your own ClassLoader which reads the JAR file from classpath (using getResourceAsStream), uncompresses it (using ZipInputStream) to memory (e.g. a map of byte arrays) and loads the classes from there.
Another, slightly easier way, is to read the JAR file from classpath and write it into a temporary file. Then you can use the plain URLClassLoader to load classes from it. This has the disadvantage that the file must be written to a file and the file probably cannot be removed until the JVM exits (unless using Java 7 or higher).
I'm using the second approach (copying to a temp file) in one project, though I'm using it to launch an external process. I would be curious to hear why you have such a requirement. If it's just a matter of having the whole application in one JAR, there are numerous simpler methods for achieving that (Maven Assembly Plugin, Maven Shade Plugin, Jar Jar Links, One-JAR to name a few).
No it's not a homework, but an online build system that uses my classes under my/app/* and several other classes (not from me) to automatically build the whole solution. Anyway, I can't give you more details on the internals of this system, as I don't know them. As said, I simply have to live with it, and that is why I am asking here...
Sounds like you are working in a WTF environment (does it have a name?), so here are some ways to start hacking around it:
Find out more about your environment, especially absolute file paths of the following: directory where the source files are saved, directory where the generated .class files are saved, and the current working directory when the program is run.
If you can get any kind of output of what your program prints during runtime, you can put into your application some debug code where you use File.listFiles() to crawl the machine's directory trees. If you can get output only from what happens when compiling, it might be possible to execute your own code during compile by creating your own annotation processor (apt is part of javac since Java 6), though I'm not sure whether the annotation processor must be compiled first separately.
The working directory can be read from the user.dir system property and the location of class files can be probably gotten from the java.class.path system property (unless custom class loaders are used). There is no guarantee that a JAR file in the source directory would be copied to the classpath, so you might need to do some looking around.
Then when you know the file path of the JAR file, then you can get an URL to it using new File("path/to/h2.jar").toURI().toURL() which you can then pass to URLClassLoader.
If nothing else works, upload the source code of the libraries and compile them together with your project.
In the long run, try to replace the WTF build environment with one that uses a standard build tool (such as Maven) and a common CI server (such as Jenkins). It's normal for projects to have lots of library dependencies, so you shouldn't need to hack around a build environment to use them.