Reputation: 1605
I am trying to write a code in a webapp, where I have a JAR file in my classpath. The objective is to check if the directory exists in the JAR. If yes, I need to save the all the contents of the files inside the JAR's directory in a HashMap<String, String>
. The Key being the file name and the value being the contents of each file.
File directory = new File(getClass().getClassLoader().getResource(directoryPath).getPath());
System.out.println("PATH IS: " + directory.getPath());
// Check if dirPth exists and is a valid directory
if (!directory.isDirectory()) {
throw new AccessException("Directory \"" + directoryPath + "\" not valid");
}
// Obtain a list of all files under the dirPath
File [] fileList = directory.listFiles();
for (File file : fileList) {
if (file.isFile()) {
// Read the file
BufferedReader br = new BufferedReader(new FileReader(file));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
br.close();
// Store the file data in the hash
entry.put(file.getName(), sb.toString);
}
}
The output of the direcotry.getPath()
is:
file:\H:\apache-tomcat-9.0.27\lib\myConfigurationFiles.jar!\META-INF\Maintenance\xmlFiles\secondary
which is the right folder I am looking for.
Here the Map object is the "entry".
Now I am not sure why direcotry.isDirectory() returns false. Shouldn't it return true?
Now since its not crossing the first exception. I have no idea how it will behave after that. Any help would be appreciated.
Upvotes: 2
Views: 1291
Reputation: 761
Given a java.nio.file.Path
to the jar you want to search (jarPath
), and a String
for the absolute directory name within the jar (directory
), this may work for you:
Map<String, String> map = new HashMap<>();
try (FileSystem fs = FileSystems.newFileSystem(jarPath, null)) {
Path dir = fs.getPath(directory);
if (Files.exists(dir)) {
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
map.put(file.toString(), Files.readString(file));
return super.visitFile(file, attrs);
}
});
}
}
Files.readString
is available with Java 11+. For earlier versions, use:
new String(Files.readAllBytes(file), StandardCharsets.UTF_8)
Upvotes: 1
Reputation: 102785
getClass()
is the wrong approach for jobs like this; it breaks if anybody subclasses. The proper way is to use MyClassName.class
instead.
getClassLoader().getResource()
is also the wrong approach; this breaks in exotic but possible cases where getClassLoader()
returns null. Just use getResource
and slightly change the path (add a leading slash, or, write the path relative to your class file).
You're turning the string file:\H:\apache-tomcat-9.0.27\lib\myConfigurationFiles.jar!\META-INF\Maintenance\xmlFiles\secondary
into a filename and then asking if it is a directory. Of course it isn't; that isn't even a file. You need to do some string manipulation to extract the actual file out of it: You want just H:\apache-tomcat-9.0.27\lib\myConfigurationFiles.jar
, feed that to the java.nio.file
API, and then use that to ask if it is a file (it will never be a directory; jars are not directories).
Note that this will not work if the resource you're reading from isn't a jar. Note that the class loading API is abstracted: You could find yourself in the scenario where source files are generated from scratch or loaded out of a DB, with more exotic URLs being produced by the getResource
method to boot. Thus, this kind of code simply won't work then. Make sure that's okay first.
Thus:
String urlAsString = MyClassName.class.getResource("MyClassName.class").toString(); // produces a link to yourself.
int start = urlAsString.startsWith("file:jar:") ? 8 : urlAsString.startsWith("file:") ? 4 : 0;
int end = urlAsString.lastIndexOf('!');
String jarFileLoc = urlAsString.substring(start, end);
if you want this to apply to actual directories (class files and such can come from dirs instead of files), you could do:
var map = new HashMap<String, String>();
Path root = Paths.get(jarFileLoc);
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
String content = new String(Files.readAllBytes(file), StandardCharsets.UTF_8);
map.put(root.relativize(file), content);
}
});
for a jar, which is really just a zip, it'll be more like:
var map = new HashMap<String, String>();
Path root = Paths.get(jarFileLoc);
try (var fileIn = Files.newInputStream(root)) {
ZipInputStream zip = new ZipInputStream(fileIn);
for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
String content = new String(zip.readAllBytes(), StandardCharsets.UTF_8);
map.put(entry.getName(), content);
}
}
Make sure you know what charsets are and that UTF_8 is correct here.
Upvotes: 2