001// CHECKSTYLE_OFF: RegexpHeader 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.IOException; 023import java.lang.management.ManagementFactory; 024import java.nio.channels.FileChannel; 025import java.nio.channels.FileLock; 026import java.nio.file.FileStore; 027import java.nio.file.Files; 028import java.nio.file.Path; 029import java.nio.file.Paths; 030import java.nio.file.StandardOpenOption; 031import java.util.concurrent.CountDownLatch; 032import java.util.concurrent.atomic.AtomicInteger; 033 034/** 035 * A simple tool to check file locking on your OS/FS/Java combo. To use this tool, just copy it to basedir on 036 * the volume you plan to use as local repository and compile and run it: 037 * <ul> 038 * <li><pre>javac TestNioLock.java</pre></li> 039 * <li><pre>java TestNioLock test someFile 1000</pre></li> 040 * </ul> 041 */ 042public class TestNioLock 043{ 044 private static final int EC_WON = 10; 045 046 private static final int EC_LOST = 20; 047 048 private static final int EC_FAILED = 30; 049 050 private static final int EC_ERROR = 100; 051 052 public static void main( String[] args ) throws IOException, InterruptedException 053 { 054 if ( args.length != 3 ) 055 { 056 System.out.println( "TestNioLock <test|perform> <file> <sleepMs>" ); 057 System.exit( EC_ERROR ); 058 } 059 060 String mode = args[0]; 061 Path path = Paths.get( args[1] ).toAbsolutePath(); 062 Path latchFile = path.getParent().resolve( TestNioLock.class.getName() + ".latchFile" ); 063 064 if ( Files.isDirectory( path ) ) 065 { 066 System.out.println( "The <file> cannot be directory." ); 067 System.exit( EC_ERROR ); 068 } 069 if ( !Files.isRegularFile( latchFile ) ) 070 { 071 Files.createFile( latchFile ); 072 } 073 074 if ( "test".equals( mode ) ) 075 { 076 System.out.println( "Testing file locking on" ); 077 System.out.println( 078 " Java " + System.getProperty( "java.version" ) + ", " + System.getProperty( "java.vendor" ) ); 079 System.out.println( 080 " OS " + System.getProperty( "os.name" ) + " " + System.getProperty( "os.version" ) + " " 081 + System.getProperty( "os.arch" ) ); 082 083 FileStore fileStore = Files.getFileStore( path.getParent() ); 084 System.out.println( " FS " + fileStore.name() + " " + fileStore.type() ); 085 System.out.println(); 086 087 AtomicInteger oneResult = new AtomicInteger( -1 ); 088 AtomicInteger twoResult = new AtomicInteger( -1 ); 089 CountDownLatch latch = new CountDownLatch( 2 ); 090 String javaCmd = System.getProperty( "java.home" ) + "/bin/java"; 091 092 try ( FileChannel latchChannel = FileChannel.open( latchFile, StandardOpenOption.READ, 093 StandardOpenOption.WRITE ) ) 094 { 095 try ( FileLock latchLock = latchChannel.lock( 0L, 1L, false ) ) 096 { 097 new Thread( () -> 098 { 099 try 100 { 101 oneResult.set( new ProcessBuilder( javaCmd, TestNioLock.class.getName(), 102 "perform", args[1], args[2] ).inheritIO().start().waitFor() ); 103 } 104 catch ( Exception e ) 105 { 106 oneResult.set( EC_FAILED ); 107 } 108 finally 109 { 110 latch.countDown(); 111 } 112 } ).start(); 113 new Thread( () -> 114 { 115 try 116 { 117 twoResult.set( new ProcessBuilder( javaCmd, TestNioLock.class.getName(), 118 "perform", args[1], args[2] ).inheritIO().start().waitFor() ); 119 } 120 catch ( Exception e ) 121 { 122 twoResult.set( EC_FAILED ); 123 } 124 finally 125 { 126 latch.countDown(); 127 } 128 } ).start(); 129 130 Thread.sleep( 1000 ); // give them a bit of time (to both block) 131 latchLock.release(); 132 latch.await(); 133 } 134 } 135 136 137 int oneExit = oneResult.get(); 138 int twoExit = twoResult.get(); 139 if ( ( oneExit == EC_WON && twoExit == EC_LOST ) || ( oneExit == EC_LOST && twoExit == EC_WON ) ) 140 { 141 System.out.println( "OK" ); 142 System.exit( 0 ); 143 } 144 else 145 { 146 System.out.println( "FAILED: one=" + oneExit + " two=" + twoExit ); 147 System.exit( EC_FAILED ); 148 } 149 } 150 else if ( "perform".equals( mode ) ) 151 { 152 String processName = ManagementFactory.getRuntimeMXBean().getName(); 153 System.out.println( processName + " > started" ); 154 boolean won = false; 155 long sleepMs = Long.parseLong( args[2] ); 156 try ( FileChannel latchChannel = FileChannel.open( latchFile, StandardOpenOption.READ ) ) 157 { 158 try ( FileLock latchLock = latchChannel.lock( 0L, 1L, true ) ) 159 { 160 System.out.println( processName + " > latchLock acquired" ); 161 try ( FileChannel channel = FileChannel.open( path, StandardOpenOption.READ, 162 StandardOpenOption.WRITE, StandardOpenOption.CREATE ) ) 163 { 164 try ( FileLock lock = channel.tryLock( 0L, 1L, false ) ) 165 { 166 if ( lock != null && lock.isValid() && !lock.isShared() ) 167 { 168 System.out.println( processName + " > WON" ); 169 won = true; 170 Thread.sleep( sleepMs ); 171 } 172 else 173 { 174 System.out.println( processName + " > LOST" ); 175 } 176 } 177 } 178 } 179 } 180 System.out.println( processName + " > ended" ); 181 if ( won ) 182 { 183 System.exit( EC_WON ); 184 } 185 else 186 { 187 System.exit( EC_LOST ); 188 } 189 } 190 else 191 { 192 System.err.println( "Unknown mode: " + mode ); 193 } 194 System.exit( EC_ERROR ); 195 } 196}