View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package demo; // CHECKSTYLE_OFF: RegexpHeader
20  
21  import java.io.IOException;
22  import java.lang.management.ManagementFactory;
23  import java.nio.channels.FileChannel;
24  import java.nio.channels.FileLock;
25  import java.nio.file.FileStore;
26  import java.nio.file.Files;
27  import java.nio.file.Path;
28  import java.nio.file.Paths;
29  import java.nio.file.StandardOpenOption;
30  import java.util.concurrent.CountDownLatch;
31  import java.util.concurrent.atomic.AtomicInteger;
32  
33  /**
34   * A simple tool to check file locking on your OS/FS/Java combo. To use this tool, just copy it to basedir on
35   * the volume you plan to use as local repository and compile and run it:
36   * <ul>
37   *   <li><pre>javac demo.TestNioLock.java</pre></li>
38   *   <li><pre>java demo.TestNioLock test someFile 1000</pre></li>
39   * </ul>
40   */
41  public class TestNioLock {
42      private static final int EC_WON = 10;
43  
44      private static final int EC_LOST = 20;
45  
46      private static final int EC_FAILED = 30;
47  
48      private static final int EC_ERROR = 100;
49  
50      public static void main(String[] args) throws IOException, InterruptedException {
51          if (args.length != 3) {
52              System.out.println("demo.TestNioLock <test|perform> <file> <sleepMs>");
53              System.exit(EC_ERROR);
54          }
55  
56          String mode = args[0];
57          Path path = Paths.get(args[1]).toAbsolutePath();
58          Path latchFile = path.getParent().resolve(TestNioLock.class.getName() + ".latchFile");
59  
60          if (Files.isDirectory(path)) {
61              System.out.println("The <file> cannot be directory.");
62              System.exit(EC_ERROR);
63          }
64          if (!Files.isRegularFile(latchFile)) {
65              Files.createFile(latchFile);
66          }
67  
68          if ("test".equals(mode)) {
69              System.out.println("Testing file locking on");
70              System.out.println(
71                      "  Java " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor"));
72              System.out.println("  OS " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " "
73                      + System.getProperty("os.arch"));
74  
75              FileStore fileStore = Files.getFileStore(path.getParent());
76              System.out.println("  FS " + fileStore.name() + " " + fileStore.type());
77              System.out.println();
78  
79              AtomicInteger oneResult = new AtomicInteger(-1);
80              AtomicInteger twoResult = new AtomicInteger(-1);
81              CountDownLatch latch = new CountDownLatch(2);
82              String javaCmd = System.getProperty("java.home") + "/bin/java";
83  
84              try (FileChannel latchChannel =
85                      FileChannel.open(latchFile, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
86                  try (FileLock latchLock = latchChannel.lock(0L, 1L, false)) {
87                      new Thread(() -> {
88                                  try {
89                                      oneResult.set(new ProcessBuilder(
90                                                      javaCmd, TestNioLock.class.getName(), "perform", args[1], args[2])
91                                              .inheritIO()
92                                              .start()
93                                              .waitFor());
94                                  } catch (Exception e) {
95                                      oneResult.set(EC_FAILED);
96                                  } finally {
97                                      latch.countDown();
98                                  }
99                              })
100                             .start();
101                     new Thread(() -> {
102                                 try {
103                                     twoResult.set(new ProcessBuilder(
104                                                     javaCmd, TestNioLock.class.getName(), "perform", args[1], args[2])
105                                             .inheritIO()
106                                             .start()
107                                             .waitFor());
108                                 } catch (Exception e) {
109                                     twoResult.set(EC_FAILED);
110                                 } finally {
111                                     latch.countDown();
112                                 }
113                             })
114                             .start();
115 
116                     Thread.sleep(1000); // give them a bit of time (to both block)
117                     latchLock.release();
118                     latch.await();
119                 }
120             }
121 
122             int oneExit = oneResult.get();
123             int twoExit = twoResult.get();
124             if ((oneExit == EC_WON && twoExit == EC_LOST) || (oneExit == EC_LOST && twoExit == EC_WON)) {
125                 System.out.println("OK");
126                 System.exit(0);
127             } else {
128                 System.out.println("FAILED: one=" + oneExit + " two=" + twoExit);
129                 System.exit(EC_FAILED);
130             }
131         } else if ("perform".equals(mode)) {
132             String processName = ManagementFactory.getRuntimeMXBean().getName();
133             System.out.println(processName + " > started");
134             boolean won = false;
135             long sleepMs = Long.parseLong(args[2]);
136             try (FileChannel latchChannel = FileChannel.open(latchFile, StandardOpenOption.READ)) {
137                 try (FileLock latchLock = latchChannel.lock(0L, 1L, true)) {
138                     System.out.println(processName + " > latchLock acquired");
139                     try (FileChannel channel = FileChannel.open(
140                             path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
141                         try (FileLock lock = channel.tryLock(0L, 1L, false)) {
142                             if (lock != null && lock.isValid() && !lock.isShared()) {
143                                 System.out.println(processName + " > WON");
144                                 won = true;
145                                 Thread.sleep(sleepMs);
146                             } else {
147                                 System.out.println(processName + " > LOST");
148                             }
149                         }
150                     }
151                 }
152             }
153             System.out.println(processName + " > ended");
154             if (won) {
155                 System.exit(EC_WON);
156             } else {
157                 System.exit(EC_LOST);
158             }
159         } else {
160             System.err.println("Unknown mode: " + mode);
161         }
162         System.exit(EC_ERROR);
163     }
164 }