I am trying to read package name from a jar file. My probem is that when I get URL, it contains unrecognized form to be recognized by windows file.
I read this solution. But this did not helped me. Convert URL to normal windows filename Java.
directoryURL.toURI().getSchemeSpecificPart() does not convert windows style.
This is my code.
// Get a File object for the package
URL directoryURL = Thread.currentThread().getContextClassLoader()
.getResource(packageNameSlashed);
logger.info("URI" + directoryURL.toURI());
logger.info("Windows file Name" + directoryURL.toURI().getSchemeSpecificPart());
// build jar file name, then loop through zipped entries
jarFileName = URLDecoder.decode(directoryURL.getFile(), "UTF-8");
jarFileName = jarFileName.substring(0, jarFileName.indexOf(".jar"));
// HERE Throws exception"
jf = new JarFile(jarFileName + ".jar");
while (jarEntries.hasMoreElements()) {
entryName = jarEntries.nextElement().getName();
logger.info("Entry name: " + entryName);
if (entryName.startsWith(packageNameSlashed)
&& entryName.length() > packageNameSlashed.length() + 5
&& entryName.endsWith(".class")) {
entryName = entryName.substring(packageNameSlashed.length() + 1);
packageClassNames.put(entryName, packageName);
}
}
This is log.
16-02-2015 14:02:15 INFO - URI jar:file:/C:/SVN/AAA/trunk/aaa/client/target/server-1.0.jar!/packageName
16-02-2015 14:02:15 INFO Windows file Name file:/C:/SVN/AAA/trunk/aaa/client/target/server-1.0.jar!/packageName
A "jar:..." URL does not identify a file. Rather, it identifies a member of a JAR file.
The syntax is (roughly speaking) "jar:<jar-url>!<path-within-jar>", where the is itself a URL; e.g. a "file:" URL in your example.
If you are going to open the JAR file and iterate entries like that, you need to:
Extract the schemeSpecificPart of the original URL
Split the schemeSpecificPart on the "!" character.
Parse the part before the "!" as a URI, then use File(URI) to get the File.
Use the File to open the ZipFile.
Lookup the part after the "!" in the ZipFile ...
The answer by Stephen has all the elements you need.
With the getResource(package).getURI() or getResoucer(package).toFile you are getting the path to the resource.
Do substring on it to extract the part between file:// and ! this is the path to physical location of your jar of interest.
De new File on this sub-path and you have handle to your jar.
Jar is normal zip file, and process it as such (java.util.zip and there are manuals on the web).
List content of your zip file (now you may need to navigate using the bits behind ! sign in your original path), and you get your classes name.
I am not sure if this is the best way to achieve your goal, I would check how classes discovery (which is what you are trying to do, are implemented in some open source framework (for example tomcat uses it, JPA impelementation to find the entitities). There is also discovery project on apache but it seems to be dead for a while.
Related
I am working on a Java project, which runs fine on Windows 10, but when I tested it in Ubuntu, it shows
"AWT-EventQueue-0" java.lang.NullPointerException: Cannot read the array length because "allFiles" is null.
I read this answer, but could not find a fix.
What I am doing in the project is load an array of images from a certain path. Here is the faulty part of my code:
BufferedImage[] allImages;
public ImageArray(String set, int n) {
File path = new File("res/mnist_png/" + set + "/" + n);
File[] allFiles = path.listFiles();
allImages = new BufferedImage[allFiles.length];
JLabel label[] = new JLabel[allFiles.length];
for (int i = 0; i < allFiles.length; i++) {
try {
allImages[i] = ImageIO.read(allFiles[i]);
label[i] = new JLabel();
ImageIcon icon = new ImageIcon(allImages[i]);
I tried removing the variable allFiles and replacing its use with the actual code it holds but with no success. I saw in the previously answered that the use of the new / this keywords could fix the issue, but I don't seem to be able to find if and where to use them.
I printed the value of the allFiles and path.listFiles() and it is indeed null. Is there a way for the program to work if they remain null? Would changing the null somehow break their intended work?
As I mentioned, the problem occurs only on Linux, but works fine on Windows. Some help would be much appreciated.
First problem: You are using a relative file name. Relative file names have a different meaning depending on the current working directory of the Java process. This is not a Java concept; each process in a system has had its own current directory since long before Java existed.
Second problem: You are trying to list application resources. If you ever choose to package your application as a .jar file, this will not work, because a .jar is a single archive file; the data inside it is all part of one file and they do not constitute actual files.
Relative file names
Any file that does not start with a directory separator (/ on most systems, or optionally \ in Windows) is a relative file name. The actual file location that relative file name refers to depends on the current directory of the Java process. All programs, not just Java programs, work this way.
Some examples:
File name Current directory Actual file location
--------- ----------------- --------------------
res/mnist_png/A/1/image01.png /home/gosho09/project /home/gosho09/project/res/mnist_png/A/1/image01.png
mnist_png /home/gosho09/project /home/gosho09/project/mnist_png
mnist_png / /mnist_png
/home/gosho09/project/res /tmp /home/gosho09/project/res
/home/gosho09/project/res /home/gosho09 /home/gosho09/project/res
/home/gosho09/project/res /usr/local/bin /home/gosho09/project/res
/var/log /tmp /var/log
/var/log /home/gosho09 /var/log
/var/log /usr/local/bin /var/log
As you can see, if the file name does not start with a /, it is relative, and the current directory determines the actual location.
If the file name starts with /, it is considered an absolute file name. Absolute file names are not affected by the current directory.
However… you should not use file names at all.
If you ever want to distribute your application, you will most likely want it to be packaged as a .jar file. A .jar file is a single archive file which contains compiled classes, and application resource files, like your image sets.
Because a .jar file is an archive (it’s actually a specialized zip file), the entries inside it are just parts of the archive, in compressed form. They are not individual files, just sequences of bytes. You cannot read them with the File class.
Fortunately, there is a way to read application resources, which will work both when your application is packaged as a .jar file, and when it exists as regular .class files and data files: the Class.getResource method.
A typical usage might look like this:
String path = "/mnist_png/" + set + "/" + n + "/image" + i + ".png");
URL imageLocation = ImageArray.class.getResource(path);
if (imageLocation == null) {
throw new RuntimeException("Missing resource \"" + path + "\"");
}
allImages[i] = ImageIO.read(imageLocation);
You may be wondering how one is supposed to list files without the File class. The answer is: You can’t and you shouldn’t.
By design, application resources cannot be listed. This is because they are not guaranteed to be listable. Resources are loaded by ClassLoaders, and ClassLoaders may or may not read from directories or from .jar files.
(In fact, the Java SE runtime no longer includes its core classes as a .jar file; as a result, third party tools which used to assume those classes would be available as a .jar file had to be rewritten. Java did not pull the rug out from under those tools’ developers; it was never considered safe to assume classes would come from .jar files, and those developers chose not to heed that warning.)
The alternative to listing the resources is to include a resource which contains a list of the known resource paths. It’s your application; you know what’s in it. So just write a simple plain text listing, include it in your application, and read from that:
String root = "/mnist_png/" + set + "/" + n + "/";
String listingPath = root + "image-list.txt";
try (BufferedReader listing = new BufferedReader(
new InputStreamReader(
Objects.requireNonNull(
ImageArray.class.getResourceAsStream(listingPath),
"Missing resource \"" + listingPath + \""),
StandardCharsets.UTF_8))) {
List<JLabel> labelList = new ArrayList<>();
String path;
while ((path = listing.readLine()) != null) {
URL imageLocation = ImageArray.class.getResource(root + path);
if (imageLocation == null) {
throw new RuntimeException(
"Missing resource \"" + root + path + "\"");
}
labelList.add(new JLabel(new ImageIcon(imageLocation)));
}
labels = labelList.toArray(new JLabel[0]);
}
I'm loading some network path in my java code. It is not taking the same format as present in the configuration file, missing one slash.
Example:
String path = "//abckatte.com/abc/test";
File fileobj = new File(path);
Whenever I saw the fileobj in log message it is displaying as /abckatte.com/abc/test. One slash is missing.
I tried with appending two more slash like.
String path = "////abckatte.com/abc/test";
then also it is not working.
You could make use of Apache Commons VFS 2, as it provides access to several file systems. Chek it out here, in Local files file://///somehost/someshare/afile.txt.
The getResourceAsStream-method returns null whenever running the executable jar in a directory which ends with a exclamation mark.
For the following example, I have a Eclipse project the following directory structure:
src\ (Source Folder)
main\ (Package)
Main.java
res\ (Source Folder)
images\
Logo.png
I'm reading the Logo.png as follows:
public static void main(String[] args) throws IOException {
try (InputStream is = Main.class.getClassLoader().getResourceAsStream("images/Logo.png")) {
Image image = ImageIO.read(is);
System.out.println(image);
}
}
See the attachment for 2 test cases. First, the executable jar is started from the directory "D:\test123!##" without any problems. Secondly, the executable jar is started from the directory "D:\test123!##!!!", with problems.
Are directories ending with an exclamation mark not supported? Is the code wrong?
Thanks in advance.
Probably because of this bug or any of the many similar bugs in the Java bug database:
http://bugs.sun.com/view_bug.do?bug_id=4523159
The reason is that "!/" in a jar URL is interpreted as the separator between the JAR file name and the path within the JAR itself. If a directory name ends with !, the "!/" character sequence at the end of the directory is incorrectly interpreted. In your case, you are actually trying to access a resource with the following URL:
jar:file:///d:/test1231##!!!/test.jar!/images/Logo.png
The bug has been open for almost 12 years and is not likely to be fixed. Actually I don't know how it can be fixed without breaking other things. The problem is the design decision to use ! as a character with a special meaning (separator) in the URL scheme for JAR files:
jar:<URL for JAR file>!/<path within the JAR file>
Since the exclamation mark is an allowed character in URLs, it may occur both in the URL to the JAR file itself, as well as in the path within the JAR file, making it impossible in some cases to find the actual "!/" separator.
A simple work around for Windows is to use "\" instead of "/" in the path. That would mean the "!/" character sequence is found after the full path. For instance:
new URL("jar:file:\\d:\\test1231##!!!\\test.jar!/images/Logo.png");
My Code:
File jar = new File(jarPath + "/" + jarName);
URL url = new URL("jar:" + jar.toURI() + "!" + dataFilePath);
InputStream stream = null;
try {
stream = url.openStream();
} catch (FileNotFoundException e) {
// Windows fix
URL urlFix = new URL("jar:" + jar.toURI().toString().replace('/', '\\')
+ "!" + dataFilePath);
stream = urlFix.openStream();
}
I use toURI() because it handles things like spaces.
Fixes:
The fix itself would be for Java to check if the file exists and if not continue to the next separator (the "!/" part of the url) until the separators are exhausted, then throw the exception. So it would see that "d:\test1231##!!" throws a java.io.FileNotFoundException and would then try "d:\test1231##!!!\test.jar" which does exist. This way it does not matter if there are "!" in the file path or in the jar's files.
Alternatively the "!/" can be switched to something else that is an illegal file name or to something specific (like "jarpath:").
Alternatively make the jar's file path use another parameter.
Note:
It may be possible to override something, swap a handler, or change the code to open the file first then look inside the jar file later but I have not looked.
If I try to download file like like below it is getting downloaded here
client.retrieveFile("/" + filename, fos); // working
But if i try to to download FTP file to particular local directory as below it is not getting downloaded there. Can anybody tell me why this is happening ?
client.retrieveFile("C:\MydownloadedFiles" + filename, fos); // not working
Thanks.
Please escape the "\".
It should be:
client.retrieveFile("C:\\MydownloadedFiles" + "\\" + filename, fos);
In general I would recommend working with the constant File.separator, to support cross-platform.
Another idea I have in mind (please test it) is the following:
File downloadsDirectory = new File("c:","MyDownloadedFiles");
File retrievedFileOnLocalComp = new File(downloadsDirectory.getAbsolutePath(),filename);
client.retrieveFile(retrievedFileOnComp.getAbsolutePath(),fos);
Explanation-
The first line creates a download directory under the parent path of "c:"
The second line creates the file name to download to with parent directory equals to the absolute path of the result from the first line.
The third downloads to it.
You had an issue with rememembering to use "\\".
I suggest to use these three lines in order to solve this issue and to get rid of platform dependant decision on the slash-type.
I'm uploading images using Spring and Hibernate. I'm saving images on the server as follows.
File savedFile = new File("E:/Project/SpringHibernet/MultiplexTicketBooking/web/images/" + itemName);
item.write(savedFile);
Where itemName is the image file name after parsing the request (enctype="multipart/form-data"). I however need to mention the relative path in the constructor of File. Something like the one shown below.
File savedFile = new File("MultiplexTicketBooking/web/images/" + itemName);
item.write(savedFile);
But it doesn't work throwing the FileNotFoundException. Is there a way to specify a relative path with File in Java?
Try printing the working directory from your program.
String curDir = System.getProperty("user.dir");
Gets you that directory. Then check if the directories MultiplexTicketBooking/web/images/ exist in that directory.
Can't count the number of times I've been mistaken about my current dir and spent some time looking for a file I wrote to...
It seems the server should offer functionality as might be seen in the methods getContextPath() or getRealPath(String). It would be common to build paths based on those types of server related and reproducible paths. Do not use something like user.dir which makes almost no sense in a server.
Update
ServletContext sc=request.getSession().getServletContext();
File savedFile = new File(sc.getRealPath("images")+"\\" + itemName);
Rather than use "\\" I'd tend to replace that with the following which will cause the correct file separator to be used for each platform. Retain cross-platform compatibility for when the client decides to swap the MS/ISS based server out for a Linux/Tomcat stack. ;)
File savedFile = new File(sc.getRealPath("images"), itemName); //note the ','
See File(String,String) for details.
You could get the path of your project using the following -
File file = new File("");
System.out.println("" + file.getAbsolutePath());
So you could have a constants or a properties file where you could define your path which is MultiplexTicketBooking/web/images/ after the relative path.
You could append your path with the path you get from file.getAbsolutePath() and that will be the real path of the file. - file.getAbsolutePath() + MultiplexTicketBooking/web/images/.
Make sure the folders after the Project path i.e. MultiplexTicketBooking/web/images/ exist.
You can specify the path both absolute and relative with File. The FileNotFoundException can be thrown because the folder might be there. Try using the mkdirs() method first in to create the folder structure you need in order to save your file where you're trying to save it.