1 package org.eclipse.aether.named.providers;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.io.UncheckedIOException;
24 import java.nio.channels.FileChannel;
25 import java.nio.file.AccessDeniedException;
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.ConcurrentHashMap;
31 import java.util.concurrent.ConcurrentMap;
32
33 import javax.inject.Named;
34 import javax.inject.Singleton;
35
36 import org.eclipse.aether.named.support.FileLockNamedLock;
37 import org.eclipse.aether.named.support.NamedLockFactorySupport;
38 import org.eclipse.aether.named.support.NamedLockSupport;
39
40 import static org.eclipse.aether.named.support.Retry.retry;
41
42
43
44
45
46
47
48 @Singleton
49 @Named( FileLockNamedLockFactory.NAME )
50 public class FileLockNamedLockFactory
51 extends NamedLockFactorySupport
52 {
53 public static final String NAME = "file-lock";
54
55
56
57
58
59
60
61
62 private static final boolean DELETE_LOCK_FILES = Boolean.parseBoolean(
63 System.getProperty( "aether.named.file-lock.deleteLockFiles", Boolean.TRUE.toString() ) );
64
65
66
67
68
69
70
71
72 private static final int ATTEMPTS = Integer.parseInt(
73 System.getProperty( "aether.named.file-lock.attempts", "5" ) );
74
75
76
77
78
79 private static final long SLEEP_MILLIS = Long.parseLong(
80 System.getProperty( "aether.named.file-lock.sleepMillis", "50" ) );
81
82 private final ConcurrentMap<String, FileChannel> fileChannels;
83
84 public FileLockNamedLockFactory()
85 {
86 this.fileChannels = new ConcurrentHashMap<>();
87 }
88
89 @Override
90 protected NamedLockSupport createLock( final String name )
91 {
92 Path path = Paths.get( name );
93 FileChannel fileChannel = fileChannels.computeIfAbsent( name, k ->
94 {
95 try
96 {
97 Files.createDirectories( path.getParent() );
98 FileChannel channel = retry( ATTEMPTS, SLEEP_MILLIS, () ->
99 {
100 try
101 {
102 if ( DELETE_LOCK_FILES )
103 {
104 return FileChannel.open(
105 path,
106 StandardOpenOption.READ, StandardOpenOption.WRITE,
107 StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE
108 );
109 }
110 else
111 {
112 return FileChannel.open(
113 path,
114 StandardOpenOption.READ, StandardOpenOption.WRITE,
115 StandardOpenOption.CREATE
116 );
117 }
118 }
119 catch ( AccessDeniedException e )
120 {
121 return null;
122 }
123 }, null, null );
124
125 if ( channel == null )
126 {
127 throw new IllegalStateException( "Could not open file channel for '"
128 + name + "' after " + ATTEMPTS + " attempts; giving up" );
129 }
130 return channel;
131 }
132 catch ( InterruptedException e )
133 {
134 Thread.currentThread().interrupt();
135 throw new RuntimeException( "Interrupted while opening file channel for '"
136 + name + "'", e );
137 }
138 catch ( IOException e )
139 {
140 throw new UncheckedIOException( "Failed to open file channel for '"
141 + name + "'", e );
142 }
143 } );
144 return new FileLockNamedLock( name, fileChannel, this );
145 }
146
147 @Override
148 protected void destroyLock( final String name )
149 {
150 FileChannel fileChannel = fileChannels.remove( name );
151 if ( fileChannel == null )
152 {
153 throw new IllegalStateException( "File channel expected, but does not exist: " + name );
154 }
155
156 try
157 {
158 fileChannel.close();
159 }
160 catch ( IOException e )
161 {
162 throw new UncheckedIOException( "Failed to close file channel for '"
163 + name + "'", e );
164 }
165 }
166 }