]> source.dussan.org Git - archiva.git/blob
92044fa864407f0c1ffa88d36af052fb984db655
[archiva.git] /
1 package org.apache.archiva.repository.content;
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 org.apache.archiva.common.filelock.FileLockException;
23 import org.apache.archiva.common.filelock.FileLockManager;
24 import org.apache.archiva.common.filelock.FileLockTimeoutException;
25 import org.apache.archiva.common.filelock.Lock;
26 import org.apache.commons.io.FileUtils;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import java.io.FileNotFoundException;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.OutputStream;
34 import java.nio.channels.FileChannel;
35 import java.nio.channels.ReadableByteChannel;
36 import java.nio.channels.WritableByteChannel;
37 import java.nio.file.CopyOption;
38 import java.nio.file.Files;
39 import java.nio.file.Path;
40 import java.nio.file.Paths;
41 import java.nio.file.StandardCopyOption;
42 import java.nio.file.StandardOpenOption;
43 import java.util.function.Consumer;
44
45 /**
46  * Implementation of <code>{@link RepositoryStorage}</code> where data is stored in the filesystem.
47  *
48  * All files are relative to a given base path. Path values are separated by '/', '..' is allowed to navigate
49  * to a parent directory, but navigation out of the base path will lead to a exception.
50  */
51 public class FilesystemStorage implements RepositoryStorage {
52
53     private static final Logger log = LoggerFactory.getLogger(FilesystemStorage.class);
54
55     private final Path basePath;
56     private final FileLockManager fileLockManager;
57
58     public FilesystemStorage(Path basePath, FileLockManager fileLockManager) throws IOException {
59         if (!Files.exists(basePath)) {
60             Files.createDirectories(basePath);
61         }
62         this.basePath = basePath.normalize().toRealPath();
63         this.fileLockManager = fileLockManager;
64     }
65
66     private Path normalize(final String path) {
67         String nPath = path;
68         while (nPath.startsWith("/")) {
69             nPath = nPath.substring(1);
70         }
71         return Paths.get(nPath);
72     }
73
74     private Path getAssetPath(String path) throws IOException {
75         Path assetPath = basePath.resolve(normalize(path)).normalize();
76         if (!assetPath.startsWith(basePath))
77         {
78             throw new IOException("Path navigation out of allowed scope: "+path);
79         }
80         return assetPath;
81     }
82
83     @Override
84     public void consumeData( StorageAsset asset, Consumer<InputStream> consumerFunction, boolean readLock ) throws IOException
85     {
86         final Path path = asset.getFilePath();
87         try {
88             if (readLock) {
89                 consumeDataLocked( path, consumerFunction );
90             } else
91             {
92                 try ( InputStream is = Files.newInputStream( path ) )
93                 {
94                     consumerFunction.accept( is );
95                 }
96                 catch ( IOException e )
97                 {
98                     log.error("Could not read the input stream from file {}", path);
99                     throw e;
100                 }
101             }
102         } catch (RuntimeException e)
103         {
104             log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
105             throw new IOException( e );
106         }
107
108     }
109
110     @Override
111     public void consumeDataFromChannel( StorageAsset asset, Consumer<ReadableByteChannel> consumerFunction, boolean readLock ) throws IOException
112     {
113         final Path path = asset.getFilePath();
114         try {
115             if (readLock) {
116                 consumeDataFromChannelLocked( path, consumerFunction );
117             } else
118             {
119                 try ( FileChannel is = FileChannel.open( path, StandardOpenOption.READ ) )
120                 {
121                     consumerFunction.accept( is );
122                 }
123                 catch ( IOException e )
124                 {
125                     log.error("Could not read the input stream from file {}", path);
126                     throw e;
127                 }
128             }
129         } catch (RuntimeException e)
130         {
131             log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
132             throw new IOException( e );
133         }
134     }
135
136     @Override
137     public void writeData( StorageAsset asset, Consumer<OutputStream> consumerFunction, boolean writeLock ) throws IOException
138     {
139         final Path path = asset.getFilePath();
140         try {
141             if (writeLock) {
142                 writeDataLocked( path, consumerFunction );
143             } else
144             {
145                 try ( OutputStream is = Files.newOutputStream( path ) )
146                 {
147                     consumerFunction.accept( is );
148                 }
149                 catch ( IOException e )
150                 {
151                     log.error("Could not write the output stream to file {}", path);
152                     throw e;
153                 }
154             }
155         } catch (RuntimeException e)
156         {
157             log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
158             throw new IOException( e );
159         }
160
161     }
162
163     @Override
164     public void writeDataToChannel( StorageAsset asset, Consumer<WritableByteChannel> consumerFunction, boolean writeLock ) throws IOException
165     {
166         final Path path = asset.getFilePath();
167         try {
168             if (writeLock) {
169                 writeDataToChannelLocked( path, consumerFunction );
170             } else
171             {
172                 try ( FileChannel os = FileChannel.open( path, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE ))
173                 {
174                     consumerFunction.accept( os );
175                 }
176                 catch ( IOException e )
177                 {
178                     log.error("Could not write the data to file {}", path);
179                     throw e;
180                 }
181             }
182         } catch (RuntimeException e)
183         {
184             log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
185             throw new IOException( e );
186         }
187     }
188
189     private void consumeDataLocked( Path file, Consumer<InputStream> consumerFunction) throws IOException
190     {
191
192         final Lock lock;
193         try
194         {
195             lock = fileLockManager.readFileLock( file );
196             try ( InputStream is = Files.newInputStream( lock.getFile()))
197             {
198                 consumerFunction.accept( is );
199             }
200             catch ( IOException e )
201             {
202                 log.error("Could not read the input stream from file {}", file);
203                 throw e;
204             } finally
205             {
206                 fileLockManager.release( lock );
207             }
208         }
209         catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
210         {
211             log.error("Locking error on file {}", file);
212             throw new IOException(e);
213         }
214     }
215
216     private void consumeDataFromChannelLocked( Path file, Consumer<ReadableByteChannel> consumerFunction) throws IOException
217     {
218
219         final Lock lock;
220         try
221         {
222             lock = fileLockManager.readFileLock( file );
223             try ( FileChannel is = FileChannel.open( lock.getFile( ), StandardOpenOption.READ ))
224             {
225                 consumerFunction.accept( is );
226             }
227             catch ( IOException e )
228             {
229                 log.error("Could not read the input stream from file {}", file);
230                 throw e;
231             } finally
232             {
233                 fileLockManager.release( lock );
234             }
235         }
236         catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
237         {
238             log.error("Locking error on file {}", file);
239             throw new IOException(e);
240         }
241     }
242
243
244     private void writeDataLocked( Path file, Consumer<OutputStream> consumerFunction) throws IOException
245     {
246
247         final Lock lock;
248         try
249         {
250             lock = fileLockManager.writeFileLock( file );
251             try ( OutputStream is = Files.newOutputStream( lock.getFile()))
252             {
253                 consumerFunction.accept( is );
254             }
255             catch ( IOException e )
256             {
257                 log.error("Could not write the output stream to file {}", file);
258                 throw e;
259             } finally
260             {
261                 fileLockManager.release( lock );
262             }
263         }
264         catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
265         {
266             log.error("Locking error on file {}", file);
267             throw new IOException(e);
268         }
269     }
270
271     private void writeDataToChannelLocked( Path file, Consumer<WritableByteChannel> consumerFunction) throws IOException
272     {
273
274         final Lock lock;
275         try
276         {
277             lock = fileLockManager.writeFileLock( file );
278             try ( FileChannel is = FileChannel.open( lock.getFile( ), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE ))
279             {
280                 consumerFunction.accept( is );
281             }
282             catch ( IOException e )
283             {
284                 log.error("Could not write to file {}", file);
285                 throw e;
286             } finally
287             {
288                 fileLockManager.release( lock );
289             }
290         }
291         catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
292         {
293             log.error("Locking error on file {}", file);
294             throw new IOException(e);
295         }
296     }
297
298     @Override
299     public StorageAsset getAsset( String path )
300     {
301         try {
302             return new FilesystemAsset(this, path, getAssetPath(path));
303         } catch (IOException e) {
304             throw new IllegalArgumentException("Path navigates outside of base directory "+path);
305         }
306     }
307
308     @Override
309     public StorageAsset addAsset( String path, boolean container )
310     {
311         try {
312             return new FilesystemAsset(this, path, getAssetPath(path), basePath, container);
313         } catch (IOException e) {
314             throw new IllegalArgumentException("Path navigates outside of base directory "+path);
315         }
316     }
317
318     @Override
319     public void removeAsset( StorageAsset asset ) throws IOException
320     {
321         Files.delete(asset.getFilePath());
322     }
323
324     @Override
325     public StorageAsset moveAsset( StorageAsset origin, String destination, CopyOption... copyOptions ) throws IOException
326     {
327         boolean container = origin.isContainer();
328         FilesystemAsset newAsset = new FilesystemAsset(this, destination, getAssetPath(destination), basePath, container );
329         moveAsset( origin, newAsset, copyOptions );
330         return newAsset;
331     }
332
333     @Override
334     public void moveAsset( StorageAsset origin, StorageAsset destination, CopyOption... copyOptions ) throws IOException
335     {
336         Files.move(origin.getFilePath(), destination.getFilePath(), copyOptions);
337     }
338
339     @Override
340     public StorageAsset copyAsset( StorageAsset origin, String destination, CopyOption... copyOptions ) throws IOException
341     {
342         boolean container = origin.isContainer();
343         FilesystemAsset newAsset = new FilesystemAsset(this, destination, getAssetPath(destination), basePath, container );
344         copyAsset( origin, newAsset, copyOptions );
345         return newAsset;
346     }
347
348     @Override
349     public void copyAsset( StorageAsset origin, StorageAsset destination, CopyOption... copyOptions ) throws IOException
350     {
351         Path destinationPath = destination.getFilePath();
352         boolean overwrite = false;
353         for (int i=0; i<copyOptions.length; i++) {
354             if (copyOptions[i].equals( StandardCopyOption.REPLACE_EXISTING )) {
355                 overwrite=true;
356             }
357         }
358         if (Files.exists(destinationPath) && !overwrite) {
359             throw new IOException("Destination file exists already "+ destinationPath);
360         }
361         if (Files.isDirectory( origin.getFilePath() ))
362         {
363             FileUtils.copyDirectory(origin.getFilePath( ).toFile(), destinationPath.toFile() );
364         } else if (Files.isRegularFile( origin.getFilePath() )) {
365             if (!Files.exists( destinationPath )) {
366                 Files.createDirectories( destinationPath );
367             }
368             Files.copy( origin.getFilePath( ), destinationPath, copyOptions );
369         }
370     }
371
372     public FileLockManager getFileLockManager() {
373         return fileLockManager;
374     }
375
376 }