How to Lock a File Using Java (If Possible)

How can I lock a file using java (if possible)

FileChannel.lock is probably what you want.

try (
FileInputStream in = new FileInputStream(file);
java.nio.channels.FileLock lock = in.getChannel().lock();
Reader reader = new InputStreamReader(in, charset)
) {
...
}

(Disclaimer: Code not compiled and certainly not tested.)

Note the section entitled "platform dependencies" in the API doc for FileLock.

Java FileLock for Reading and Writing

  1. Are you aware that locking the file won't keep other processes from touching it unless they also use locks?
  2. You have to lock via a writable channel. Get the lock via a RandomAccessFile in "rw" mode and then open your FileInputStream. Make sure to close both!

Locking a file in Java

I think you are going the wrong way : you need the OS to make a file read-only and everybody with enough privileges will be able to override this and modify the file.

Instead of locking the file just encrypt it. It would be much more appropriate for your needs. See https://stackoverflow.com/a/27962481/1980659 for easy file encryption in Java.

How to lock a file then clear its contents?

By reading the doc I found that you can create a RandomAccessFile instance first, then call getChannel on it. Once you get the channel, you can lock it and channel.truncate(0) will clear its content. You can then proceed to write to the file by calling channel.write(byteBuffer).

Here's what I mean:

RandomAccessFile raFile = new RandomAccessFile(filename, 'rw');
FileChannel fc = raFile.getChannel();
fc.lock();
fc.truncate(0);
fc.write(someBuffer);
raFile.close(); // Will release the lock.

Creating a Java program that locks a file

You can do this where you want to lock:

File f1 = new File(Your file path);
f1.setExecutable(false);
f1.setWritable(false);
f1.setReadable(false);

And for unlock you can just do this:

File f1 = new File(Your file path);
f1.setExecutable(true);
f1.setWritable(true);
f1.setReadable(true);

Before applying

Check if the file permission allow:

file.canExecute(); – return true, file is executable; false is not.
file.canWrite(); – return true, file is writable; false is not.
file.canRead(); – return true, file is readable; false is not.

For a Unix system you have to put in this code:

Runtime.getRuntime().exec("chmod 777 file");

File Locking in java

The documentation on FileLock class says:

This file-locking API is intended to map directly to the native locking facility of the underlying operating system. Thus the locks held on a file should be visible to all programs that have access to the file, regardless of the language in which those programs are written.

So it seems that you could just use same code as in your first snippet:

File folder = new File("E:\\folder_to_LIST_OF_FILES");
File[] listOfFiles = folder.listFiles();

for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
FilesDto returnDto = new FilesDto();
returnDto.setFileName(FilenameUtils.removeExtension(listOfFiles[i].getName()));

File file = new File("filename");
FileChannel channel = new RandomAccessFile(file, "rw").getChannel();
FileLock lock;
try {
lock = channel.tryLock();
} catch (OverlappingFileLockException e) {
// File is already locked
}

if (lock==null) {
returnDto.setStatus("unlocked");
returnDto.setFilePath(listOfFiles[i].getAbsolutePath());

} else {
lock.release();
returnDto.setStatus("Locked");
}

returnDtoList.add(returnDto);
}
}

How to lock a file on different application levels?

If you only need to write the file rarely, how about writing the file under a temporary name and then using rename to make it "visible" to the readers?

This only works reliably with Unix file systems, though. On Windows, you will need to handle the case that some process has the file open (for reading). In this case, the rename will fail. Just try again until the rename succeeds.

I suggest to test this thoroughly because you might run into congestion: There are so many read requests that the writer task can't replace the file for a long time.

If that is the case, make the readers check for the temporary file and wait a few moments with the next read until the file vanishes.

Java: How to hold off read/writing a file while it's locked

The standard way to use FileLock, is to open the file, e.g. via FileChannel.open, followed by tryLock. The presence of a lock does not prevent other processes from opening the file.

This can be demonstrated by the following program:

import java.io.IOException;
import java.nio.channels.*;
import java.nio.file.*;

class Locking {
public static void main(String[] args) throws IOException, InterruptedException {
if(args.length > 0) {
String me = String.format("%6s ", ProcessHandle.current());
Path p = Paths.get(args[0]);
try(FileChannel fc = FileChannel.open(p,
StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {

FileLock l = fc.tryLock();
if(l == null) System.out.println(me + "could not acquire lock");
else {
System.out.println(me + "got lock");
Thread.sleep(3000);
System.out.println(me + "releasing lock");
l.release();
}
}
}
else {
Path p = Files.createTempFile("lock", "test");
String[] command = {
Paths.get(System.getProperty("java.home"), "bin", "java").toString(),
"-cp", System.getProperty("java.class.path"),
"Locking", p.toString()
};
ProcessBuilder b = new ProcessBuilder(command).inheritIO();
Process p1 = b.start(), p2 = b.start(), p3 = b.start();
p1.waitFor();
p2.waitFor();
p3.waitFor();
Files.delete(p);
}
}
}

which prints something alike

 12116 got lock
13948 could not acquire lock
13384 could not acquire lock
12116 releasing lock

which can be demonstrated online on tio.run

While this program works the same under Windows, this operating system supports opening files unshared, preventing other processes from opening. If a different process has opened the file in that way, we can’t even open it to probe the locking state.

This is not the way, Java opens the file, however, there’s a non-standard open option to replicate the behavior, com.sun.nio.file.ExtendedOpenOption.NOSHARE_WRITE. In recent JDKs, it’s in the jdk.unsupported module.

When we run the following extended test program under Windows

import java.io.IOException;
import java.nio.channels.*;
import java.nio.file.*;
import java.util.HashSet;
import java.util.Set;

class LockingWindows {
public static void main(String[] args) throws IOException, InterruptedException {
if(args.length > 0) {
String me = String.format("%6s ", ProcessHandle.current());
Path p = Paths.get(args[0]);
Set<OpenOption> options
= Set.of(StandardOpenOption.WRITE, StandardOpenOption.APPEND);
if(Boolean.parseBoolean(args[1])) options = addExclusive(options);
try(FileChannel fc = FileChannel.open(p, options)) {
FileLock l = fc.tryLock();
if(l == null) System.out.println(me + "could not acquire lock");
else {
System.out.println(me + "got lock");
Thread.sleep(3000);
System.out.println(me + "releasing lock");
l.release();
}
}
}
else {
Path p = Files.createTempFile("lock", "test");
String[] command = {
Paths.get(System.getProperty("java.home"), "bin", "java").toString(),
"-cp", System.getProperty("java.class.path"),
"LockingWindows", p.toString(), "false"
};
ProcessBuilder b = new ProcessBuilder(command).inheritIO();
for(int run = 0; run < 2; run++) {
Process p1 = b.start(), p2 = b.start(), p3 = b.start();
p1.waitFor();
p2.waitFor();
p3.waitFor();
if(run == 0) {
command[command.length - 1] = "true";
b.command(command);
System.out.println("\nNow with exclusive mode");
}
}
Files.delete(p);
}
}

private static Set<OpenOption> addExclusive(Set<OpenOption> options) {
OpenOption o;
try {
o = (OpenOption) Class.forName("com.sun.nio.file.ExtendedOpenOption")
.getField("NOSHARE_WRITE").get(null);
options = new HashSet<>(options);
options.add(o);
} catch(ReflectiveOperationException | ClassCastException ex) {
System.err.println("opening exclusive not supported");
}
return options;
}
}

we will get something like

  2356 got lock
6412 could not acquire lock
9824 could not acquire lock
2356 releasing lock

Now with exclusive mode
9160 got lock
Exception in thread "main" java.nio.file.FileSystemException: C:\Users\...\Temp\lock13936982436235244405test: The process cannot access the file because it is being used by another process
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:92)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
at java.base/sun.nio.fs.WindowsFileSystemProvider.newFileChannel(WindowsFileSystemProvider.java:121)
at java.base/java.nio.channels.FileChannel.open(FileChannel.java:298)
at LockingWindows.main(LockingWindows.java:148)
Exception in thread "main" java.nio.file.FileSystemException: C:\Users\...\Temp\lock13936982436235244405test: The process cannot access the file because it is being used by another process
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:92)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
at java.base/sun.nio.fs.WindowsFileSystemProvider.newFileChannel(WindowsFileSystemProvider.java:121)
at java.base/java.nio.channels.FileChannel.open(FileChannel.java:298)
at LockingWindows.main(LockingWindows.java:148)
9160 releasing lock

The similarity to the outcome of your test suggests that the Windows program you ran concurrently to your Java program did use such a mode.

For your Java programs, no such issue should arise, as long as you don’t use that mode. Only when you have to interact with another Windows program not using the collaborative locking, you have to deal with this.



Related Topics



Leave a reply



Submit