Getting a directory inside a .jar - java

I am trying to access a directory inside my jar file.
I want to go through every of the files inside the directory itself. I tried, for example, using the following:
URL imagesDirectoryURL=getClass().getClassLoader().getResource("Images");
if(imagesFolderURL!=null)
{
File imagesDirectory= new File(imagesDirectoryURL.getFile());
}
If I test this applet, it works well. But once I put the contents into the jar, it doesn't because of several reasons.
If I use this code, the URL always points outside the jar, so I have to put the Images directory there.
But if I use new File(imagesDirectoryURL.toURI());, it doesn't work inside the jar because I get the error URI not hierarchical. I am sure the directory exists inside the jar.
How am I supposed the get the contents of Images inside the jar?

Here is a solution which should work given that you use Java 7... The "trick" is to use the new file API. Oracle JDK provides a FileSystem implementation which can be used to peek into/modify ZIP files, and that include jars!
Preliminary: grab System.getProperty("java.class.path", "."), split against :; this will give you all entries in your defined classpath.
First, define a method to obtain a FileSystem out of a classpath entry:
private static final Map<String, ?> ENV = Collections.emptyMap();
//
private static FileSystem getFileSystem(final String entryName)
throws IOException
{
final String uri = entryName.endsWith(".jar") || entryName.endsWith(".zip"))
? "jar:file:" + entryName : "file:" + entryName;
return FileSystems.newFileSystem(URI.create(uri), ENV);
}
Then create a method to tell whether a path exists within a filesystem:
private static boolean pathExists(final FileSystem fs, final String needle)
{
final Path path = fs.getPath(needle);
return Files.exists(path);
}
Use it to locate your directory.
Once you have the correct FileSystem, use it to walk your directory using .getPath() as above and open a DirectoryStream using Files.newDirectoryStream().
And don't forget to .close() a FileSystem once you're done with it!
Here is a sample main() demonstrating how to read all the root entries of a jar:
public static void main(final String... args)
throws IOException
{
final Map<String, ?> env = Collections.emptyMap();
final String jarName = "/opt/sunjdk/1.6/current/jre/lib/plugin.jar";
final URI uri = URI.create("jar:file:" + jarName);
final FileSystem fs = FileSystems.newFileSystem(uri, env);
final Path dir = fs.getPath("/");
for (Path entry : Files.newDirectoryStream(dir))
System.out.println(entry);
}

Paths within Jars are paths, not actual directories as you can use them on a file system. To get all resources within a particular path of a Jar file:
Gain an URL pointing to the Jar.
Get an InputStream from the URL.
Construct a ZipInputStream from the InputStream.
Iterate each ZipEntry, looking for matches to the desired path.
..will I still be able to test my Applet when it's not inside that jar? Or will I have to program two ways to get my Images?
The ZipInputStream will not work with loose resources in directories on the file system. But then, I would strongly recommend using a build tool such as Ant to build (compile/jar/sign etc.) the applet. It might take an hour or so to write the build script & check it, but thereafter you can build the project by a few keystrokes and a couple of seconds.
It would be quite annoying if I always have to extract and sign my jar if I want to test my Aplet
I'm not sure what you mean there. Where does the 'extract' come into it? In case I was not clear, a sand-boxed applet can load resources this way, from any Jar that is mentioned in the archive attribute. Another thing you might do, is to separate the resource Jar(s) from the applet Jar. Resources typically change less than code, so your build might be able to take some shortcuts.
I think I really have to consider putting my Images into a seperate directory outside the jar.
If you mean on the server, there will be no practical way to get a listing of the image files short of help from the server. E.G. Some servers are insecurely set up to produce an HTML based 'file list' for any directory with no default file (such as an index.html).
I have only got one jar, in which my classes, images and sounds are.
OK - consider moving the sounds & images into a separate Jar. Or at the very least, put them in the Jar with 'no compression'. While Zip comression techniques work well with classes, they are less efficient at compressing (otherwise already compressed) media formats.
I have to sign it because I use the "Preferences" class to save user settings."
There are alternatives to the Preferences for applets, such as cookies. In the case of plug-in 2 architecture applet, you can launch the applet (still embedded in the browser) using Java Web Start. JWS offers the PersistenceService. Here is my small demo. of the PersistenceService.
Speaking of JWS, that brings me to: Are you absolutely certain this game would be better as an applet, rather than an app (e.g. using a JFrame) launched using JWS?
Applets will give you no end of stress, and JWS has offered the PersistenceService since it was introduced in Java 1.2.

You can use the PathMatchingResourcePatternResolver provided by Spring.
public class SpringResourceLoader {
public static void main(String[] args) throws IOException {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// Ant-style path matching
Resource[] resources = resolver.getResources("/Images/**");
for (Resource resource : resources) {
System.out.println("resource = " + resource);
InputStream is = resource.getInputStream();
BufferedImage img = ImageIO.read(is);
System.out.println("img.getHeight() = " + img.getHeight());
System.out.println("img.getWidth() = " + img.getWidth());
}
}
}
I didn't do anything fancy with the returned Resource but you get the picture.
Add this to your maven dependency (if using maven):
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
This will work directly from within Eclipse/NetBeans/IntelliJ and in the jar that's deployed.
Running from within IntelliJ gives me the following output:
resource = file [C:\Users\maba\Development\stackoverflow\Q12016222\target\classes\pictures\BMW-R1100S-2004-03.jpg]
img.getHeight() = 768
img.getWidth() = 1024
Running from command line with executable jar gives me the following output:
C:\Users\maba\Development\stackoverflow\Q12016222\target>java -jar Q12016222-1.0-SNAPSHOT.jar
resource = class path resource [pictures/BMW-R1100S-2004-03.jpg]
img.getHeight() = 768
img.getWidth() = 1024

I think you can directly access resources in ZIP/JAR file
Please see Tutorial its giving solution to your question
How to extract Java resources from JAR and zip archives
Hopes that helps

If I understand your problem you want to check the directory inside the jar and check all the files inside that directory.You can do something like:
JarInputStream jar = new JarInputStream(new FileInputStream("D:\\x.jar"));
JarEntry jarEntry ;
while(true)
{
jarEntry = jar.getNextJarEntry();
if(jarEntry != null)
{
if(jarEntry.isDirectory() == false)
{
String str = jarEntry.getName();
if(str.startsWith("weblogic/xml/saaj"))
{
anything which comes here are inside weblogic\xml\saaj directory
}
}
}
}

What you are looking for here might be the JarEntry list of the Jar... I had done some similar work during grad school... You can get the modified class here (http://code.google.com/p/marcellodesales-cs-research/source/browse/trunk/grad-ste-ufpe-brazil/ptf-add-on-dev/src/br/ufpe/cin/stp/global/filemanager/JarFileContentsLoader.java) Note that the URL contains an older Java class not using Generics...
This class returns a set of URLs with the protocol "jar:file:/" for a given token...
package com.collabnet.svnedge.discovery.client.browser.util;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class JarFileContentsLoader {
private JarFile jarFile;
public JarFileContentsLoader(String jarFilePath) throws IOException {
this.jarFile = new JarFile(jarFilePath);
}
/**
* #param existingPath an existing path string inside the jar.
* #return the set of URL's from inside the Jar (whose protocol is "jar:file:/"
*/
public Set<URL> getDirEntries(String existingPath) {
Set<URL> set = new HashSet<URL>();
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
String element = entries.nextElement().getName();
URL url = getClass().getClassLoader().getResource(element);
if (url.toString().contains("jar:file")
&& !element.contains(".class")
&& element.contains(existingPath)) {
set.add(url);
}
}
return set;
}
public static void main(String[] args) throws IOException {
JarFileContentsLoader jarFileContents = new JarFileContentsLoader(
"/u1/svnedge-discovery/client-browser/lib/jmdns.jar");
Set<URL> entries = jarFileContents.getDirEntries("impl");
Iterator<URL> a = entries.iterator();
while (a.hasNext()) {
URL element = a.next();
System.out.println(element);
}
}
}
The output would be:
jar:file:/u1/svnedge-discovery/client-browser/lib/jmdns.jar!/javax/jmdns/impl/constants/
jar:file:/u1/svnedge-discovery/client-browser/lib/jmdns.jar!/javax/jmdns/impl/tasks/state/
jar:file:/u1/svnedge-discovery/client-browser/lib/jmdns.jar!/javax/jmdns/impl/tasks/resolver/
jar:file:/u1/svnedge-discovery/client-browser/lib/jmdns.jar!/javax/jmdns/impl/
jar:file:/u1/svnedge-discovery/client-browser/lib/jmdns.jar!/javax/jmdns/impl/tasks/

May the following code sample can help you
Enumeration<URL> inputStream = BrowserFactory.class.getClassLoader().getResources(".");
System.out.println("INPUT STREAM ==> "+inputStream);
System.out.println(inputStream.hasMoreElements());
while (inputStream.hasMoreElements()) {
URL url = (URL) inputStream.nextElement();
System.out.println(url.getFile());
}

IF you really want to treat JAR files like directories, then please have a look at TrueZIP 7. Something like the following might be what you want:
URL url = ... // whatever
URI uri = url.toURI();
TFile file = new TFile(uri); // File-look-alike in TrueZIP 7
if (file.isDirectory) // true for regular directories AND JARs if the module truezip-driver-file is on the class path
for (TFile entry : file.listFiles()) // iterate top level directory
System.out.println(entry.getPath()); // or whatever
Regards,
Christian

Related

How to have my java project to use some files without using their absolute path?

I have written a project where some images are used for the application's appearance and some text files will get created and deleted along the process. I only used the absolute path of all used files in order to see how the project would work, and now that it is finished I want to send it to someone else. so what I'm asking for is that how I can link those files to the project so that the other person doesn't have to set those absolute paths relative to their computer. something like, turning the final jar file with necessary files into a zip file and then that the person extracts the zip file and imports jar file, when runs it, the program work without any problems.
by the way, I add the images using ImageIcon class.
I'm using eclipse.
For files that you just want to read, such as images used in your app's icons:
Ship them the same way you ship your class files: In your jar or jmod file.
Use YourClassName.class.getResource or .getResourceAsStream to read these. They are not files, any APIs that need a File object can't work. Don't use those APIs (they are bad) - good APIs take a URI, URL, or InputStream, which works fine with this.
Example:
package com.foo;
public class MyMainApp {
public void example() {
Image image = new Image(MyMainApp.class.getResource("img/send.png");
}
public void example2() throws IOException {
try (var raw = MyMainApp.class.getResourceAsStream("/data/countries.txt")) {
BufferedReader in = new BufferedReader(
new InputStreamReader(raw, StandardCharsets.UTF_8));
for (String line = in.readLine(); line != null; line = in.readLine()) {
// do something with each country
}
}
}
}
This class file will end up in your jar as /com/foo/MyMainApp.class. That same jar file should also contain /com/foo/img/send.png and /data/countries.txt. (Note how starting the string argument you pass to getResource(AsStream) can start with a slash or not, which controls whether it's relative to the location of the class or to the root of the jar. Your choice as to what you find nicer).
For files that your app will create / update:
This shouldn't be anywhere near where your jar file is. That's late 80s/silly windows thinking. Applications are (or should be!) in places that you that that app cannot write to. In general the installation directory of an application is a read-only affair, and most certainly should not be containing a user's documents. These should be in the 'user home' or possibly in e.g. `My Documents'.
Example:
public void save() throws IOException {
Path p = Paths.get(System.getProperty("user.home"), "navids-app.save");
// save to that file.
}

Getting the path of a running JAR file returns "rsrc:./"

My code runs inside a JAR file and I need to get the full path of that file.
For example, my JAR is called example.jar and is located at D:\example\
So I need to get "D:\example\example.jar" by some code inside that jar.
I have tried many methods to get that path, but none of them worked correctly.
One of them is
getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath()
Many people say that this works for them, but it returns "rsrc:./" for me.
I have searched after that and I noticed that my MANIFEST.MF contains this:
Manifest-Version: 1.0
Rsrc-Class-Path: ./
Class-Path: .
Rsrc-Main-Class: Example
Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
I don't know what that means, but if I remove that Rsrc stuff and replace the other things with it, it says the jar is broken. I think this is the reason why it doesn't work. Does anybody know what this means?
PS: I'm running my JAR using a BAT file.
I stumbled over this problem too and leave my investigations here for people who ask themselves in the future what the rsrc means.
I'm using Eclipse Mars 1 and try to export my project as a runnable JAR. There I can choose the library handling and decide between:
Extract required libraries into generated JAR
Package required libraries into generated JAR
Copy required libraries into a sub-folder next to the generated JAR
The line to be tested is
System.out.println(MyClass.class.getProtectionDomain().getCodeSource().getLocation());
the JAR file's name is MyJar.jar (which will be put on desktop), Project's name and folder is MyProject.
Results:
file:/C:/Users/admin/Desktop/MyJar.jar
rsrc:./
file:/C:/Users/admin/Desktop/MyJar.jar
<means running in Eclipse> file:/C:/Development/workspace/MyProject/target/classes/
I wrote a convinience method for that:
public class SystemUtils {
/**
* Let no one instanciate this class.
*/
private SystemUtils() {}
/**
* If the current JVM was started from within a JAR file.
* #return <code>Boolean.TRUE</code> if it is, <code>Boolean.FALSE</code> if it is not, <code>null</code> if unknown.
*/
public static Boolean executedFromWithinJar() {
Boolean withinJar = null;
try {
String location = SystemUtils.class.getProtectionDomain().getCodeSource().getLocation().toString();
if (location.startsWith("rsrc:")
|| location.endsWith(".jar") && !new File(location.substring(location.indexOf(':') + 1)).isDirectory())
withinJar = Boolean.TRUE;
else
withinJar = Boolean.FALSE;
}
catch (Exception ex) {/* value is still null */}
return withinJar;
}
}
Based on your comments, it appears your real question is how to copy files from inside your application .jar. You can do that with something like this:
String jarEntry = "/files/data.txt";
Path destination = Paths.get(
System.getProperty("user.home"), "Downloads", "data.txt");
try (InputStream stream = getClass().getResourceAsStream(jarEntry)) {
Files.copy(stream, destination);
}
Class.getResource and Class.getResourceAsStream read data from the classpath, usually as an entry in a .jar file that is on the classpath, such as your own application’s .jar file.
A file embedded in the classpath is normally called an application resource or just “resource” for short. Resources are always specified using the forward slash (/) as a directory separator, on all platforms, even Windows.
If you are not sure what string you should pass to getResource or getResourceAsStream, examine the contents of your .jar file.
package com.example;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
public class HomeJar {
public static void main(String[] args) throws IOException {
URL u = HomeJar.class.getProtectionDomain().getCodeSource().getLocation();
File f = new File(u.getPath().toString());
if (!f.isFile()) {
throw new RuntimeException("'" + u + "' isn't a jar");
}
try (JarInputStream jis = new JarInputStream(new BufferedInputStream(new FileInputStream(f)))) {
JarEntry je = jis.getNextJarEntry();
while (je != null) {
System.out.println(je.getName());
je = jis.getNextJarEntry();
}
}
}
}
I just have created a new workspace and moved every project into it and everything works fine now, I think it was a bug or something...Thank you all for your help!
This was the exact problem I had today. I finally found this:
To get the jar-file location when packaging with eclipses Package required libraries into generated JAR one can use this (insert name of the calling class in the <>:
var thisClassesResourcePath = <CLASSNAME>.class.getName().replace('.', '/') + ".class";
var resource = ClassLoader.getSystemResource(thisClassesResourcePath);
var path = resource.getPath();
var jarUrl = new URL(path.substring(0, path.lastIndexOf("jar!") + 3));

Play 2.2.3 can not see file by Play.application().resource

Play 2.2.3. Windows 7.
public static Result menu() throws IOException {
String path = Play.application().resource("resources/menu.json").toString();
String content = Files.toString(new File(path), Charsets.UTF_8);
return ok(content).as("JSON");
}
Got an error:
... scala-2.10\classes\resources\menu.json The filename, directory
name, or volume label syntax is incorrect
Checking that path in file-system, I'm able to find my file there:
..\target\scala-2.10\classes\resources\menu.json
I'm able to find it there. Why play can't?
--
UPDATE:
I've just figured out I can not create files on C:\ root folder on my machine. That maybe the issue. But on other hand I'm not accessing root folder, and ad trying to get read only access. And I do have write access to that file on that path anyway.
As actually you want to use your menu.json file as a static asset you can put it i.e. into public/resources/menu.json file and then read it with simple:
<script>
$.get('#routes.Assets.at("resources/menu.json")');
</script>
or just directly by request:
http://localhost:9000/assets/resources/menu.json
To do what you want via controller you need to read the InputStream by classpath (remember that finally it will be archived into jar file!) but it need to be placed in conf folder i.e.: conf/resources/menu.json then from controller:
public static Result menuViaControllerJson() {
InputStream is = Play.application().classloader().getResourceAsStream("resources/menu.json");
return (is != null)
? ok(is)
: notFound();
}
Anyway you will get exactly the same result as for common Assets.at, so consider if it's worth of effort.
Edit: If you want to use this file as custom config just use HOCON syntax file i.e.: conf/ses.conf:
foo = "bar"
and in controller:
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
....
Config cfg = ConfigFactory.parseResources(Play.application().classloader(), "ses.conf");
debug("My 'foo' is configured as: " + cfg.getString("foo"));

load a folder from a jar

I am trying to access a directory inside my jar file. I want to go through every of the files inside the directory itself. I tried using the following:
File[] files = new File("ressources").listFiles();
for (File file : files) {
XMLParser parser = new XMLParser(file.getAbsolutePath());
// some work
}
If I test this, it works well. But once I put the contents into the jar, it doesn't because of several reasons. If I use this code, the URL always points outside the jar.
structure of my project :
src
controllers
models
class that containt traitement
views
ressources
See this:
How do I list the files inside a JAR file?
Basically, you just use a ZipInputStream to find a list of files (a .jar is the same as a .zip)
Once you know the names of the files, you can use getClass().getResource(String path) to get the URL to the file.
I presume this jar is on your classpath.
You can list all the files in a directory using the ClassLoader.
First you can get a list of the file names then you can get URLs from the ClassLoader for individual files:
public static void main(String[] args) throws Exception {
final String base = "/path/to/folder/inside/jar";
final List<URL> urls = new LinkedList<>();
try (final Scanner s = new Scanner(MyClass.class.getResourceAsStream(base))) {
while (s.hasNext()) {
urls.add(MyClass.class.getResource(base + "/" + s.nextLine()));
}
}
System.out.println(urls);
}
You can do whatever you want with the URL - either read and InputStream into memory or copy the InputStream into a File on your hard disc.
Note that this definitely works with the URLClassLoader which is the default, if you are using an applet or a custom ClassLoader then this approach may not work.
NB:
You have a typo - its resources not ressources.
You should use reverse domain name notation for your project, this is the convention.

JarInputStream: getNextJarEntry always returns null

I have an I18n helper class that can find out the available Locales by looking at the name of the files inside the application's Jar.
private static void addLocalesFromJar(List<Locale> locales) throws IOException {
ProtectionDomain domain = I18n.class.getProtectionDomain();
CodeSource src = domain.getCodeSource();
URL url = src.getLocation();
JarInputStream jar = new JarInputStream(url.openStream());
while (true) {
JarEntry entry = jar.getNextJarEntry();
if (entry == null) {
break;
}
String name = entry.getName();
// ...
}
}
Currently, this isn't working - jar.getNextJarEntry() seems to always return null. I have no idea why that's happening, all I know is that url is set to rsrc:./. I have never seen that protocol, and couldn't find anything about it.
Curiously, this works:
class Main {
public static void main(String[] args) {
URL url = Main.class.getProtectionDomain().getCodeSource().getLocation();
JarInputStream jar = new JarInputStream(url.openStream());
while (true) {
JarEntry entry = jar.getNextJarEntry();
if (entry == null) {
break;
}
System.out.println(entry.getName());
}
}
}
In this version, even though there is practically no difference between them, the url is correctly set to the path of the Jar file.
Why doesn't the first version work, and what is breaking it?
UPDATE:
The working example really only works if I don't use Eclipse to export it. It worked just fine in NetBeans, but in the Eclipse version the URL got set to rsrc:./ too.
Since I exported it with Package required libraries into generated JAR library handling, Eclipse put its jarinjarloader in my Jar so I can have all dependencies inside it. It works fine with the other settings, but is there any way to make this work independently of them?
Another question
At the moment, that class is part of my application, but I plan to put it in a separate library. In that case, how can I make sure it will work with separate Jars?
The problem is the jarinjarloader ClassLoader that is being used by Eclipse. Apparently it is using its own custom rsrc: URL scheme to point to jar files stored inside the main jar file. This scheme is not understood by your URL stream handler factory, so the openStream() method returns null which causes the problem that you're seeing.
This answers the second part of your question about separate jars - not only will this work, it's the only way that it will work. You need to change your main application to use separate jars instead of bundling them all up inside the main jar. If you're building a web application, copy them into the WEB-INF/lib directory and you're fine. If you're building a desktop application, add a relative path reference in the META-INF/MANIFEST.MF to the other jars, and they will automatically be included as part of the classpath when you run the main jar.
The code may or may not result into the jar file where I18n resides. Also getProtectionDomain can be null. It depends how the classloader is implemented.
ProtectionDomain domain = I18n.class.getProtectionDomain();
CodeSource src = domain.getCodeSource();
URL url = src.getLocation();
about the rsrc:./ protocol, the classloader is free to use whatever URL they please (or name it for that matter)
try this out, you might get lucky :)
URL url = getClass().getResource(getClass().getSimpleName()+".class");
java.net.JarURLConnection conn = (java.net.JarURLConnection) url.openConnection();
Enumeration<JarEntry> e = conn.getJarFile().entries();
...
and good luck!
Eclipse's jarinjarloader loads everything using the system classloader and it never knows what jar file it was loaded from. That's why you can't get the jar URL for a rsrc: url.
I suggest storing the list of locales in a file in each application jar, e.g. META-INF/locales. Then you can use ClassLoader.getResources("META-INF/locales") to get the list of all the files with that name in the classpath and combine them to obtain the full list of locales.
I use System.getProperty("java.class.path") for getting the location of the jar. I do not know if that makes a difference. I have not explored the ProtectDomain path so I cannot help you there, sorry. As for multiple jars, just iterate through those jar file also.

Categories

Resources