Copy file exception with TrueZIP - java

Here is my code to copy a war file to another using TrueZIP.
TFile srcFile = new TFile(sourceFilePath);
TFile destFile = new TFile(destFilePath);
if (!destFile.getParentFile().exists()) {
destFile.getParentFile().mkdirs();
}
try {
srcFile.cp_rp(destFile);
TVFS.umount();
} catch (IOException e) {
e.printStackTrace();
}
For example, source file location:
I:\Code-Base\test.7.2.0\projects\test\main\branches\test.7.2.0_for_test\ui\portlets\dist\portlets.war\WEB-INF\server-config.wsdd
and destination location:
D:\deploy\work\237798_237980\web\deploy\prtlets.war\WEB-INF\server-config.wsdd
I've already checked that both paths exists, but I get an InputException error::
de.schlichtherle.truezip.io.InputException: de.schlichtherle.truezip.fs.FsReadOnlyArchiveFileSystemException: This is a read-only archive file system!
at de.schlichtherle.truezip.socket.IOSocket.copy(IOSocket.java:102)
at de.schlichtherle.truezip.file.TBIO.cp0(TBIO.java:221)
at de.schlichtherle.truezip.file.TBIO.cp_r0(TBIO.java:179)
at de.schlichtherle.truezip.file.TBIO.cp_r(TBIO.java:138)
at de.schlichtherle.truezip.file.TFile.cp_rp(TFile.java:3210)
at com.accela.work.WorkThread.run(WorkThread.java:110)
at com.accela.work.Worker.getUpgradePackageByVersion(Worker.java:162)
at com.accela.work.Main.generateUpgradePackage(Main.java:114)
at com.accela.work.Main.getUpgradePackageByVersion(Main.java:107)
at com.accela.work.Main.main(Main.java:75)
Caused by: de.schlichtherle.truezip.fs.FsReadOnlyArchiveFileSystemException: This is a read-only archive file system!
at de.schlichtherle.truezip.fs.FsReadOnlyArchiveFileSystem.mknod(FsReadOnlyArchiveFileSystem.java:54)
at de.schlichtherle.truezip.fs.FsBasicArchiveController$1Output.mknod(FsBasicArchiveController.java:273)
at de.schlichtherle.truezip.fs.FsBasicArchiveController$1Output.getLocalTarget(FsBasicArchiveController.java:220)
at de.schlichtherle.truezip.fs.FsBasicArchiveController$1Output.getLocalTarget(FsBasicArchiveController.java:217)
at de.schlichtherle.truezip.fs.FsContextController$Output.getLocalTarget(FsContextController.java:296)
at de.schlichtherle.truezip.fs.FsContextController$Output.getLocalTarget(FsContextController.java:280)
at de.schlichtherle.truezip.socket.DelegatingOutputSocket.getLocalTarget(DelegatingOutputSocket.java:47)
at de.schlichtherle.truezip.socket.DelegatingOutputSocket.getLocalTarget(DelegatingOutputSocket.java:21)
at de.schlichtherle.truezip.socket.DelegatingOutputSocket.getLocalTarget(DelegatingOutputSocket.java:47)
at de.schlichtherle.truezip.socket.DelegatingOutputSocket.getLocalTarget(DelegatingOutputSocket.java:21)
at de.schlichtherle.truezip.fs.FsSyncController$Output.getLocalTarget(FsSyncController.java:421)
at de.schlichtherle.truezip.fs.FsSyncController$Output.getLocalTarget(FsSyncController.java:408)
at de.schlichtherle.truezip.fs.FsLockController$Output$1GetLocalTarget.call(FsLockController.java:498)
at de.schlichtherle.truezip.fs.FsLockController$Output$1GetLocalTarget.call(FsLockController.java:495)
at de.schlichtherle.truezip.fs.FsLockController.locked(FsLockController.java:316)
at de.schlichtherle.truezip.fs.FsLockController.writeLocked(FsLockController.java:268)
at de.schlichtherle.truezip.fs.FsLockController$Output.getLocalTarget(FsLockController.java:501)
at de.schlichtherle.truezip.fs.FsLockController$Output.getLocalTarget(FsLockController.java:484)
at de.schlichtherle.truezip.socket.DelegatingOutputSocket.getLocalTarget(DelegatingOutputSocket.java:47)
at de.schlichtherle.truezip.socket.DelegatingOutputSocket.getLocalTarget(DelegatingOutputSocket.java:21)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$1Output$GetLocalTarget.call(FsFalsePositiveArchiveController.java:374)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$1Output$GetLocalTarget.call(FsFalsePositiveArchiveController.java:367)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$TryChild.call(FsFalsePositiveArchiveController.java:507)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController.call(FsFalsePositiveArchiveController.java:104)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$1Output.getLocalTarget(FsFalsePositiveArchiveController.java:364)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$1Output.getLocalTarget(FsFalsePositiveArchiveController.java:348)
at de.schlichtherle.truezip.socket.InputSocket.getPeerTarget(InputSocket.java:50)
at de.schlichtherle.truezip.fs.FsBasicArchiveController$1Input.getDelegate(FsBasicArchiveController.java:199)
at de.schlichtherle.truezip.socket.DelegatingInputSocket.getBoundSocket(DelegatingInputSocket.java:43)
at de.schlichtherle.truezip.socket.DelegatingInputSocket.newInputStream(DelegatingInputSocket.java:63)
at de.schlichtherle.truezip.fs.FsContextController$Input.newInputStream(FsContextController.java:273)
at de.schlichtherle.truezip.fs.FsResourceController$Input.newInputStream(FsResourceController.java:242)
at de.schlichtherle.truezip.socket.DelegatingInputSocket.newInputStream(DelegatingInputSocket.java:63)
at de.schlichtherle.truezip.fs.FsSyncController$Input.newInputStream(FsSyncController.java:378)
at de.schlichtherle.truezip.fs.FsLockController$Input$1NewInputStream.call(FsLockController.java:455)
at de.schlichtherle.truezip.fs.FsLockController$Input$1NewInputStream.call(FsLockController.java:452)
at de.schlichtherle.truezip.fs.FsLockController.locked(FsLockController.java:328)
at de.schlichtherle.truezip.fs.FsLockController.writeLocked(FsLockController.java:268)
at de.schlichtherle.truezip.fs.FsLockController$Input.newInputStream(FsLockController.java:459)
at de.schlichtherle.truezip.fs.FsFinalizeController$Input.newInputStream(FsFinalizeController.java:177)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$1Input$NewInputStream.call(FsFalsePositiveArchiveController.java:333)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$1Input$NewInputStream.call(FsFalsePositiveArchiveController.java:326)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$TryChild.call(FsFalsePositiveArchiveController.java:507)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController.call(FsFalsePositiveArchiveController.java:104)
at de.schlichtherle.truezip.fs.FsFalsePositiveArchiveController$1Input.newInputStream(FsFalsePositiveArchiveController.java:323)
at de.schlichtherle.truezip.socket.IOSocket.copy(IOSocket.java:100)
... 9 more

TrueZIP does a simple test to check if the archive file is writable. If this test fails, the archive file system is set read-only as indicated by the exception.
In most cases, this is simply an issue with the access permissions. But Windows is particularly bitchy. For example, if there is another tool concurrently accessing the archive file (many Explorer plug-ins do this) then the file is effectively read-only, too.
So please stay away from the archive file (and best, its directory) while the operation is running.

You cannot do a replace on a read-only file, because you would have to delete it, i.e. write to it.
Make sure your destFile is writeable.

Related

Java in Mac OS X Catalina - Zip file not found in Resources, while the file is found when run from Eclipse

I have a compressed file in the Resources folder which is decompressed in the folder where the program is run for the first time. If the program starts from Eclipse, the file is found and decompressed without problems. When I export the program in a jar file, and run the program with:
java -jar JRS2020-31.jar
the output is:
java.lang.RuntimeException: Error unzipping file initialData/compressed.zip
at InterpreteSQL.Main.unzip(Main.java:111)
at InterpreteSQL.Main.CreateInitialDirectoryIfNotFound(Main.java:77)
at InterpreteSQL.Main.main(Main.java:60)
Caused by: java.io.FileNotFoundException: file:/Users/XXX/JRS2020-31.jar!/initialData/compressed.zip (No such file or directory)
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.<init>(ZipFile.java:219)
at java.util.zip.ZipFile.<init>(ZipFile.java:149)
at java.util.zip.ZipFile.<init>(ZipFile.java:120)
at InterpreteSQL.Main.unzip(Main.java:88)
... 2 more
Note that other files in resources (help html files) are opened regularly in the program.
This is the code that opens the file:
public static void unzip(String zipFilePath, String unzipDir) throws Exception {
try{
ZipFile zipFile = new ZipFile(Main.class.getClassLoader().getResource(zipFilePath).getFile());
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while(entries.hasMoreElements()){
...
and it is called with:
unzip("initialData/compressed.zip", "JRS_directory");
Note that the program is run in the home folder, and that it can create directories and files.
Any idea about the problem? Thank you very much.
You'reusing getResources wrong, in two ways.
You're asking the resource to turn itself into a file for no reason. In your eclipse run, the resource IS a file. However, in a jar run, it's not (it is an entry in a jar, which is not a file). Don't call .getFile() on these resources; the whole point of the abstraction is that it might not be a file at all. Could be an entry in a jar. Could be imported via the network. All you know is: Load this data from where-ever you're finding the class files.
Don't use ZipFile; use ZipInputStream which has very similar API. Then use getResourceAsStream.
The proper form is Main.class.getResource(). Avoid the classloader intermediate. It is both a pointless method call, and in rare cases will break (in certain contexts, a class's classloader is null, causing a nullpointerexception). Note that this form means the string you pass in is relative to the class location. If you don't want that, add a leading slash.
While you're at it, these are resources that must be closed, so let's use the try-with-resources construct to ensure this is done properly even in the face of exceptions.
Putting this together:
try (InputStream raw = Main.class.getResourceAsStream("/" + zipFilePath);
ZipInputStream zip = new ZipInputStream(raw)) {
for (ZipEntry entry; (entry = zip.getNextEntry()) != null; ) {
// do something with entry here
}
}

FileOutputStream try-with-resources doesn't close file descriptor

I have code that extracts some specific large (about 15k entries) binary serialized file archive to folder on disk.
public void extractExact(Path absolutePath, DoubleConsumer progressConsumer) throws IOException
{
...
// Extract to file channel
try (final FileOutputStream fos = new FileOutputStream(absolutePath.toFile()))
{
PakExtractor.Extract(pakFile, Entry, fos.getChannel(), progressConsumer);
}
}
extractExact function calls for every entry in archive.
after this, if I try to call Files.delete(<archive_file_path>) method - I will get an exception:
java.nio.file.FileSystemException: The process cannot access the file because it is being used by another process.
I checked my archive file in Process Explorer search and it says that I have ~15k file openings by my java.exe (as many as the files in the archive)
this happens only in windows (jdk1.8.0_162). On Linux I don't have any problems with "zombie" opened files.
Finally - we found the solution. Many thanks to #Netherwire. FileChannel class have map method that does some implicit copy operations with file descriptors, so be careful when use it. Here is more information.

In Java, why does class.getResource("/path/to/res"); not work in the runnable jar when copying files to the system?

I have been working on a project that requires the user to "install" the program upon running it the first time. This installation needs to copy all the resources from my "res" folder to a dedicated directory on the user's hard drive. I have the following chunk of code that was working perfectly fine, but when I export the runnable jar from eclipse, I received a stack trace which indicated that the InputStream was null. The install loop passes the path of each file in the array list to the export function, which is where the issue is (with the InputStream). The paths are being passed correctly in both Eclipse and the runnable jar, so I doubt that is the issue. I have done my research and found other questions like this, but none of the suggested fixes (using a classloader, etc) have worked. I don't understand why the method I have now works in Eclipse but not in the jar?
(There also exists an ArrayList of File called installFiles)
private static String installFilesLocationOnDisk=System.getProperty("user.home")+"/Documents/[project name]/Resources/";
public static boolean tryInstall(){
for(File file:installFiles){
//for each file, make the required directories for its extraction location
new File(file.getParent()).mkdirs();
try {
//export the file from the jar to the system
exportResource("/"+file.getPath().substring(installFilesLocationOnDisk.length()));
} catch (Exception e) {
return false;
}
}
return true;
}
private static void exportResource(String resourceName) throws Exception {
InputStream resourcesInputStream = null;
OutputStream resourcesOutputStream = null;
//the output location for exported files
String outputLocation = new File(installFilesLocationOnDisk).getPath().replace('\\', '/');
try {
//This is where the issue arises when the jar is exported and ran.
resourcesInputStream = InstallFiles.class.getResourceAsStream(resourceName);
if(resourcesInputStream == null){
throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file.");
}
//Write the data from jar's resource to system file
int readBytes;
byte[] buffer = new byte[4096];
resourcesOutputStream = new FileOutputStream(outputLocation + resourceName);
while ((readBytes = resourcesInputStream.read(buffer)) > 0) {
resourcesOutputStream.write(buffer, 0, readBytes);
}
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
} finally {
//Close streams
resourcesInputStream.close();
resourcesOutputStream.close();
}
}
Stack Trace:
java.lang.Exception: Cannot get resource "/textures\gameIcon.png" from Jar file.
All help is appreciated! Thanks
Stack Trace:
java.lang.Exception: Cannot get resource "/textures\gameIcon.png" from Jar file.
The name if the resource is wrong. As the Javadoc of ClassLoader.getResource(String) describes (and Class.getResourceAsStream(String) refers to ClassLoader for details):
The name of a resource is a /-separated path name that identifies
the resource.
No matter whether you get your resources from the File system or from a Jar File, you should always use / as the separator.
Using \ may sometimes work, and sometimes not: there's no guarantee. But it's always an error.
In your case, the solution is a change in the way that you invoke exportResource:
String path = file.getPath().substring(installFilesLocationOnDisk.length());
exportResource("/" + path.replace(File.pathSeparatorChar, '/'));
Rename your JAR file to ZIP, uncompress it and check where did resources go.
There is a possibility you're using Maven with Eclipse, and this means exporting Runnable JAR using Eclipse's functionality won't place resources in JAR properly (they'll end up under folder resources inside the JAR if you're using default Maven folder names conventions).
If that is the case, you should use Maven's Assembly Plugin (or a Shade plugin for "uber-JAR") to create your runnable JAR.
Even if you're not using Maven, you should check if the resources are placed correctly in the resulting JAR.
P.S. Also don't do this:
.getPath().replace('\\', '/');
And never rely on particular separator character - use java.io.File.separator to determine system's file separator character.

File not found exception with external files

Hi i have made a small program that reads a config file. This file is stored outside the actual jar file. On the same level as the jarfile actually.
When i start my program from a commandline in the actual directory (ie. D:\test\java -jar name.jar argument0 argument1) in runs perfectly.
But when i try to run the program from another location then the actual directory i get the filenotfound exception (ie. D:\java -jar D:\test\name.jar argument0 argument1).
The basic functionality does seem to work, what am i doing wrong?
As requested a part of the code:
public LoadConfig() {
Properties properties = new Properties();
try {
// load the properties file
properties.load(new FileInputStream("ibantools.config.properties"));
} catch (IOException ex) {
ex.printStackTrace();
} // end catch
// get the actual values, if the file can't be read it will use the default values.
this.environment = properties.getProperty("application.environment","tst");
this.cbc = properties.getProperty("check.bankcode","true");
this.bankcodefile = properties.getProperty("check.bankcodefile","bankcodes.txt");
} // end loadconfig
The folder looks like this:
This works:
This doesn't:
The jar doesn't contain the text file.
When reading a File using the String/path constructors of File, FileInpustream, etc.. a relative path is derived from the working directory - the directory where you started your program.
When reading a file from a Jar, the file being external to the jar, you have at least two options :
Provide an absolute path: D:/blah/foo/bar
Make the directory where your file is located part of the class path and use this.getClass().getClassLoader().getResourceAsStream("myfile")
The latter is probably more appropriate for reading configuration files stored in a path relative to the location of your application.
There could be one more possibility:
If one part of your code is writing the file and another one is reading, then it is good to consider that the reader is reading before the writer finishes writing the file.
You can cross check this case by putting your code on debug mode. If it works fine there and gives you FileNotFoundException, then surely this could be the potential reason of this exception.
Now, how to resolve:
You can use retry mechanism something similar to below code block
if(!file..exists()){
Thread.sleep(200);
}
in your code and change the sleep value according to your needs.
Hope that helps.!!

get File from JAR

I'm using Spring's Resource abstraction to work with resources (files) in the filesystem. One of the resources is a file inside a JAR file. According to the following code, it appears the reference is valid
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
// The path to the resource from the root of the JAR file
Resource fileInJar = resourcePatternResolver.getResources("/META-INF/foo/file.txt");
templateResource.exists(); // returns true
templateResource.isReadable(); // returns true
At this point, all is well, but then when I try to convert the Resource to a File
templateResource.getFile();
I get the exception
java.io.FileNotFoundException: class path resource [META-INF/foo/file.txt] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/D:/m2repo/uic-3.2.6-0.jar!/META-INF/foo/file.txt
at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:198)
at org.springframework.core.io.ClassPathResource.getFile(ClassPathResource.java:174)
What is the correct way to get a File reference to a Resource that exists inside a JAR file?
What is the correct way to get a File
reference to a Resource that exists
inside a JAR file?
The correct way is not doing that at all because it's impossible. A File represents an actual file on a file system, which a JAR entry is not, unless you have a special file system for that.
If you just need the data, use getInputStream(). If you have to satisfy an API that demands a File object, then I'm afraid the only thing you can do is to create a temp file and copy the data from the input stream to it.
If you want to read it, just call resource.getInputStream()
The exception message is pretty clear - the file does not reside on the file-system, so you can't have a File instance. Besides - what will do do with that File, apart from reading its content?
A quick look at the link you provided for Resource documentation, says the following:
Throws: IOException if the resource cannot be resolved as absolute file path,
i.e. if the resource is not available in a file system
Maybe the text file is inside a jar? In that case you will have to use getInputStream() to read its contents.
Just adding an example to the answers here. If you need a File (and not just the contents of it) from within your JAR, you need to create a temporary file from the resource first. (The below is written in Groovy):
InputStream inputStream = resourceLoader.getResource('/META-INF/foo/file.txt').inputStream
File tempFile = new File('file.txt')
OutputStream outputStream = new FileOutputStream(tempFile)
try {
IOUtils.copy(inputStream, outputStream)
} catch (IOException e) {
// Handle exception
} finally {
outputStream.close()
}

Categories