jdk/test/java/nio/channels/AsynchronousFileChannel/Lock.java
changeset 2057 3acf8e5e2ca0
child 2594 3755ecdb395d
equal deleted inserted replaced
2056:115e09b7a004 2057:3acf8e5e2ca0
       
     1 /*
       
     2  * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    20  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    21  * have any questions.
       
    22  */
       
    23 
       
    24 
       
    25 /* @test
       
    26  * @bug 4607272
       
    27  * @summary Unit test for AsynchronousFileChannel#lock method
       
    28  */
       
    29 
       
    30 import java.net.*;
       
    31 import java.nio.ByteBuffer;
       
    32 import java.nio.charset.Charset;
       
    33 import java.nio.file.*;
       
    34 import static java.nio.file.StandardOpenOption.*;
       
    35 import java.nio.channels.*;
       
    36 import java.io.File;
       
    37 import java.io.IOException;
       
    38 import java.io.InputStream;
       
    39 import java.util.Random;
       
    40 import java.util.concurrent.*;
       
    41 
       
    42 public class Lock {
       
    43 
       
    44     static final Random rand = new Random();
       
    45 
       
    46     public static void main(String[] args) throws Exception {
       
    47         if (args.length > 0 && args[0].equals("-lockslave")) {
       
    48             int port = Integer.parseInt(args[1]);
       
    49             runLockSlave(port);
       
    50             System.exit(0);
       
    51         }
       
    52 
       
    53         LockSlaveMirror slave = startLockSlave();
       
    54         try {
       
    55 
       
    56              // create temporary file
       
    57             File blah = File.createTempFile("blah", null);
       
    58             blah.deleteOnExit();
       
    59 
       
    60             testLockProtocol(blah, slave);
       
    61             testAsyncClose(blah, slave);
       
    62 
       
    63         } finally {
       
    64             slave.shutdown();
       
    65         }
       
    66     }
       
    67 
       
    68     // test locking protocol
       
    69     static void testLockProtocol(File file, LockSlaveMirror slave)
       
    70         throws Exception
       
    71     {
       
    72         FileLock fl;
       
    73 
       
    74         // slave VM opens file and acquires exclusive lock
       
    75         slave.open(file.getPath()).lock();
       
    76 
       
    77         AsynchronousFileChannel ch = AsynchronousFileChannel
       
    78             .open(file.toPath(), READ, WRITE);
       
    79 
       
    80         // this VM tries to acquire lock
       
    81         // (lock should not be acquire until released by slave VM)
       
    82         Future<FileLock> result = ch.lock();
       
    83         try {
       
    84             result.get(2, TimeUnit.SECONDS);
       
    85             throw new RuntimeException("Timeout expected");
       
    86         } catch (TimeoutException x) {
       
    87         }
       
    88 
       
    89         // slave VM releases lock
       
    90         slave.unlock();
       
    91 
       
    92         // this VM should now acquire lock
       
    93         fl = result.get();
       
    94         fl.release();
       
    95 
       
    96         // slave VM acquires lock on range
       
    97         slave.lock(0, 10, false);
       
    98 
       
    99         // this VM acquires lock on non-overlapping range
       
   100         fl = ch.lock(10, 10, false, null, null).get();
       
   101         fl.release();
       
   102 
       
   103         // done
       
   104         ch.close();
       
   105         slave.close();
       
   106     }
       
   107 
       
   108     // test close of channel with outstanding lock operation
       
   109     static void testAsyncClose(File file, LockSlaveMirror slave) throws Exception {
       
   110         // slave VM opens file and acquires exclusive lock
       
   111         slave.open(file.getPath()).lock();
       
   112 
       
   113         for (int i=0; i<100; i++) {
       
   114             AsynchronousFileChannel ch = AsynchronousFileChannel
       
   115                 .open(file.toPath(), READ, WRITE);
       
   116 
       
   117             // try to lock file (should not complete because file is locked by slave)
       
   118             Future<FileLock> result = ch.lock();
       
   119             try {
       
   120                 result.get(rand.nextInt(100), TimeUnit.MILLISECONDS);
       
   121                 throw new RuntimeException("Timeout expected");
       
   122             } catch (TimeoutException x) {
       
   123             }
       
   124 
       
   125             // close channel with lock operation outstanding
       
   126             ch.close();
       
   127 
       
   128             // operation should complete with AsynchronousCloseException
       
   129             try {
       
   130                 result.get();
       
   131                 throw new RuntimeException("ExecutionException expected");
       
   132             } catch (ExecutionException x) {
       
   133                 if (!(x.getCause() instanceof AsynchronousCloseException)) {
       
   134                     x.getCause().printStackTrace();
       
   135                     throw new RuntimeException("AsynchronousCloseException expected");
       
   136                 }
       
   137             }
       
   138         }
       
   139 
       
   140         slave.close();
       
   141     }
       
   142 
       
   143     // starts a "lock slave" in another process, returning a mirror object to
       
   144     // control the slave
       
   145     static LockSlaveMirror startLockSlave() throws Exception {
       
   146         ServerSocketChannel ssc = ServerSocketChannel.open()
       
   147             .bind(new InetSocketAddress(0));
       
   148         int port = ((InetSocketAddress)(ssc.getLocalAddress())).getPort();
       
   149 
       
   150         String sep = FileSystems.getDefault().getSeparator();
       
   151 
       
   152         String command = System.getProperty("java.home") +
       
   153             sep + "bin" + sep + "java Lock -lockslave " + port;
       
   154         Process p = Runtime.getRuntime().exec(command);
       
   155         IOHandler.handle(p.getInputStream());
       
   156         IOHandler.handle(p.getErrorStream());
       
   157 
       
   158         // wait for slave to connect
       
   159         SocketChannel sc = ssc.accept();
       
   160         return new LockSlaveMirror(sc);
       
   161     }
       
   162 
       
   163     // commands that the slave understands
       
   164     static final String OPEN_CMD    = "open";
       
   165     static final String CLOSE_CMD   = "close";
       
   166     static final String LOCK_CMD    = "lock";
       
   167     static final String UNLOCK_CMD  = "unlock";
       
   168     static final char TERMINATOR    = ';';
       
   169 
       
   170     // provides a proxy to a "lock slave"
       
   171     static class LockSlaveMirror {
       
   172         private final SocketChannel sc;
       
   173 
       
   174         LockSlaveMirror(SocketChannel sc) {
       
   175             this.sc = sc;
       
   176         }
       
   177 
       
   178         private void sendCommand(String cmd, String... params)
       
   179             throws IOException
       
   180         {
       
   181             for (String s: params) {
       
   182                 cmd += " " + s;
       
   183             }
       
   184             cmd += TERMINATOR;
       
   185 
       
   186             ByteBuffer buf = Charset.defaultCharset().encode(cmd);
       
   187             while (buf.hasRemaining()) {
       
   188                 sc.write(buf);
       
   189             }
       
   190 
       
   191             // wait for ack
       
   192             buf = ByteBuffer.allocate(1);
       
   193             int n = sc.read(buf);
       
   194             if (n != 1)
       
   195                 throw new RuntimeException("Reply expected");
       
   196             if (buf.get(0) != TERMINATOR)
       
   197                 throw new RuntimeException("Terminated expected");
       
   198         }
       
   199 
       
   200         LockSlaveMirror open(String file) throws IOException {
       
   201             sendCommand(OPEN_CMD, file);
       
   202             return this;
       
   203         }
       
   204 
       
   205         void close() throws IOException {
       
   206             sendCommand(CLOSE_CMD);
       
   207         }
       
   208 
       
   209         LockSlaveMirror lock() throws IOException {
       
   210             sendCommand(LOCK_CMD);
       
   211             return this;
       
   212         }
       
   213 
       
   214 
       
   215         LockSlaveMirror lock(long position, long size, boolean shared)
       
   216             throws IOException
       
   217         {
       
   218             sendCommand(LOCK_CMD, position + "," + size + "," + shared);
       
   219             return this;
       
   220         }
       
   221 
       
   222         LockSlaveMirror unlock() throws IOException {
       
   223             sendCommand(UNLOCK_CMD);
       
   224             return this;
       
   225         }
       
   226 
       
   227         void shutdown() throws IOException {
       
   228             sc.close();
       
   229         }
       
   230     }
       
   231 
       
   232     // Helper class to direct process output to the parent System.out
       
   233     static class IOHandler implements Runnable {
       
   234         private final InputStream in;
       
   235 
       
   236         IOHandler(InputStream in) {
       
   237             this.in = in;
       
   238         }
       
   239 
       
   240         static void handle(InputStream in) {
       
   241             IOHandler handler = new IOHandler(in);
       
   242             Thread thr = new Thread(handler);
       
   243             thr.setDaemon(true);
       
   244             thr.start();
       
   245         }
       
   246 
       
   247         public void run() {
       
   248             try {
       
   249                 byte b[] = new byte[100];
       
   250                 for (;;) {
       
   251                     int n = in.read(b);
       
   252                     if (n < 0) return;
       
   253                     for (int i=0; i<n; i++) {
       
   254                         System.out.print((char)b[i]);
       
   255                     }
       
   256                 }
       
   257             } catch (IOException ioe) { }
       
   258         }
       
   259     }
       
   260 
       
   261     // slave process that responds to simple commands a socket connection
       
   262     static void runLockSlave(int port) throws Exception {
       
   263 
       
   264         // establish connection to parent
       
   265         SocketChannel sc = SocketChannel.open(new InetSocketAddress(port));
       
   266         ByteBuffer buf = ByteBuffer.allocateDirect(1024);
       
   267 
       
   268         FileChannel fc = null;
       
   269         FileLock fl = null;
       
   270         try {
       
   271             for (;;) {
       
   272 
       
   273                 // read command (ends with ";")
       
   274                 buf.clear();
       
   275                 int n, last = 0;
       
   276                 do {
       
   277                     n = sc.read(buf);
       
   278                     if (n < 0)
       
   279                         return;
       
   280                     if (n == 0)
       
   281                         throw new AssertionError();
       
   282                     last += n;
       
   283                 } while (buf.get(last-1) != TERMINATOR);
       
   284 
       
   285                 // decode into command and optional parameter
       
   286                 buf.flip();
       
   287                 String s = Charset.defaultCharset().decode(buf).toString();
       
   288                 int sp = s.indexOf(" ");
       
   289                 String cmd = (sp < 0) ? s.substring(0, s.length()-1) :
       
   290                     s.substring(0, sp);
       
   291                 String param = (sp < 0) ? "" : s.substring(sp+1, s.length()-1);
       
   292 
       
   293                 // execute
       
   294                 if (cmd.equals(OPEN_CMD)) {
       
   295                     if (fc != null)
       
   296                         throw new RuntimeException("File already open");
       
   297                     fc = FileChannel.open(Paths.get(param),READ, WRITE);
       
   298                 }
       
   299                 if (cmd.equals(CLOSE_CMD)) {
       
   300                     if (fc == null)
       
   301                         throw new RuntimeException("No file open");
       
   302                     fc.close();
       
   303                     fc = null;
       
   304                     fl = null;
       
   305                 }
       
   306                 if (cmd.equals(LOCK_CMD)) {
       
   307                     if (fl != null)
       
   308                         throw new RuntimeException("Already holding lock");
       
   309 
       
   310                     if (param.length() == 0) {
       
   311                         fl = fc.lock();
       
   312                     } else {
       
   313                         String[] values = param.split(",");
       
   314                         if (values.length != 3)
       
   315                             throw new RuntimeException("Lock parameter invalid");
       
   316                         long position = Long.parseLong(values[0]);
       
   317                         long size = Long.parseLong(values[1]);
       
   318                         boolean shared = Boolean.parseBoolean(values[2]);
       
   319                         fl = fc.lock(position, size, shared);
       
   320                     }
       
   321                 }
       
   322 
       
   323                 if (cmd.equals(UNLOCK_CMD)) {
       
   324                     if (fl == null)
       
   325                         throw new RuntimeException("Not holding lock");
       
   326                     fl.release();
       
   327                     fl = null;
       
   328                 }
       
   329 
       
   330                 // send reply
       
   331                 byte[] reply = { TERMINATOR };
       
   332                 n = sc.write(ByteBuffer.wrap(reply));
       
   333             }
       
   334 
       
   335         } finally {
       
   336             sc.close();
       
   337             if (fc != null) fc.close();
       
   338         }
       
   339     }
       
   340 }