/* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* @test * @bug 4607272 6814948 6842687 * @summary Unit test for AsynchronousFileChannel#lock method * @key randomness */ import java.net.*; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.file.*; import static java.nio.file.StandardOpenOption.*; import java.nio.channels.*; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Random; import java.util.concurrent.*; public class Lock { static final Random rand = new Random(); public static void main(String[] args) throws Exception { if (args.length > 0 && args[0].equals("-lockslave")) { int port = Integer.parseInt(args[1]); runLockSlave(port); System.exit(0); } LockSlaveMirror slave = startLockSlave(); try { // create temporary file File blah = File.createTempFile("blah", null); blah.deleteOnExit(); // run tests testLockProtocol(blah, slave); testAsyncClose(blah, slave); // eagerly clean-up blah.delete(); } finally { slave.shutdown(); } } // test locking protocol static void testLockProtocol(File file, LockSlaveMirror slave) throws Exception { FileLock fl; // slave VM opens file and acquires exclusive lock slave.open(file.getPath()).lock(); AsynchronousFileChannel ch = AsynchronousFileChannel .open(file.toPath(), READ, WRITE); // this VM tries to acquire lock // (lock should not be acquire until released by slave VM) Future result = ch.lock(); try { result.get(2, TimeUnit.SECONDS); throw new RuntimeException("Timeout expected"); } catch (TimeoutException x) { } // slave VM releases lock slave.unlock(); // this VM should now acquire lock fl = result.get(); fl.release(); // slave VM acquires lock on range slave.lock(0, 10, false); // this VM acquires lock on non-overlapping range fl = ch.lock(10, 10, false).get(); fl.release(); // done ch.close(); slave.close(); } // test close of channel with outstanding lock operation static void testAsyncClose(File file, LockSlaveMirror slave) throws Exception { // slave VM opens file and acquires exclusive lock slave.open(file.getPath()).lock(); for (int i=0; i<100; i++) { AsynchronousFileChannel ch = AsynchronousFileChannel .open(file.toPath(), READ, WRITE); // try to lock file (should not complete because file is locked by slave) Future result = ch.lock(); try { result.get(rand.nextInt(100), TimeUnit.MILLISECONDS); throw new RuntimeException("Timeout expected"); } catch (TimeoutException x) { } // close channel with lock operation outstanding ch.close(); // operation should complete with AsynchronousCloseException try { result.get(); throw new RuntimeException("ExecutionException expected"); } catch (ExecutionException x) { if (!(x.getCause() instanceof AsynchronousCloseException)) { x.getCause().printStackTrace(); throw new RuntimeException("AsynchronousCloseException expected"); } } } slave.close(); } // starts a "lock slave" in another process, returning a mirror object to // control the slave static LockSlaveMirror startLockSlave() throws Exception { ServerSocketChannel ssc = ServerSocketChannel.open() .bind(new InetSocketAddress(0)); int port = ((InetSocketAddress)(ssc.getLocalAddress())).getPort(); String sep = FileSystems.getDefault().getSeparator(); String command = System.getProperty("java.home") + sep + "bin" + sep + "java"; String testClasses = System.getProperty("test.classes"); if (testClasses != null) command += " -cp " + testClasses; command += " Lock -lockslave " + port; Process p = Runtime.getRuntime().exec(command); IOHandler.handle(p.getInputStream()); IOHandler.handle(p.getErrorStream()); // wait for slave to connect SocketChannel sc = ssc.accept(); return new LockSlaveMirror(sc); } // commands that the slave understands static final String OPEN_CMD = "open"; static final String CLOSE_CMD = "close"; static final String LOCK_CMD = "lock"; static final String UNLOCK_CMD = "unlock"; static final char TERMINATOR = ';'; // provides a proxy to a "lock slave" static class LockSlaveMirror { private final SocketChannel sc; LockSlaveMirror(SocketChannel sc) { this.sc = sc; } private void sendCommand(String cmd, String... params) throws IOException { for (String s: params) { cmd += " " + s; } cmd += TERMINATOR; ByteBuffer buf = Charset.defaultCharset().encode(cmd); while (buf.hasRemaining()) { sc.write(buf); } // wait for ack buf = ByteBuffer.allocate(1); int n = sc.read(buf); if (n != 1) throw new RuntimeException("Reply expected"); if (buf.get(0) != TERMINATOR) throw new RuntimeException("Terminated expected"); } LockSlaveMirror open(String file) throws IOException { sendCommand(OPEN_CMD, file); return this; } void close() throws IOException { sendCommand(CLOSE_CMD); } LockSlaveMirror lock() throws IOException { sendCommand(LOCK_CMD); return this; } LockSlaveMirror lock(long position, long size, boolean shared) throws IOException { sendCommand(LOCK_CMD, position + "," + size + "," + shared); return this; } LockSlaveMirror unlock() throws IOException { sendCommand(UNLOCK_CMD); return this; } void shutdown() throws IOException { sc.close(); } } // Helper class to direct process output to the parent System.out static class IOHandler implements Runnable { private final InputStream in; IOHandler(InputStream in) { this.in = in; } static void handle(InputStream in) { IOHandler handler = new IOHandler(in); Thread thr = new Thread(handler); thr.setDaemon(true); thr.start(); } public void run() { try { byte b[] = new byte[100]; for (;;) { int n = in.read(b); if (n < 0) return; for (int i=0; i