Accessing resources within JAR - java

I'm creating a Java application that uses a Maven profile and a JAR build.
I have some XML files in this folder : project/src/main/resources/XML/*.xml
The src/main/resources folder is a source folder in my build path so I have my project.jar/XML/*.xml built in the JAR binary.
I'd like to be able to find and list those files "/XML/*.xml" in JAR execution. Is my structure good for this ?
I have no problem running with the Maven profile, I just can't find the good way to find and list those files.

There isn't really a good way. If you don't mind the dependency on spring-core then they have a nice PathMatchingResourcePatternResolver that abstracts away from the differences between JARs and directories.
PathMatchingResourcePatternResolver resolver =
new PathMatchingResourcePatternResolver(MyClass.class.getClassLoader());
Resource[] xmls = resolver.getResources("classpath*:XML/*.xml");

If you want to list the contents of your jar file, you could just simply run:
jar tf ./path/to/your/file.jar
or
jar tf ./path/to/your/file.jar | grep XML
Now from code (I believe this is not the only way to do it) and you have to replace Main with whatever class you are in that is within the jar:
List<String> myJarXmlFiles = new ArrayList<String>();
try {
// replace Main here
CodeSource src = Main.class.getProtectionDomain().getCodeSource();
if (src != null) {
URL jar = src.getLocation();
ZipInputStream zip = new ZipInputStream(jar.openStream());
ZipEntry entry = null;
while ((entry = zip.getNextEntry()) != null) {
if (entry.getName().contains("XML/")
&& entry.getName().endsWith(".xml")) {
myJarXmlFiles.add(entry.getName());
}
}
} else {
// failed
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

This code demonstrates how to find out the path of a jar on your app's classpath and list the contents of a folder.
// you need to get of a URL of any class in your jar
URL url = Appender.class.getProtectionDomain().getCodeSource()
.getLocation();
File file = new File(url.getFile());
System.out.println(file.getCanonicalPath());
JarFile jar = new JarFile(file);
Enumeration<JarEntry> i = jar.entries();
while (i.hasMoreElements()) {
JarEntry e = i.nextElement();
if (e.getName().startsWith("META-INF/")) {
System.out.println(e.getName());
}
}
jar.close();
output
jar:file:/C:/temp/.repository/log4j/log4j/1.2.17/log4j-1.2.17.jar!/org/apache/log4j/Appender.class
C:\temp\.repository\log4j\log4j\1.2.17\log4j-1.2.17.jar
META-INF/MANIFEST.MF
META-INF/
META-INF/LICENSE
META-INF/NOTICE
META-INF/maven/
META-INF/maven/log4j/
META-INF/maven/log4j/log4j/
META-INF/maven/log4j/log4j/pom.properties
META-INF/maven/log4j/log4j/pom.xml

Related

How to execute or update files stored in resources folder (intelliJ plugin)

I am trying to make a customized IntelliJ plugin and run a shell script file stored in src/main/resources folder from a java code file in the main folder.
I need Help with a way to execute and change the contents of files in the resources folder using configured paths.
Tried these ways so far
//Method - 1
String filePath = getClass().getClassLoader().getResource("file.sh").getPath();
System.out.println(filePath);
String[] paths = filePath.split(":");
System.out.println(paths[1]);
//Method-2
URL res = getClass().getClassLoader().getResource("file.sh");
File script = null;
try {
assert res != null;
script = Paths.get(res.toURI()).toFile();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
String absolutePath = script.getAbsolutePath();
//Method-3
final ClassLoader classLoader = getClass().getClassLoader();
final File script = new File(classLoader.getResource("file.sh").getFile());
System.out.println(script.getPath());
But all these lead to a path file:/Users/username/projects/projectname/build/idea-sandbox/plugins/project-name/lib/Project-Name-1.0-SNAPSHOT.jar!/file.sh

SpringBoot - accessing a file inside resources folder

I am accessing a File inside the resources folder from the main class
File file = new ClassPathResource("remoteUnitsIdsInOldServer.txt").getFile();
and I am getting this error:
java.io.FileNotFoundException: class path resource [remoteUnitsIdsInOldServer.txt] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/Users/lopes/Documents/workspace-sts-3.9.0.RELEASE/telefonicaUtils/target/telefonicaUtils-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/remoteUnitsIdsInOldServer.txt
and I even open the jar file and the file remoteUnitsIdsInOldServer.txt is there, inside classes
The simplest solution for me was,
try {
ClassPathResource classPathResource = new ClassPathResource("remoteUnitsIdsInOldServer.txt");
byte[] data = FileCopyUtils.copyToByteArray(classPathResource.getInputStream());
String content = new String(data, StandardCharsets.UTF_8);
} catch (Exception ex) {
ex.printStackTrace();
}
It's depends on your requirements..
Case 1:
Considering you need to access text file from resource. You can simply use apache IOUtils and java ClassLoader.
Snippet (note: IOUtils package --> org.apache.commons.io.IOUtils)
String result = "";
ClassLoader classLoader = getClass().getClassLoader();
try {
result = IOUtils.toString(classLoader.getResourceAsStream("fileName"));
} catch (IOException e) {
e.printStackTrace();
}
Classic way:
StringBuilder result = new StringBuilder("");
//Get file from resources folder
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("fileName").getFile());
try (Scanner scanner = new Scanner(file)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
result.append(line).append("\n");
}
scanner.close();
} catch (IOException e) {
e.printStackTrace();
}
Case 2:
Considering you need to access properties from resources such as xml, properties files.
Its too simple, Simply use spring annotation #ImportResource({ "classpath:application-properties.xml", "classpath:context.properties" })
Hope that will be helpful to you.
Typically, a source tree would look like this:
src/
main/
java/
com/
...
resources/
remoteUnitsIdsInOldServer.txt
And, using standard Maven/Gradle functionality, would produce a JAR like this:
<JAR_ROOT>/
com/
...
remoteUnitsIdsInOldServer.txt
Your listed code should work for this situation. However, you mentioned looking in your JAR "inside classes". I wouldn't think there would be a "classes" folder within the JAR.
Good luck.

Java loading files from the jar

I don't get how to load files from the produced Jar.
This is my code and it works fine inside the IDE, but not when I run the Jar:
URL url = ClassLoader.getSystemResource(".");
try
{
File dir = new File(url.toURI());
for (File f : dir.listFiles())
{
String fn = f.getName();
if (fn.endsWith(".png"))
{
ImageView iv = new ImageView(fn);
// ...
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
The structure of the Jar is:
META-INF
de (and the rest of the packages with the class files)
file1
file2
... and so on
So the files are directly in the jar not in any subfolder.
Your code doesn't work because File objects cannot be used to access files inside a jar. What you can do is use ZipInputStreams to open & read your jar file alongside ZipEntry's to read the individual files in your jar.
This code will work in a jar, but most likely not in an IDE. In which case, you can detect the current state (IDE or Jar) and execute the desired loading code accordingly.
CodeSource src = ClientMain.class.getProtectionDomain().getCodeSource();
URL jar = src.getLocation();
ZipInputStream zip = new ZipInputStream(jar.openStream());
ZipEntry entry = null;
while ((entry = zip.getNextEntry()) != null) {
String entryName = entry.getName();
if (entryName.endsWith(".png")) {
BufferedImage image = ImageIO.read(zip);
// ...
}
}
Using the URL's already setup, we can determine if the program is in a jar or not with this simple code:
new File(jar.getFile()).toString().endsWith("jar")
This works because when in an IDE, (eclipse in my case)
new File(jar.getFile()).toString() returns
"D:\Java\Current%20Projects\Test\bin"
where as in a jar, I got
"D:\Windows%20Folders\Desktop\Test.jar"

How to load all files from a jar added as Maven dependency in a Tomcat webapp?

Before marking it duplicate please read the whole post!
I have to read all files under the profiles folder in a jar which is a Maven dependency:
Below is the code which I am using for reading files:
CodeSource cs = Detector.class.getProtectionDomain().getCodeSource();
public List<String> pr = new ArrayList<>();
if (cs != null) {
URL jar = cs.getLocation();
ZipInputStream zip = null;
try {
zip = new ZipInputStream(jar.openStream());
while (true) {
ZipEntry entry = zip.getNextEntry();
if (entry == null)
break;
String ename = entry.getName();
if (ename.startsWith("profiles/")) {
try(InputStream in =
Detector.class.getClassLoader().getResourceAsStream(ename)) {
pr.add(IOUtils.toString(in));
}
}
}
DetectorFactory.loadProfile(pr);
}
}
When I use this code in a standalone Java project it works fine and files are being read but when I use the same code in a Tomcat webapp it does not. However as per my observation, it seems to be a Tomcat's ClassLoader issue because in the Java application the class loader is LauncherAppClassLoader whereas in webapp it is the ParallelWebAppClassLoader.

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

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

Categories

Resources