View Javadoc
1   // CHECKSTYLE_OFF: RegexpHeader
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.lang.management.ManagementFactory;
24  import java.nio.channels.FileChannel;
25  import java.nio.channels.FileLock;
26  import java.nio.file.FileStore;
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.nio.file.Paths;
30  import java.nio.file.StandardOpenOption;
31  import java.util.concurrent.CountDownLatch;
32  import java.util.concurrent.atomic.AtomicInteger;
33  
34  /**
35   * A simple tool to check file locking on your OS/FS/Java combo. To use this tool, just copy it to basedir on
36   * the volume you plan to use as local repository and compile and run it:
37   * <ul>
38   *   <li><pre>javac TestNioLock.java</pre></li>
39   *   <li><pre>java TestNioLock test someFile 1000</pre></li>
40   * </ul>
41   */
42  public class TestNioLock
43  {
44      private static final int EC_WON = 10;
45  
46      private static final int EC_LOST = 20;
47  
48      private static final int EC_FAILED = 30;
49  
50      private static final int EC_ERROR = 100;
51  
52      public static void main( String[] args ) throws IOException, InterruptedException
53      {
54          if ( args.length != 3 )
55          {
56              System.out.println( "TestNioLock <test|perform> <file> <sleepMs>" );
57              System.exit( EC_ERROR );
58          }
59  
60          String mode = args[0];
61          Path path = Paths.get( args[1] ).toAbsolutePath();
62          Path latchFile = path.getParent().resolve( TestNioLock.class.getName() + ".latchFile" );
63  
64          if ( Files.isDirectory( path ) )
65          {
66              System.out.println( "The <file> cannot be directory." );
67              System.exit( EC_ERROR );
68          }
69          if ( !Files.isRegularFile( latchFile ) )
70          {
71              Files.createFile( latchFile );
72          }
73  
74          if ( "test".equals( mode ) )
75          {
76              System.out.println( "Testing file locking on" );
77              System.out.println(
78                      "  Java " + System.getProperty( "java.version" ) + ", " + System.getProperty( "java.vendor" ) );
79              System.out.println(
80                      "  OS " + System.getProperty( "os.name" ) + " " + System.getProperty( "os.version" ) + " "
81                              + System.getProperty( "os.arch" ) );
82  
83              FileStore fileStore = Files.getFileStore( path.getParent() );
84              System.out.println( "  FS " + fileStore.name() + " " + fileStore.type() );
85              System.out.println();
86  
87              AtomicInteger oneResult = new AtomicInteger( -1 );
88              AtomicInteger twoResult = new AtomicInteger( -1 );
89              CountDownLatch latch = new CountDownLatch( 2 );
90              String javaCmd = System.getProperty( "java.home" ) + "/bin/java";
91  
92              try ( FileChannel latchChannel = FileChannel.open( latchFile, StandardOpenOption.READ,
93                      StandardOpenOption.WRITE ) )
94              {
95                  try ( FileLock latchLock = latchChannel.lock( 0L, 1L, false ) )
96                  {
97                      new Thread( () ->
98                      {
99                          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 }