001package org.apache.archiva.common.filelock; 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.Closeable; 023import java.io.IOException; 024import java.nio.channels.FileChannel; 025import java.nio.channels.FileLock; 026import java.nio.file.Path; 027import java.nio.file.StandardOpenOption; 028import java.util.HashMap; 029import java.util.Map; 030import java.util.concurrent.atomic.AtomicBoolean; 031import java.util.concurrent.atomic.AtomicInteger; 032 033/** 034 * @author Olivier Lamy 035 * @since 2.0.0 036 */ 037public class Lock implements Closeable 038{ 039 private Path file; 040 041 private AtomicBoolean write; 042 043 private final Map<Thread, AtomicInteger> fileClients = new HashMap<>(); 044 045 private FileLock fileLock; 046 047 private FileChannel fileChannel; 048 049 public Lock( Path file ) 050 { 051 this.file = file; 052 } 053 054 public Lock( Path file, boolean write ) 055 throws IOException 056 { 057 this.file = file; 058 this.write = new AtomicBoolean( write ); 059 fileChannel = write ? FileChannel.open(file, StandardOpenOption.WRITE, StandardOpenOption.READ) : FileChannel.open(file, StandardOpenOption.READ); 060 } 061 062 public Path getFile() 063 { 064 return file; 065 } 066 067 public AtomicBoolean isWrite() 068 { 069 return write; 070 } 071 072 public void setFile( Path file ) 073 { 074 this.file = file; 075 } 076 077 public void setWrite( boolean write ) 078 { 079 this.write.set( write ); 080 } 081 082 public boolean isShared() 083 { 084 return this.fileLock.isValid() && this.fileLock.isShared(); 085 } 086 087 public boolean isValid() 088 { 089 return this.fileLock!=null && this.fileLock.isValid(); 090 } 091 092 public Map<Thread, AtomicInteger> getFileClients() 093 { 094 return fileClients; 095 } 096 097 public void addFileClient( Thread thread ) 098 { 099 this.fileClients.put( thread, new AtomicInteger( 1 ) ); 100 } 101 102 public boolean removeFileClient( Thread thread ) 103 { 104 return this.fileClients.remove( thread ) != null; 105 } 106 107 public void close() 108 throws IOException 109 { 110 IOException ioException = null; 111 try 112 { 113 if (this.fileLock!=null) { 114 this.fileLock.release(); 115 } 116 } 117 catch ( IOException e ) 118 { 119 ioException = e; 120 } finally { 121 closeQuietly( fileChannel ); 122 fileClients.remove( Thread.currentThread() ); 123 } 124 125 if ( ioException != null ) 126 { 127 throw ioException; 128 } 129 130 } 131 132 protected void openLock( boolean write, boolean timeout ) 133 throws IOException 134 { 135 fileClients.put( Thread.currentThread(), new AtomicInteger( 1 ) ); 136 137 this.fileLock = timeout 138 ? fileChannel.tryLock( 0L, Long.MAX_VALUE, write ? false : true ) 139 : fileChannel.lock( 0L, Long.MAX_VALUE, write ? false : true ); 140 141 } 142 143 144 145 private void closeQuietly( Closeable closeable ) 146 { 147 try 148 { 149 closeable.close(); 150 } 151 catch ( IOException e ) 152 { 153 // ignore 154 } 155 } 156 157 158 @Override 159 public String toString() 160 { 161 final StringBuilder sb = new StringBuilder( "Lock{" ); 162 sb.append( "file=" ).append( file ); 163 sb.append( '}' ); 164 return sb.toString(); 165 } 166}