001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package demo; // CHECKSTYLE_OFF: RegexpHeader 020 021import java.io.IOException; 022import java.lang.management.ManagementFactory; 023import java.nio.channels.FileChannel; 024import java.nio.channels.FileLock; 025import java.nio.file.FileStore; 026import java.nio.file.Files; 027import java.nio.file.Path; 028import java.nio.file.Paths; 029import java.nio.file.StandardOpenOption; 030import java.util.concurrent.CountDownLatch; 031import java.util.concurrent.atomic.AtomicInteger; 032 033/** 034 * A simple tool to check file locking on your OS/FS/Java combo. To use this tool, just copy it to basedir on 035 * the volume you plan to use as local repository and compile and run it: 036 * <ul> 037 * <li><pre>javac demo.TestNioLock.java</pre></li> 038 * <li><pre>java demo.TestNioLock test someFile 1000</pre></li> 039 * </ul> 040 */ 041public class TestNioLock { 042 private static final int EC_WON = 10; 043 044 private static final int EC_LOST = 20; 045 046 private static final int EC_FAILED = 30; 047 048 private static final int EC_ERROR = 100; 049 050 public static void main(String[] args) throws IOException, InterruptedException { 051 if (args.length != 3) { 052 System.out.println("demo.TestNioLock <test|perform> <file> <sleepMs>"); 053 System.exit(EC_ERROR); 054 } 055 056 String mode = args[0]; 057 Path path = Paths.get(args[1]).toAbsolutePath(); 058 Path latchFile = path.getParent().resolve(TestNioLock.class.getName() + ".latchFile"); 059 060 if (Files.isDirectory(path)) { 061 System.out.println("The <file> cannot be directory."); 062 System.exit(EC_ERROR); 063 } 064 if (!Files.isRegularFile(latchFile)) { 065 Files.createFile(latchFile); 066 } 067 068 if ("test".equals(mode)) { 069 System.out.println("Testing file locking on"); 070 System.out.println( 071 " Java " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor")); 072 System.out.println(" OS " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " 073 + System.getProperty("os.arch")); 074 075 FileStore fileStore = Files.getFileStore(path.getParent()); 076 System.out.println(" FS " + fileStore.name() + " " + fileStore.type()); 077 System.out.println(); 078 079 AtomicInteger oneResult = new AtomicInteger(-1); 080 AtomicInteger twoResult = new AtomicInteger(-1); 081 CountDownLatch latch = new CountDownLatch(2); 082 String javaCmd = System.getProperty("java.home") + "/bin/java"; 083 084 try (FileChannel latchChannel = 085 FileChannel.open(latchFile, StandardOpenOption.READ, StandardOpenOption.WRITE)) { 086 try (FileLock latchLock = latchChannel.lock(0L, 1L, false)) { 087 new Thread(() -> { 088 try { 089 oneResult.set(new ProcessBuilder( 090 javaCmd, TestNioLock.class.getName(), "perform", args[1], args[2]) 091 .inheritIO() 092 .start() 093 .waitFor()); 094 } catch (Exception e) { 095 oneResult.set(EC_FAILED); 096 } finally { 097 latch.countDown(); 098 } 099 }) 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}