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