]> source.dussan.org Git - archiva.git/blob
65b5610be46d9eaeae4696191edf17a029ef7afc
[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.nio.file.Files;
34 import java.nio.file.Path;
35 import java.nio.file.Paths;
36 import java.util.function.Consumer;
37
38 /**
39  * Implementation of <code>{@link RepositoryStorage}</code> where data is stored in the filesystem.
40  *
41  * All files are relative to a given base path. Path values are separated by '/', '..' is allowed to navigate
42  * to a parent directory, but navigation out of the base path will lead to a exception.
43  */
44 public class FilesystemStorage implements RepositoryStorage {
45
46     private static final Logger log = LoggerFactory.getLogger(FilesystemStorage.class);
47
48     private final Path basePath;
49     private final FileLockManager fileLockManager;
50
51     public FilesystemStorage(Path basePath, FileLockManager fileLockManager) throws IOException {
52         this.basePath = basePath.normalize().toRealPath();
53         this.fileLockManager = fileLockManager;
54     }
55
56     private Path normalize(final String path) {
57         String nPath = path;
58         while (nPath.startsWith("/")) {
59             nPath = nPath.substring(1);
60         }
61         return Paths.get(nPath);
62     }
63
64     private Path getAssetPath(String path) throws IOException {
65         Path assetPath = basePath.resolve(normalize(path)).normalize();
66         if (!assetPath.startsWith(basePath))
67         {
68             throw new IOException("Path navigation out of allowed scope: "+path);
69         }
70         return assetPath;
71     }
72
73     @Override
74     public void consumeData( StorageAsset asset, Consumer<InputStream> consumerFunction, boolean readLock ) throws IOException
75     {
76         final Path path = asset.getFilePath();
77         try {
78             if (readLock) {
79                 consumeDataLocked( path, consumerFunction );
80             } else
81             {
82                 try ( InputStream is = Files.newInputStream( path ) )
83                 {
84                     consumerFunction.accept( is );
85                 }
86                 catch ( IOException e )
87                 {
88                     log.error("Could not read the input stream from file {}", path);
89                     throw e;
90                 }
91             }
92         } catch (RuntimeException e)
93         {
94             log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
95             throw new IOException( e );
96         }
97
98     }
99
100     private void consumeDataLocked( Path file, Consumer<InputStream> consumerFunction) throws IOException
101     {
102
103         final Lock lock;
104         try
105         {
106             lock = fileLockManager.readFileLock( file );
107             try ( InputStream is = Files.newInputStream( lock.getFile()))
108             {
109                 consumerFunction.accept( is );
110             }
111             catch ( IOException e )
112             {
113                 log.error("Could not read the input stream from file {}", file);
114                 throw e;
115             } finally
116             {
117                 fileLockManager.release( lock );
118             }
119         }
120         catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
121         {
122             log.error("Locking error on file {}", file);
123             throw new IOException(e);
124         }
125     }
126
127
128     @Override
129     public StorageAsset getAsset( String path )
130     {
131         try {
132             return new FilesystemAsset( path, getAssetPath(path));
133         } catch (IOException e) {
134             throw new IllegalArgumentException("Path navigates outside of base directory "+path);
135         }
136     }
137
138     @Override
139     public StorageAsset addAsset( String path, boolean container )
140     {
141         try {
142             return new FilesystemAsset( path, getAssetPath(path), container);
143         } catch (IOException e) {
144             throw new IllegalArgumentException("Path navigates outside of base directory "+path);
145         }
146     }
147
148     @Override
149     public void removeAsset( StorageAsset asset ) throws IOException
150     {
151         Files.delete(asset.getFilePath());
152     }
153
154     @Override
155     public StorageAsset moveAsset( StorageAsset origin, String destination ) throws IOException
156     {
157         boolean container = origin.isContainer();
158         FilesystemAsset newAsset = new FilesystemAsset( destination, getAssetPath(destination), container );
159         Files.move(origin.getFilePath(), newAsset.getFilePath());
160         return newAsset;
161     }
162
163     @Override
164     public StorageAsset copyAsset( StorageAsset origin, String destination ) throws IOException
165     {
166         boolean container = origin.isContainer();
167         FilesystemAsset newAsset = new FilesystemAsset( destination, getAssetPath(destination), container );
168         if (Files.exists(newAsset.getFilePath())) {
169             throw new IOException("Destination file exists already "+ newAsset.getFilePath());
170         }
171         if (Files.isDirectory( origin.getFilePath() ))
172         {
173             FileUtils.copyDirectory(origin.getFilePath( ).toFile(), newAsset.getFilePath( ).toFile() );
174         } else if (Files.isRegularFile( origin.getFilePath() )) {
175             FileUtils.copyFile(origin.getFilePath( ).toFile(), newAsset.getFilePath( ).toFile() );
176         }
177         return newAsset;
178     }
179
180 }