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.
Related
I currently develop an open-source project where people may add their own .jar to extend the included features. However, I'm stuck with how to load the classes in the jar.
I have an abstract class which has an abstract method onEnable() and some getter that provides some objects to work with the application. The plugin needs the subclass my plugin-class BasePlugin. The jar should be added to /plugins and thus I want all *.jar files in the /plugins folder to be loaded when the application starts.
The problem I'm running to now is that, of all the approaches I found, I need to declare a classpath of the classes in the jar file, which I do not know. Neither do I know the name of the jar file. Thus, I need to scan the /plugins folder for any *.jar file and load the corresponding class inside the jar which implements BasePlugin and invoke the onEnable() method.
The basic idea is too...
Read all the files in a specific directory
Convert the File reference to a URL for each result
Use a URLClassLoader, seeded with the URL results to load each result
Use URLClassLoader#findResources to find all the match resources with a specific name
Iterate over the matching resources and load each one, which should give, at least, the "entry point" class name
Load the class specified by the "entry point" property
For example...
public List<PluginClass> loadPlugins() throws MalformedURLException, IOException, ClassNotFoundException {
File plugins[] = new File("./Plugins").listFiles(new FileFilter() {
#Override
public boolean accept(File file) {
return file.getName().endsWith(".jar");
}
});
List<URL> plugInURLs = new ArrayList<>(plugins.length);
for (File plugin : plugins) {
plugInURLs.add(plugin.toURI().toURL());
}
URLClassLoader loader = new URLClassLoader(plugInURLs.toArray(new URL[0]));
Enumeration<URL> resources = loader.findResources("/META-INFO/Plugin.properties");
List<PluginClass> classes = new ArrayList<>(plugInURLs.size());
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
Properties properties = new Properties();
try (InputStream is = resource.openStream()) {
properties.load(is);
String className = properties.getProperty("enrty-point");
PluginClass pluginClass = loader.loadClass(className);
classes.add(pluginClass);
}
}
return classes
}
nb: I've not run this, but this is the "basic
SpigotMC uses JAR files as plugins as well, inside the jar is a plugin.yaml file that stores extra information about the plugin including the classpath. You don't need to use a YAML file, instead you could use something like JSON or even a plain text file.
The YAML file is inside the jar and can be accessed by using some of the methods explained here. You can then get the classpath property and then load the jar using the methods explained here. Extra information can be stored about the plugin such as the name, version, and dependencies.
Java already has a class for this: ServiceLoader.
The ServiceLoader class was introduced with Java 6, but the “SPI jar” concept is actually as old as Java 1.3. The idea is that each jar contains a short text file that describes its implementations of a particular service provider interface.
For instance, if a .jar file contains two subclasses of BasePlugin named FooPlugin and BarPlugin, the .jar file would also contain the following entry:
META-INF/services/com.example.BasePlugin
And that jar entry would be a text file, containing the following lines:
com.myplugins.FooPlugin
com.myplugins.BarPlugin
Your project would scan for the plugins by creating a ClassLoader that reads from the plugins directory:
Collection<URL> urlList = new ArrayList<>();
Path pluginsDir = Paths.get(
System.getProperty("user.home"), "plugins");
try (DirectoryStream<Path> jars =
Files.newDirectoryStream(pluginsDir, "*.jar")) {
for (Path jar : jars) {
urlList.add(jar.toUri().toURL());
}
}
URL[] urls = urlList.toArray(new URL[0]);
ClassLoader pluginClassLoader = new URLClassLoader(urls,
BasePlugin.class.getClassLoader());
ServiceLoader<BasePlugin> plugins =
ServiceLoader.load(BasePlugin.class, pluginClassLoader);
for (BasePlugin plugin : plugins) {
plugin.onEnable();
// etc.
}
An additional advantage of using ServiceLoader is that your code will be capable of working with modules, a more complete form of code encapsulation introduced with Java 9 which offers increased security.
There is an example here it may be helpful. Also, you should take a look at OSGi.
I created a data folder beside the js folder in resource/static at the backend of my spring boot application.
I want to list its content in a selection for the user.
I was able to list it if I use the File and Path functions of the base Java. But I don't like this solution, because the path could change in different environment of our system.
I can load a file with org.springframework.core.io.ResourceLoader. I like this solution better as it is independent of the file path of my system. But I did not found the way to list the files in a given resource folder.
Do you have any idea?
Finally I moved my data folder to the WEB-INF.
And I can list the content of it with the following code:
#Autowired
ServletContext context;
public Collection<String> getFileList(String path) {
Collection<String> result = new ArrayList<String>();
Set<String> paths = context.getResourcePaths(path);
for (String p : paths) {
String[] parts = p.split("/");
result.add(parts[parts.length-1]);
}
return result;
}
And I call it with the following parameter:
getFileList("/WEB-INF/data");
This solution works if the WEB-INF folder is unpacked during deploy. And also works when the data folder remains in the war archive.
Check this out :)
You can also read about Java Reflection
Right now I'm manually writing song file names into a String array, then sending the strings to InputStream is = this.getClass().getResourceAsStream(filename); to play the song.
I would rather loop through all the song names in my resources folder and load them into the array so that each time I add a song, I don't need to manually type it into the array.
All of my songs are located in resources folder:
Is there a java method or something to grab these names?
The files need to work when I export as .jar.
I thought I could use something like this?
InputStream is = this.getClass().getClassLoader().getResourceAsStream("resources/");
Is there a listFiles() or something for that?
Thanks
No, because the classpath is dynamic in Java. Any additional Jar could contribute to a package, a classloader can be as dynamic as imaginable and could even dynamically create classes or resources on demand.
If you have a specific JAR file and you know the path within that JAR file then you could simply treat that file as archive and access it via java.util.jar.JarFile which lets you list all entries in the file.
JarFile jar = new JarFile("res.jar");
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
System.out.println(entry.getName());
}
If you want to do this without knowing the JAR filename and path (e.g. by a class within the JAR itself) you need to get the filename dynamically as Evgeniy suggested; just use your own class name (including package; your screenshot looks like you are in default package, which is a bad idea).
You can try something like this
Enumeration<URL> e = ClassLoader.getSystemResources("org/apache/log4j");
while (e.hasMoreElements()) {
URL u = e.nextElement();
String file = u.getFile();
...
file:/D:/.repository/log4j/log4j/1.2.14/log4j-1.2.14.jar!/org/apache/log4j
extract jar path from here and use JarFile class to know org/apache/log4j contents.
If it is unpacked, you will get straight path and use File.listFIles
I'm trying to figure out how to add a lot of images into my Java program without having to add every file name in the code.
So I want to take all the .png images in the correct folder and make them into an IconImage and then place them in an ArrayList.
What I've got right now is this.
private void fillList()
{
File[] files = new File("C:/Users/marre/Google Drive/Java/lek/imageplay/src/img").listFiles();
for (File f : files)
{
if (f.isFile())
{
ImageIcon tempIcon = new ImageIcon(getClass().getResource("/img/"+f.getName()));
List.add(tempIcon);
}
}
}
This works the way I want it to regarding getting the names of all images. But the file only reads from absolute file path.
What can I use instead to get the same result, but that works within .jar file (so it reads from src/img/ and not the whole c:/ bla bla..)?
I'm not sure if this will be helpful but I had a similar issue in attempting to obtain files within a jar file. In the case this can be useful to you, my approach was to obtain the URL for the files inside the jar using the ClassLoader:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL url = classLoader.getResource("src/img/");
Check inside your jar to make sure of the directory structure for the images so you can specify the correct relative path.
Keep in mind, a jar is a packaged form, so this will most likely return a zipped file (and not a simple File object). You might have to dabble in ZipFile.
ZipFile folderInsideJar = new ZipFile(new File(url.getPath());
Enumeration<? extends ZipEntry> entries = folderInsideJar.entries();
while (entries.hasMoreElements()) {
// ideally this would be your image file, zipped
ZipEntry childFile = entries.nextElement();
// ... etc ...
}
For environments such as JBoss, etc, you might also get virtual files (vfsfile/vfszip). Point being, you can determine what you are dealing with by looking at the result URL.getProtocol().
It would also appear ImageIcon has a constructor that takes a URL. You might try giving this a shot to see if it handles the "resource inside jar" case before manually attempting to retrieve the zip file.
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