How to open multiple files with Runtime Object in Java? - java

I open office files (docx, xlsx) by using Runtime.getRuntime().exec(String cmd) function. Simultaneously I store meta data of these files in a database. In order to keep integrity I lock the file with a flag in the meta data so that no other user concurrently can modify the file. This implies that the flag must be automatically resetted after the user closes the file (e.g. closes the external process).
Here's the snippet that opens the file:
File file = new File("c:/test.docx");
Process process = null;
if(file.getName().endsWith("docx")) {
process = Runtime.getRuntime().exec("c:/msoffice/WINWORD.EXE "+file.getAbsolutePath());
} else if(file.getName().endsWith("xlsx")) {
process = Runtime.getRuntime().exec("c:/msoffice/EXCEL.EXE "+file.getAbsolutePath());
}
if(process!=null) {
new ProcessExitListener(file, process);
}
Here's my listener that waits until the user closes the file (and finally unlocks the file by setting the flag in the meta data):
private class ProcessExitListener extends Thread {
private File file;
private Process process;
public ProcessExitListener(File file, Process process) throws IOException {
this.setName("File-Thread ["+process.toString()+"]");
this.file = file;
this.process = process;
this.start();
}
#Override
public void run() {
try {
process.waitFor();
database.unlock(file);
} catch (InterruptedException ex) {
// print exception
}
}
}
This works fine for different file types, e.g. if I open 1 docx and 1 xlsx file simultaneously. But when opening 2 docx files, one of the process exits right after it has been initialized.
Any ideas why ?
Thanks for your help in advance!

But when opening 2 docx files, one of the process exists right after it has been initialized.
Probably because winword.exe process finds out that there is already one instance of it running, so instead of keeping two instances in memory, it just asks the first instance to open the second document. Don't know how it looks from GUI perspective, but looking at the task manager, try opening two Word documents from Windows Explorer. The second file won't cause second winword.exe process to start.
I can reproduce the exact same behaviour on Ubuntu Linux. When I ran:
$ geany foo.txt
and the geany editor wasn't yet running, the console hangs until I close the editor. But if instead I open another terminal and call:
$ geany bar.txt
this returns immediately and bar.txt is simply opened as another tab in already existing process.

Related

Simple java Process for opening files ends inexplicably

Part of my program needs to open files and documents, wait until user manually closes them and then deletes them. So I have a separate thread that just opens the file with Process class and waits for the Process to end, than it deletes the file. Here is the code:
#Override
public void run() {
try {
ProcessBuilder ps=new ProcessBuilder("cmd.exe","/C",path);
Process p=ps.start();
p.waitFor();
ps=new ProcessBuilder("cmd.exe","/C","del",path);
p=ps.start();
p.waitFor();
}catch(IOException | InterruptedException e) {
e.printStackTrace();
}
}
This works with pdf, txt or docx documents, but it doesn't work with images, the png or jpg files. In case of an image, thread opens the image with Windows Photos program but doesn't wait for process to end, instead it acts like the process is already dead and proceedes to delete it right after opening it, so instead of an image I see this:
My question is why does it act like the Process is immediately dead when I open PNG or JPG files, but it works normally when I open PDF, TXT, DOCX files? Is the problem in Windows Photos viewer maybe?
I managed to open image files adding
Thread.sleep(5000);
right after opening an image, but it still deletes the image after 5 seconds, it just somehow still stays open in Windows Photos viewer even if it has been deleted. Clearly this is not the solution, I want to properly solve this situation.
You can delete the file when your program exits. Why do you want that the File is delete right after the file is closed?
File file = new File("path");
file.deleteOnExit();

Use Java to build an index of files in Windows 10

I am writing a desktop application in Java to quickly find files. I have used the exec command in Java to run powershell to do this, as Java's os.walk method seems to be much slower. Right now it takes about 5 minutes to generate a text file that lists the contents of all files on my computer (a total of around 440,000 files).
This is fine, but the problem I have is that I have no way of updating this list of files. So if I change a few files in my file system and want to update my file list, I can't do so quickly (i.e. incrementally). Instead, I have to generate the file list all over from scratch.
I know you can use git-bash to create a locate database (using updatedb). Now this is an awesome solution, but the application I'm trying to create may be used by people who don't have that installed. So I'd like to do it using default apps provided with Windows (i.e. powershell, or natively in Java). I am trying to make this app easy to use, so I don't want the user to have to install a bunch of other dependencies.
The following code shows how to use Java and avoid Powershell altogether. It builds an array in memory and writes it to a text file (467,000 files listed) all in under 30 seconds!
Run the following code in Main or wherever you want. It calls the createFileList method.
List<Path> pathsArrayList = new ArrayList<>();
Path rootPath_obj;
rootPath_obj = Paths.get(this.configMap.get("root_path"));
createFileList(rootPath_obj);
Here's the tree stream traversal code:
public void createFileList(Path path_in) throws IOException, AccessDeniedException {
try (DirectoryStream<Path> mystream = Files.newDirectoryStream(path_in)) {
for (Path entry : mystream) {
if (Files.isDirectory(entry)) {
createFileList(entry);
}
pathsArrayList.add(entry);
}
}
catch (AccessDeniedException ex) {
// Do nothing, just move on to the next file
}
}
Now write the file to save for later. This is the listing of all files within the root path tree.
System.out.println("Writing database...");
try (FileWriter writer = new FileWriter(this.configMap.get("db_path"))) {
for(Path pth: pathsArrayList){
writer.write(pth.toString() + System.lineSeparator());
}
}
System.out.println("...database written.");

How to know if safe file move on WatchService event is possible? [duplicate]

I've tried to copy&paste a very small file into a folder which is observed by a watch service. The first time works great, but on all following copy&paste actions, i get an exception that another process handles the file already. With experiments I've found out that my service is informed when Windows creates the file and not when its content is copied. If I lock the file, Windows isn't able to copy any data and the file is empty. On the other hand, if I move the file into the directory, everything works fine.
Is that a bug from Windows? I wasn't able to test it on a mac or Linux workstation. Or maybe it was just me being incapable. Any help is appreciated.
My code looks like the following:
try (WatchService watchService = importPath.getFileSystem().newWatchService()) {
importPath.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
handleExistingFiles();
try {
do {
WatchKey watchKey = watchService.take();
if (!watchKey.isValid()) {
continue;
}
boolean hasCreationEvents = false;
for (WatchEvent<?> event : watchKey.pollEvents()) {
hasCreationEvents |= event.kind().equals(StandardWatchEventKinds.ENTRY_CREATE);
}
watchKey.reset();
if (hasCreationEvents) {
handleNewFiles();
}
}
while (!Thread.currentThread().isInterrupted());
}
catch (InterruptedException ignoredEx) {
Thread.currentThread().interrupt();
}
}
The copy operation is not always atomic.
With atomic copy (or move) you will get a single ENTRY_CREATE event and the file referenced by the event will be complete and available for reading.
If the copy is not atomic, you will receive an ENTRY_CREATE event when the file is created and then you will receive one or more ENTRY_MODIFY events while the file is being written by the copy operation.
There is no easy way to determine when the copy operation has finished writing to a file and released it. Depending on the OS and file system you could get FileNotFoundException when trying to open a file for reading while it is locked by the copy operation or you could successfully open a file but you will get partial contents when you actually read it.
You will have to implement some heuristics like trying to read a file immediately after ENTRY_CREATE and rescheduling the reading for some later time if the initial reading failed.

Spawned zip process doesn't seem to add file to archive

I am trying to add a text file to a zip archive through a Java program on Linux. The program spawns a process (using java.lang.Process) to execute the commandline "zip -j .zip .txt", reads the output and error streams of the spawned process and waits for the process to complete using waitFor(). Though the program seems to run fine (spawned process exits with exit code 0, indicating that the zip commandline was executed successfully) and the output read from output and error streams do not indicate any errors, at the end of the program the zip archive doesn't always contain the file supposed to have been added. This problem doesn't happen consistently though (even with the same existing-archive and file-to-add) - once in a while (perhaps once in 4 attempts) the zip is found to have been updated correctly. Strangely, the problem doesn't occur at all when the program is run through Eclipse debugger mode. Any pointers on why this problem occurs and how it can be addressed would be helpful. Thanks!
Below is the code snippet. The program calls addFileToZip(File, File, String):
public static void addFileToZip(final File zipFile, final File fileToBeAdded,
final String fileNameToBeAddedAs) throws Exception {
File tempDir = createTempDir();
File fileToBeAddedAs = new File(tempDir, fileNameToBeAddedAs);
try {
FileUtils.copyFile(fileToBeAdded, fileToBeAddedAs);
addFileToZip(zipFile, fileToBeAddedAs);
} finally {
deleteFile(fileToBeAddedAs);
deleteFile(tempDir);
}
}
public static void addFileToZip(final File zipFile, final File fileToBeAdded) throws Exception {
final String[] command = {"zip", "-j", zipFile.getAbsolutePath(), fileToBeAdded.getAbsolutePath()};
ProcessBuilder procBuilder = new ProcessBuilder(command);
Process proc = procBuilder.start();
int exitCode = proc.waitFor();
/*
* Code to read output/error streams of proc and log/print them
*/
if (exitCode != 0) {
throw new Exception("Unable to add file, error: " + errMsg);
}
}
Make sure no other process has the zip file locked for write, or the file being added locked for read. If you're generating the file to be added, make sure the stream is flushed and closed before spawning the zip utility.
I am trying to add a text file to a zip archive through a Java program on Linux.
Use the java.util.zip API, which:
Provides classes for reading and writing the standard ZIP and GZIP file formats.
If you intend to stick with using a Process to do this, be sure to implement all the suggestions of When Runtime.exec() won't.

How to handle incomplete files? Getting exception

I need to create a java program which will create thread to search for a file in particular folder(source folder) and pick the file immediately for process work(convert it into csv file format) once it found the file in the source folder. Problem i am facing now is file which comes to source folder is big size(FTP tool is used to copy file from server to source folder), thread is picking that file immediately before it copies fully to source folder and throwing exception. How do i stop thread until the file copy into source folder completely?. It has to pick the file for processing only after the file is copied completely into source folder.
Tha safest way is to download the file to a different location and then move it to the target folder.
Another variation mentioned by Bombe is to change the file name to some other extension after downloading and look only for files with that extension.
I only read the file which is not in write mode. This is safest as this means no other process is writing in this file. You can check if file is not in write mode by using canWrite method of File class.
This solution works fine for me as I also have the exact same scenario you facing.
You could try different things:
Repeatedly check the last modification date and the size of the file until it doesn’t change anymore for a given amount of time, then process it. (As pointed out by qbeuek this is neither safe nor deterministic.)
Only process files with names that match certain criteria (e.g. *.dat). Change the FTP upload/download process to upload/download files with a different name (e.g. *.dat.temp) and rename the files once they are complete.
Download the files to a different location and move them to your processing directory once they’re complete.
As Vinegar said, if it doesn’t work the first time, try again later. :)
If you have some control on the process that does the FTP you could potentially have it create a "flag file" in the source directory immediately AFTER the ftp for the big file is finished.
Then your Java thread has to check the presence of this flag file, if it's present then there is a file ready to be processed in the source directory. Before processing the big file, the thread should remove the flag file.
Flag file can be anything (even an empty file).
Assuming you have no control over FTP process...
Let it be like this. When you get the exception, then try to process it again next time. Repeat it until the file gets processed. Its good to keep few attributes in case of exception to check it later, like; name, last-modified, size.
Check the exact exception before deciding to process it later, the exception might occur for some other reason.
If your OS is Linux, and your kernel > 2.6.13, you could use the filesystem event notification API named inotify.
There's a Java implementation here : https://bitbucket.org/nbargnesi/inotify-java.
Here's a sample code (heavily inspired from the website).
try {
Inotify i = new Inotify();
InotifyEventListener e = new InotifyEventListener() {
#Override
public void filesystemEventOccurred(InotifyEvent e) {
System.out.println("inotify event occurred!");
}
#Override
public void queueFull(EventQueueFull e) {
System.out.println("inotify event queue: " + e.getSource() +
" is full!");
}
};
i.addInotifyEventListener(e);
i.addWatch(System.getProperty("user.home"), Constants.IN_CLOSE_WRITE);
} catch (UnsatisfiedLinkError e) {
System.err.println("unsatisfied link error");
} catch (UserLimitException e) {
System.err.println("user limit exception");
} catch (SystemLimitException e) {
System.err.println("system limit exception");
} catch (InsufficientKernelMemoryException e) {
System.err.println("insufficient kernel memory exception");
}
This is in Grails and I am using FileUtils Library from the Apache commons fame. The sizeof function returns the size in bytes.
def fileModified = sourceFile.lastModified()
def fileSize = FileUtils.sizeOf(sourceFile)
Thread.sleep(3000) //sleep to calculate size difference if the file is currently getting copied
if((fileSize != FileUtils.sizeOf(sourceFile)) && (fileModified != sourceFile.lastModified())) //the file is still getting copied to return
{
if(log.infoEnabled)
log.info("File is getting copied!")
return
}
Thread.sleep(1000) //breather for picking up file just copied.
Please note that this also depends on what utility or OS you are using to transfer the files.
The safest bet is to copy the file which is been copied or has been copied to different file or directory. The copy process is robust one and it assure you that file is present after the copying process. The one I am using is from commons API.
FileUtils.copyFileToDirectory(File f, Directory D)
If you are copying a huge file which is in process of getting copied beware that this will take time and you might like to start this in parallel thread or best have a seperate application dedicated for transfer process.

Categories