Upload retry mechanism using JSch library - java

I have a file to upload (say abc.pdf). Very first time I want to upload this file as a temp file (say abc.pdf.temp). Then , if the file is successfully transferred (fully transferred) then I need to rename it to its original name (abc.pdf). But if the file is not fully transferred then I need to delete the temp file that I uploaded initially since I don't want to keep a corrupted file in the server. Is this achievable to do using this JSch library. Below is the sample code. Does this code make sense to achieve this?
Sample Code:
originalFile = 'abc.pdf';
tempFile = 'abc.pdf.temp';
fileInputStream = createobject("java", "java.io.FileInputStream").init('C:\abc.pdf');
SftpChannel.put(fileInputStream,tempFile);
// Comparing remote file size with local file
if(SftpChannel.lstat(tempFile).getSize() NEQ localFileSize){
// Allow to Resume the file transfer since the file size is different
SftpChannel.put(fileInputStream,tempFile,SftpChannel.RESUME);
if(SftpChannel.lstat(tempFile).getSize() NEQ localFileSize){
// Check again if the file is not fully transferred (During RESUME) then
// deleting the file since dont want to keep a corrupted file in the server.
SftpChannel.rm(tempFile);
}
}else{//assuming file is fully transferred
SftpChannel.rename(tempFile ,originalFile);
}

It's very unlikely that after the put finishes without throwing, the file size won't match. It can hardly happen. Even if it happens, it makes little sense to call RESUME. If something catastrophic goes wrong that is not detected by put, RESUME is not likely to help.
And even if you want to try with RESUME, it does not make sense to try once. If you believe it makes sense to retry, you have to keep retrying until you succeed, not only once.
You should catch exception and resume/delete/whatever. That's the primary recovery mechanism. This is 100x more likely to happen than 1.

Related

Unlock a file opened by Excel, Word, or any program

The code I'm writing in Java is is close a file left open by the user. So, here is what typically happens: a user is editing an Excel file, they save it, leave it open, and then close the lid on their laptop. The file is still kept open and locked so no one else can edit it. Is there a way to kick them off and unlock the file? When they are using the file, it is "checked out." Here is what shows up:
What checked out looks like: (image)
The following code, interfacing through WinDAV with SharePoint, tells me if a file is locked or not (I know it's not great code, but it works and I've tried several other solutions including Filelock, Apache IO, FileStream, etc.):
String fileName = String.valueOf(node);
File file = new File(fileName);
boolean replaced;
File sameFileName = new File(fileName);
if(file.renameTo(new File(sameFileName + "_UNLOCK"))){
replaced = true; //file is currently not in use
(new File(sameFileName + "_UNLOCK")).renameTo(sameFileName);
}else{
replaced = false; //file is currently in use
}
So, how would I unlock a file now? The only other solution is PowerShell using SharePoint libraries, but that has a whole lot of other problems...
As per the post, you can use the tool Handle, which is a CLI tool to find out which process is locking the file. Once you have the process ID, you can kill that process. I'm not aware of any Java API that would identify the culprit process. For killing the process you can use taskkill, and you can call it using Runtime like this. Both the operation require you app to run at Administrator or above privilege.

fwrite() failed : EDC5024i:An attempt was made to close a file that had been open in another thread

I am submitting a JCL job to allocate a VB dataset in Mainframe. After submitting the job, the dataset gets created successfully.
Then I am running a java program in omvs region of mainframe, to open the file and write some contents into it. When I try to write the data into the file I am getting the below error.
//DD:SYS00011 : fwrite() failed. EDC5024I An attempt was made to close a file that had been opened by another thread.; errno=24 errno2=0xc0640021 last_op=0 errorCode=0x0.
JCL submitted to allocate the dataset:
//USERNAME JOB ABC,,NOTIFY=&SYSUID,CLASS=F,MSGLEVEL=(1,1),MSGCLASS=X
//STEP1 EXEC PGM=IEFBR14
//STEP DD DSN=ASD00T.SM.ULRL,
// DISP=(NEW,CATLG,DELETE),
// UNIT=SYSDA,SPACE=(1,(10,60),RLSE),AVGREC=M,
// DCB=(RECFM=VB),
// DSORG=PS
code to write the file:
zFileIn = new ZFile("//'ASD00T.INPUT.ULRL'", "rb,type=record,noseek");
if (zFileIn.getDsorg() != ZFile.DSORG_PS) {
throw new IllegalStateException("Input dataset must be DSORG=PS");
}
zFileOut = new ZFile("//'ASD00T.SM.ULRL'", "wb,type=record,noseek, recfm="+ zFileIn.getRecfm()+ ", lrecl="+ zFileIn.getLrecl());
long count = 0;
byte[] recBuf = new byte[zFileIn.getLrecl()];
int nRead;
while((nRead = zFileIn.read(recBuf)) >= 0) {
zFileOut.write(recBuf, 0, nRead);
count++;
}
The heart of your problem is that you need to invoke the ZFile.close() method after you're done writing. Doing so will guarantee that the open, writes and close all happen under the same thread and you should be fine. This is a side-effect of using conventional datasets instead of USS files.
The reason for this is complicated, but it has to do with the fact that in z/OS, "conventional" QSAM/BSAM/VSAM datasets behave slightly differently than do UNIX filesystem files.
If you were writing to a UNIX file (HFS, ZFS, NFS, etc) instead of a conventional dataset, what you're doing would work perfectly fine...this is because USS treats resource ownership differently - file handles are owned at a process level, not a thread. When you open a USS file, that file handle can be used or closed by any thread in the process...this is mandated by the various UNIX standards, so z/OS has no choice but to work this way.
Conventional datasets are a bit different. When you open a conventional dataset, the operating system structures that define the open file are stored in memory anchored to the thread where the file was opened. There's enough information in the file handle that you can do I/O from other threads, but closing the file needs to happen from the thread where the file was opened.
Now, since you don't seem to have a close() in your code, the file stays open until your Java thread ends. When your Java thread ends, the system runtime gets control in order to clean up any resources you might have allocated. It sees the lingering open file and tries to close it, but now it's not running under the thread that opened the file, so you get the failure you're seeing.
Normally, UNIX files and z/OS datasets work almost exactly the same, but this issue is one of the slight differences. IBM gets away with it from a standards compliance perspective since z/OS datasets aren't part of any standard, and generally, the way they can be used interchangeably is a great feature.
By the way, all of this is spelled out in the fine print of the LE (Language Environment) and C Runtime references.

Java 7 Watch Service ENTRY_CREATE triggered before file is written

I have a watch service watching a directory. Once files are created, I'm processing the directory and updating a tree view.
This works fine on ENTRY_DELETE, but sometimes (not always) when a WatchEvent of ENTRY_CREATE occurs, the file has not yet been written to the disk.
I've confirmed this by creating a new File() of the directory the watch service is registered to along with the path of the file and checking the exists() method, so it seems that the OS is triggering the create event before the file is actually created.
This question appears to be the same issue, but from the folder's point of view.
Any way I can work around this?
The event is triggered when a file is created. The file needs to be created before it can be written to. A file doesn't simply appear once it is fully written, it appears once it is created.
What you can do is once you get the creation event:
Create a File object to point to the file
Create a java.nio.channels.FileChannel for random access using RandomAccessFile with rw mode (so read & write access)
Lock the channel. This will block until the file is free for read/write access (read the more general Lock method for more info)
When the lock is acquired, your file was released by the process that wrote the file
A simplified example:
File lockFile = new File( "file_to_lock" );
FileChannel channel = new RandomAccessFile( lockFile, "rw" ).getChannel( );
channel.lock( );
I had the same issue, I added few seconds delay once the event is created before processing. Since Other application used to write the file and it used to take couple of seconds to flush the content and release the file.

FileTransfer "Saved file:" followed by FileNotFoundException

My app downloads a zip file from a remote webserver, then extracts it.
The javascript successfully calls FileTransfer, which logs:
FileTransfer Saved file: file:///data/data/com.yadda.yadda/update.zip
As part of the success function, javascript calls my custom update plugin which immediately tests for the file:
Context ctx = this.cordova.getActivity().getBaseContext();
File update = new File(ctx.getFilesDir(),"update.zip");
if(!update.exists()) Log.w("File not found",update.getAbsolutePath());
The log for that last line is:
File Not Found /data/data/com.yadda.yadda/update.zip
Later in a try/catch block I have an InputStream variable created and one of the catch blocks is a FileNotFoundException which is firing every time.
Begin edit - more info
The FileNotFoundException has an interesting bit. The file path is wrong - even though I'm sending the same "update" variable to create the FileInputStream
InputStream fileis = new FileInputStream(update);
And the interesting bit of the exception:
Caused by: java.io.FileNotFoundException: /data/data/com.yadda.yadda/files/update.zip
End edit
What is going wrong here? Cordova logs that the file transfer completed and the file was saved, but then the file doesn't exist when I test for it! When I create the FileInputStream, why is the path different, causing the exception?
What am I missing? Everything works fine in the IOS version of the app.
Edit 2: per request, I browsed the device filesystem and found that update.zip does indeed exist in /data/user/0/com.yadda.yadda
OK, somewhere there is a bug. I'm inclined to believe it's a bug in getAbsolutePath() because I'm seeing consistent operation elsewhere.
When I create the "File update" variable, then immediately test and log the update.getAbsolutePath() - it shows the correct path. But when I attempt to create the FileInputStream, the path is different (+ /files/)
So, a little searching and I found that in order to access the application data directory (without /files) I must send a different directory with the new File command. Here's what it looks like:
File update = new File(ctx.getApplicationInfo().dataDir,"update.zip");
Obtaining the dir with getFilesDir()
ctx.getFilesDir() = /data/data/com.yadda.yadda/files
Obtaining the correct dir
ctx.getApplicationInfo().dataDir = /data/data/com.yadda.yadda

Checking for write access in a directory before creating files inside it

My small utility application asks the user for an output directory via a GUI file selector.
Then it creates a lot of files in this output directory after some processing.
I need to check if the application has write access so that it informs the user and does
not continue with the processing (which might take a long time)
My first attempt was the canWrite() method of java.io.File. But this does not work
since it deals with the directory entry itself and not its contents. I have seen at least
one instance of a Windows XP folder that can be renamed or deleted but no files may be created
in it (because of permissions). This is actually my testcase.
I finally settled with the following solution
//User places the input file in a directory and selects it from the GUI
//All output files will be created in the directory that contains the input file
File fileBrowse = chooser.getSelectedFile(); //chooser is a JFileChooser
File sample = new File(fileBrowse.getParent(),"empty.txt");
try
{
/*
* Create and delete a dummy file in order to check file permissions. Maybe
* there is a safer way for this check.
*/
sample.createNewFile();
sample.delete();
}
catch(IOException e)
{
//Error message shown to user. Operation is aborted
}
However this does not feel elegant to me since it just tries to actually create a file and checks if the operation succeeds.
I suspect that there must be a better way for this but all solutions I have found so far
with Security Managers and stuff deal with Java Applets and not standalone applications.
Am I missing something?
What is the recommended way of checking for file access inside a directory before
actually writing the files?
I am using Java 5.
You could check the file permissions, make sure the directory exists, and do a lot of checking or find a library that does all that checking for you BUT (!) isn't the best way of checking to try ? If you check for permissions and the filesystem changes... you will have to change your code. But trying to write a file will ALWAYS tell you if you can write a file.
Your solution doesn't have to be the most elegant one. It's not a cheap hard coded patch or something ugly. It's just normal code. And it will always work. But if you don't like to see that check in the code just separate it by putting it in class which only goal is to check for the possibly of writing. In fact, you should put it in a utility class wheter you like the elegance or not.
The other solution would be to place your whole writing-to-the-hard-drive code, in the try. And if you can't write, the whole part will be skipped and you give feedback to the user with a message in the catch part.
it doesn't works even if you invoke canWrite on the final path?
File sample = new File(fileBrowse.getParent(),"empty.txt");
if (sample.canWrite()) {
doSomethingUseful(sample);
} else {
notifyUser();
}
you can use FilePermission to get the details .
I find one way where you need to implement SecurityManager the code is here and here
Using Java 1.8 I was able to use the following.
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(Paths.get(destDir), LinkOption.NOFOLLOW_LINKS);
Assert.assertTrue("User did not have read permission.", permissions.contains(PosixFilePermission.OWNER_READ));
Assert.assertTrue("User did not have execute permission.", permissions.contains(PosixFilePermission.OWNER_EXECUTE));
Assert.assertTrue("User did not have write permission.", permissions.contains(PosixFilePermission.OWNER_WRITE));
Assert.assertFalse("Group did have read permission.", permissions.contains(PosixFilePermission.GROUP_READ));
Assert.assertFalse("Group did have execute permission.", permissions.contains(PosixFilePermission.GROUP_EXECUTE));
Assert.assertFalse("Group did have write permission.", permissions.contains(PosixFilePermission.GROUP_WRITE));
Assert.assertFalse("Others did have read permission.", permissions.contains(PosixFilePermission.OTHERS_READ));
Assert.assertFalse("Others did have execute permission.", permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
Assert.assertFalse("Others did have write permission.", permissions.contains(PosixFilePermission.OTHERS_WRITE));

Categories

Resources