1 package org.eclipse.aether.internal.impl;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 import java.io.ByteArrayInputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.Closeable;
28 import java.io.File;
29 import java.io.FileInputStream;
30 import java.io.IOException;
31 import java.io.RandomAccessFile;
32 import java.nio.channels.FileChannel;
33 import java.nio.channels.FileLock;
34 import java.nio.channels.OverlappingFileLockException;
35 import java.util.Map;
36 import java.util.Properties;
37
38
39
40
41 class TrackingFileManager
42 {
43
44 private static final Logger LOGGER = LoggerFactory.getLogger( TrackingFileManager.class );
45
46 public Properties read( File file )
47 {
48 synchronized ( getLock( file ) )
49 {
50 FileLock lock = null;
51 FileInputStream stream = null;
52 try
53 {
54 if ( !file.exists() )
55 {
56 return null;
57 }
58
59 stream = new FileInputStream( file );
60
61 lock = lock( stream.getChannel(), Math.max( 1, file.length() ), true );
62
63 Properties props = new Properties();
64 props.load( stream );
65
66 return props;
67 }
68 catch ( IOException e )
69 {
70 LOGGER.warn( "Failed to read tracking file {}", file, e );
71 }
72 finally
73 {
74 release( lock, file );
75 close( stream, file );
76 }
77 }
78
79 return null;
80 }
81
82 public Properties update( File file, Map<String, String> updates )
83 {
84 Properties props = new Properties();
85
86 synchronized ( getLock( file ) )
87 {
88 File directory = file.getParentFile();
89 if ( !directory.mkdirs() && !directory.exists() )
90 {
91 LOGGER.warn( "Failed to create parent directories for tracking file {}", file );
92 return props;
93 }
94
95 RandomAccessFile raf = null;
96 FileLock lock = null;
97 try
98 {
99 raf = new RandomAccessFile( file, "rw" );
100 lock = lock( raf.getChannel(), Math.max( 1, raf.length() ), false );
101
102 if ( file.canRead() )
103 {
104 byte[] buffer = new byte[(int) raf.length()];
105
106 raf.readFully( buffer );
107
108 ByteArrayInputStream stream = new ByteArrayInputStream( buffer );
109
110 props.load( stream );
111 }
112
113 for ( Map.Entry<String, String> update : updates.entrySet() )
114 {
115 if ( update.getValue() == null )
116 {
117 props.remove( update.getKey() );
118 }
119 else
120 {
121 props.setProperty( update.getKey(), update.getValue() );
122 }
123 }
124
125 ByteArrayOutputStream stream = new ByteArrayOutputStream( 1024 * 2 );
126
127 LOGGER.debug( "Writing tracking file {}", file );
128 props.store( stream, "NOTE: This is a Maven Resolver internal implementation file"
129 + ", its format can be changed without prior notice." );
130
131 raf.seek( 0 );
132 raf.write( stream.toByteArray() );
133 raf.setLength( raf.getFilePointer() );
134 }
135 catch ( IOException e )
136 {
137 LOGGER.warn( "Failed to write tracking file {}", file, e );
138 }
139 finally
140 {
141 release( lock, file );
142 close( raf, file );
143 }
144 }
145
146 return props;
147 }
148
149 private void release( FileLock lock, File file )
150 {
151 if ( lock != null )
152 {
153 try
154 {
155 lock.release();
156 }
157 catch ( IOException e )
158 {
159 LOGGER.warn( "Error releasing lock for tracking file {}", file, e );
160 }
161 }
162 }
163
164 private void close( Closeable closeable, File file )
165 {
166 if ( closeable != null )
167 {
168 try
169 {
170 closeable.close();
171 }
172 catch ( IOException e )
173 {
174 LOGGER.warn( "Error closing tracking file {}", file, e );
175 }
176 }
177 }
178
179 private Object getLock( File file )
180 {
181
182
183
184
185
186 try
187 {
188 return file.getCanonicalPath().intern();
189 }
190 catch ( IOException e )
191 {
192 LOGGER.warn( "Failed to canonicalize path {}: {}", file, e.getMessage() );
193 return file.getAbsolutePath().intern();
194 }
195 }
196
197 private FileLock lock( FileChannel channel, long size, boolean shared )
198 throws IOException
199 {
200 FileLock lock = null;
201
202 for ( int attempts = 8; attempts >= 0; attempts-- )
203 {
204 try
205 {
206 lock = channel.lock( 0, size, shared );
207 break;
208 }
209 catch ( OverlappingFileLockException e )
210 {
211 if ( attempts <= 0 )
212 {
213 throw (IOException) new IOException().initCause( e );
214 }
215 try
216 {
217 Thread.sleep( 50L );
218 }
219 catch ( InterruptedException e1 )
220 {
221 Thread.currentThread().interrupt();
222 }
223 }
224 }
225
226 if ( lock == null )
227 {
228 throw new IOException( "Could not lock file" );
229 }
230
231 return lock;
232 }
233
234 }