1 package org.apache.archiva.repository.content;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
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;
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;
39 * Implementation of <code>{@link RepositoryStorage}</code> where data is stored in the filesystem.
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.
44 public class FilesystemStorage implements RepositoryStorage {
46 private static final Logger log = LoggerFactory.getLogger(FilesystemStorage.class);
48 private final Path basePath;
49 private final FileLockManager fileLockManager;
51 public FilesystemStorage(Path basePath, FileLockManager fileLockManager) throws IOException {
52 this.basePath = basePath.normalize().toRealPath();
53 this.fileLockManager = fileLockManager;
56 private Path normalize(final String path) {
58 while (nPath.startsWith("/")) {
59 nPath = nPath.substring(1);
61 return Paths.get(nPath);
64 private Path getAssetPath(String path) throws IOException {
65 Path assetPath = basePath.resolve(normalize(path)).normalize();
66 if (!assetPath.startsWith(basePath))
68 throw new IOException("Path navigation out of allowed scope: "+path);
74 public void consumeData( StorageAsset asset, Consumer<InputStream> consumerFunction, boolean readLock ) throws IOException
76 final Path path = asset.getFilePath();
79 consumeDataLocked( path, consumerFunction );
82 try ( InputStream is = Files.newInputStream( path ) )
84 consumerFunction.accept( is );
86 catch ( IOException e )
88 log.error("Could not read the input stream from file {}", path);
92 } catch (RuntimeException e)
94 log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
95 throw new IOException( e );
100 private void consumeDataLocked( Path file, Consumer<InputStream> consumerFunction) throws IOException
106 lock = fileLockManager.readFileLock( file );
107 try ( InputStream is = Files.newInputStream( lock.getFile()))
109 consumerFunction.accept( is );
111 catch ( IOException e )
113 log.error("Could not read the input stream from file {}", file);
117 fileLockManager.release( lock );
120 catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
122 log.error("Locking error on file {}", file);
123 throw new IOException(e);
129 public StorageAsset getAsset( String path )
132 return new FilesystemAsset( path, getAssetPath(path));
133 } catch (IOException e) {
134 throw new IllegalArgumentException("Path navigates outside of base directory "+path);
139 public StorageAsset addAsset( String path, boolean container )
142 return new FilesystemAsset( path, getAssetPath(path), container);
143 } catch (IOException e) {
144 throw new IllegalArgumentException("Path navigates outside of base directory "+path);
149 public void removeAsset( StorageAsset asset ) throws IOException
151 Files.delete(asset.getFilePath());
155 public StorageAsset moveAsset( StorageAsset origin, String destination ) throws IOException
157 boolean container = origin.isContainer();
158 FilesystemAsset newAsset = new FilesystemAsset( destination, getAssetPath(destination), container );
159 Files.move(origin.getFilePath(), newAsset.getFilePath());
164 public StorageAsset copyAsset( StorageAsset origin, String destination ) throws IOException
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());
171 if (Files.isDirectory( origin.getFilePath() ))
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() );