import org.apache.archiva.repository.ContentNotFoundException;
import org.apache.archiva.repository.ManagedRepositoryContent;
import org.apache.archiva.metadata.audit.RepositoryListener;
-import org.apache.archiva.repository.storage.FsStorageUtil;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.repository.storage.util.StorageUtil;
import org.apache.commons.lang3.StringUtils;
import junit.framework.TestCase;
import org.apache.archiva.common.filelock.DefaultFileLockManager;
import org.apache.archiva.policies.urlcache.UrlFailureCache;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
import org.junit.Test;
* under the License.
*/
-import org.apache.archiva.checksum.Checksum;
import org.apache.archiva.common.filelock.DefaultFileLockManager;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
import org.apache.commons.io.FileUtils;
import junit.framework.TestCase;
import org.apache.archiva.common.filelock.DefaultFileLockManager;
import org.apache.archiva.policies.urlcache.UrlFailureCache;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
import org.junit.Test;
import junit.framework.TestCase;
import org.apache.archiva.common.filelock.DefaultFileLockManager;
import org.apache.archiva.policies.urlcache.UrlFailureCache;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
import org.junit.Test;
*/
import junit.framework.TestCase;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
import org.junit.Before;
import javax.inject.Named;
import java.nio.charset.Charset;
import java.nio.file.Files;
-import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.Locale;
import java.util.MissingResourceException;
import org.apache.archiva.repository.RepositoryType;
import org.apache.archiva.repository.metadata.base.MetadataTools;
import org.apache.archiva.repository.metadata.RepositoryMetadataException;
-import org.apache.archiva.repository.storage.FilesystemStorage;
-import org.apache.archiva.repository.storage.FsStorageUtil;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FsStorageUtil;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.scheduler.ArchivaTaskScheduler;
import org.apache.archiva.scheduler.repository.model.RepositoryTask;
import org.apache.archiva.repository.Repository;
import org.apache.archiva.repository.RepositoryType;
import org.apache.archiva.repository.UnsupportedRepositoryTypeException;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.repository.features.IndexCreationFeature;
import org.apache.archiva.repository.features.RemoteIndexFeature;
import org.apache.archiva.common.filelock.DefaultFileLockManager;
import org.apache.archiva.indexer.ArchivaIndexingContext;
import org.apache.archiva.repository.Repository;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.maven.index.context.IndexingContext;
import java.io.IOException;
-import java.net.URI;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.sql.Date;
import org.apache.archiva.repository.RepositoryRequestInfo;
import org.apache.archiva.repository.RepositoryType;
import org.apache.archiva.repository.StandardCapabilities;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.RepositoryStorage;
import org.apache.archiva.repository.features.ArtifactCleanupFeature;
import org.apache.archiva.repository.features.IndexCreationFeature;
import org.apache.archiva.repository.RepositoryCapabilities;
import org.apache.archiva.repository.RepositoryType;
import org.apache.archiva.repository.StandardCapabilities;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.RepositoryStorage;
import org.apache.archiva.repository.features.IndexCreationFeature;
import org.apache.archiva.repository.features.RemoteIndexFeature;
import org.apache.archiva.repository.content.ContentItem;
import org.apache.archiva.repository.content.base.builder.OptBuilder;
import org.apache.archiva.repository.mock.ManagedRepositoryContentMock;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.apache.archiva.repository.EditableManagedRepository;
import org.apache.archiva.repository.EditableRemoteRepository;
import org.apache.archiva.repository.ManagedRepository;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.scanner.mock.ManagedRepositoryContentMock;
import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
import org.apache.commons.io.FileUtils;
-import org.hamcrest.CoreMatchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
-import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.archiva.repository.content.Namespace;
import org.apache.archiva.repository.content.Project;
import org.apache.archiva.repository.content.Version;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.commons.lang3.StringUtils;
+++ /dev/null
-package org.apache.archiva.repository.storage;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.channels.FileChannel;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.WritableByteChannel;
-import java.nio.file.*;
-import java.nio.file.attribute.*;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Implementation of an asset that is stored on the filesystem.
- * <p>
- * The implementation does not check the given paths. Caller should normalize the asset path
- * and check, if the base path is a parent of the resulting path.
- * <p>
- * The file must not exist for all operations.
- *
- * @author Martin Stockhammer <martin_s@apache.org>
- */
-public class FilesystemAsset implements StorageAsset, Comparable {
-
- private final static Logger log = LoggerFactory.getLogger(FilesystemAsset.class);
-
- private final Path basePath;
- private final Path assetPath;
- private final String relativePath;
-
- public static final String DEFAULT_POSIX_FILE_PERMS = "rw-rw----";
- public static final String DEFAULT_POSIX_DIR_PERMS = "rwxrwx---";
-
- public static final Set<PosixFilePermission> DEFAULT_POSIX_FILE_PERMISSIONS;
- public static final Set<PosixFilePermission> DEFAULT_POSIX_DIR_PERMISSIONS;
-
- public static final AclEntryPermission[] DEFAULT_ACL_FILE_PERMISSIONS = new AclEntryPermission[]{
- AclEntryPermission.DELETE, AclEntryPermission.READ_ACL, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_DATA, AclEntryPermission.WRITE_ACL,
- AclEntryPermission.WRITE_ATTRIBUTES, AclEntryPermission.WRITE_DATA, AclEntryPermission.APPEND_DATA
- };
-
- public static final AclEntryPermission[] DEFAULT_ACL_DIR_PERMISSIONS = new AclEntryPermission[]{
- AclEntryPermission.ADD_FILE, AclEntryPermission.ADD_SUBDIRECTORY, AclEntryPermission.DELETE_CHILD,
- AclEntryPermission.DELETE, AclEntryPermission.READ_ACL, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_DATA, AclEntryPermission.WRITE_ACL,
- AclEntryPermission.WRITE_ATTRIBUTES, AclEntryPermission.WRITE_DATA, AclEntryPermission.APPEND_DATA
- };
-
- static {
-
- DEFAULT_POSIX_FILE_PERMISSIONS = PosixFilePermissions.fromString(DEFAULT_POSIX_FILE_PERMS);
- DEFAULT_POSIX_DIR_PERMISSIONS = PosixFilePermissions.fromString(DEFAULT_POSIX_DIR_PERMS);
- }
-
- Set<PosixFilePermission> defaultPosixFilePermissions = DEFAULT_POSIX_FILE_PERMISSIONS;
- Set<PosixFilePermission> defaultPosixDirectoryPermissions = DEFAULT_POSIX_DIR_PERMISSIONS;
-
- List<AclEntry> defaultFileAcls;
- List<AclEntry> defaultDirectoryAcls;
-
- boolean supportsAcl = false;
- boolean supportsPosix = false;
- final boolean setPermissionsForNew;
- final RepositoryStorage storage;
-
- boolean directoryHint = false;
-
- private static final OpenOption[] REPLACE_OPTIONS = new OpenOption[]{StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE};
- private static final OpenOption[] APPEND_OPTIONS = new OpenOption[]{StandardOpenOption.APPEND};
-
-
- FilesystemAsset(RepositoryStorage storage, String path, Path assetPath, Path basePath) {
- this.assetPath = assetPath;
- this.relativePath = normalizePath(path);
- this.setPermissionsForNew=false;
- this.basePath = basePath;
- this.storage = storage;
- init();
- }
-
- /**
- * Creates an asset for the given path. The given paths are not checked.
- * The base path should be an absolute path.
- *
- * @param path The logical path for the asset relative to the repository.
- * @param assetPath The asset path.
- */
- public FilesystemAsset(RepositoryStorage storage, String path, Path assetPath) {
- this.assetPath = assetPath;
- this.relativePath = normalizePath(path);
- this.setPermissionsForNew = false;
- this.basePath = null;
- this.storage = storage;
- // The base directory is always a directory
- if ("".equals(path) || "/".equals(path)) {
- this.directoryHint = true;
- }
- init();
- }
-
- /**
- * Creates an asset for the given path. The given paths are not checked.
- * The base path should be an absolute path.
- *
- * @param path The logical path for the asset relative to the repository
- * @param assetPath The asset path.
- * @param directory This is only relevant, if the represented file or directory does not exist yet and
- * is a hint.
- */
- public FilesystemAsset(RepositoryStorage storage, String path, Path assetPath, Path basePath, boolean directory) {
- this.assetPath = assetPath;
- this.relativePath = normalizePath(path);
- this.directoryHint = directory;
- this.setPermissionsForNew = false;
- this.basePath = basePath;
- this.storage = storage;
- init();
- }
-
- /**
- * Creates an asset for the given path. The given paths are not checked.
- * The base path should be an absolute path.
- *
- * @param path The logical path for the asset relative to the repository
- * @param assetPath The asset path.
- * @param directory This is only relevant, if the represented file or directory does not exist yet and
- * is a hint.
- */
- public FilesystemAsset(RepositoryStorage storage, String path, Path assetPath, Path basePath, boolean directory, boolean setPermissionsForNew) {
- this.assetPath = assetPath;
- this.relativePath = normalizePath(path);
- this.directoryHint = directory;
- this.setPermissionsForNew = setPermissionsForNew;
- this.basePath = basePath;
- this.storage = storage;
- init();
- }
-
- private String normalizePath(final String path) {
- if (!path.startsWith("/")) {
- return "/"+path;
- } else {
- String tmpPath = path;
- while (tmpPath.startsWith("//")) {
- tmpPath = tmpPath.substring(1);
- }
- return tmpPath;
- }
- }
-
- private void init() {
-
- if (setPermissionsForNew) {
- try {
- supportsAcl = Files.getFileStore(assetPath.getRoot()).supportsFileAttributeView(AclFileAttributeView.class);
- } catch (IOException e) {
- log.error("Could not check filesystem capabilities {}", e.getMessage());
- }
- try {
- supportsPosix = Files.getFileStore(assetPath.getRoot()).supportsFileAttributeView(PosixFileAttributeView.class);
- } catch (IOException e) {
- log.error("Could not check filesystem capabilities {}", e.getMessage());
- }
-
- if (supportsAcl) {
- AclFileAttributeView aclView = Files.getFileAttributeView(assetPath.getParent(), AclFileAttributeView.class);
- UserPrincipal owner = null;
- try {
- owner = aclView.getOwner();
- setDefaultFileAcls(processPermissions(owner, DEFAULT_ACL_FILE_PERMISSIONS));
- setDefaultDirectoryAcls(processPermissions(owner, DEFAULT_ACL_DIR_PERMISSIONS));
-
- } catch (IOException e) {
- supportsAcl = false;
- }
-
-
- }
- }
- }
-
- private List<AclEntry> processPermissions(UserPrincipal owner, AclEntryPermission[] defaultAclFilePermissions) {
- AclEntry.Builder aclBuilder = AclEntry.newBuilder();
- aclBuilder.setPermissions(defaultAclFilePermissions);
- aclBuilder.setType(AclEntryType.ALLOW);
- aclBuilder.setPrincipal(owner);
- ArrayList<AclEntry> aclList = new ArrayList<>();
- aclList.add(aclBuilder.build());
- return aclList;
- }
-
-
- @Override
- public RepositoryStorage getStorage( )
- {
- return storage;
- }
-
- @Override
- public String getPath() {
- return relativePath;
- }
-
- @Override
- public String getName() {
- return assetPath.getFileName().toString();
- }
-
- @Override
- public Instant getModificationTime() {
- try {
- return Files.getLastModifiedTime(assetPath).toInstant();
- } catch (IOException e) {
- log.error("Could not read modification time of {}", assetPath);
- return Instant.now();
- }
- }
-
- /**
- * Returns true, if the path of this asset points to a directory
- *
- * @return
- */
- @Override
- public boolean isContainer() {
- if (Files.exists(assetPath)) {
- return Files.isDirectory(assetPath);
- } else {
- return directoryHint;
- }
- }
-
- @Override
- public boolean isLeaf( )
- {
- if (Files.exists( assetPath )) {
- return Files.isRegularFile( assetPath );
- } else {
- return !directoryHint;
- }
- }
-
- /**
- * Returns the list of directory entries, if this asset represents a directory.
- * Otherwise a empty list will be returned.
- *
- * @return The list of entries in the directory, if it exists.
- */
- @Override
- public List<StorageAsset> list() {
- try {
- return Files.list(assetPath).map(p -> new FilesystemAsset(storage, relativePath + "/" + p.getFileName().toString(), assetPath.resolve(p)))
- .collect(Collectors.toList());
- } catch (IOException e) {
- return Collections.EMPTY_LIST;
- }
- }
-
- /**
- * Returns the size of the represented file. If it cannot be determined, -1 is returned.
- *
- * @return
- */
- @Override
- public long getSize() {
- try {
- return Files.size(assetPath);
- } catch (IOException e) {
- return -1;
- }
- }
-
- /**
- * Returns a input stream to the underlying file, if it exists. The caller has to make sure, that
- * the stream is closed after it was used.
- *
- * @return
- * @throws IOException
- */
- @Override
- public InputStream getReadStream() throws IOException {
- if (isContainer()) {
- throw new IOException("Can not create input stream for container");
- }
- return Files.newInputStream(assetPath);
- }
-
- @Override
- public ReadableByteChannel getReadChannel( ) throws IOException
- {
- return FileChannel.open( assetPath, StandardOpenOption.READ );
- }
-
- private OpenOption[] getOpenOptions(boolean replace) {
- return replace ? REPLACE_OPTIONS : APPEND_OPTIONS;
- }
-
- @Override
- public OutputStream getWriteStream( boolean replace) throws IOException {
- OpenOption[] options = getOpenOptions( replace );
- if (!Files.exists( assetPath )) {
- create();
- }
- return Files.newOutputStream(assetPath, options);
- }
-
- @Override
- public WritableByteChannel getWriteChannel( boolean replace ) throws IOException
- {
- OpenOption[] options = getOpenOptions( replace );
- return FileChannel.open( assetPath, options );
- }
-
- @Override
- public boolean replaceDataFromFile( Path newData) throws IOException {
- final boolean createNew = !Files.exists(assetPath);
- Path backup = null;
- if (!createNew) {
- backup = findBackupFile(assetPath);
- }
- try {
- if (!createNew) {
- Files.move(assetPath, backup);
- }
- Files.move(newData, assetPath, StandardCopyOption.REPLACE_EXISTING);
- applyDefaultPermissions(assetPath);
- return true;
- } catch (IOException e) {
- log.error("Could not overwrite file {}", assetPath);
- // Revert if possible
- if (backup != null && Files.exists(backup)) {
- Files.move(backup, assetPath, StandardCopyOption.REPLACE_EXISTING);
- }
- throw e;
- } finally {
- if (backup != null) {
- try {
- Files.deleteIfExists(backup);
- } catch (IOException e) {
- log.error("Could not delete backup file {}", backup);
- }
- }
- }
-
- }
-
- private void applyDefaultPermissions(Path filePath) {
- try {
- if (supportsPosix) {
- Set<PosixFilePermission> perms;
- if (Files.isDirectory(filePath)) {
- perms = defaultPosixFilePermissions;
- } else {
- perms = defaultPosixDirectoryPermissions;
- }
- Files.setPosixFilePermissions(filePath, perms);
- } else if (supportsAcl) {
- List<AclEntry> perms;
- if (Files.isDirectory(filePath)) {
- perms = getDefaultDirectoryAcls();
- } else {
- perms = getDefaultFileAcls();
- }
- AclFileAttributeView aclAttr = Files.getFileAttributeView(filePath, AclFileAttributeView.class);
- aclAttr.setAcl(perms);
- }
- } catch (IOException e) {
- log.error("Could not set permissions for {}: {}", filePath, e.getMessage());
- }
- }
-
- private Path findBackupFile(Path file) {
- String ext = ".bak";
- Path backupPath = file.getParent().resolve(file.getFileName().toString() + ext);
- int idx = 0;
- while (Files.exists(backupPath)) {
- backupPath = file.getParent().resolve(file.getFileName().toString() + ext + idx++);
- }
- return backupPath;
- }
-
- @Override
- public boolean exists() {
- return Files.exists(assetPath);
- }
-
- @Override
- public Path getFilePath() throws UnsupportedOperationException {
- return assetPath;
- }
-
- @Override
- public boolean isFileBased( )
- {
- return true;
- }
-
- @Override
- public boolean hasParent( )
- {
- if (basePath!=null && assetPath.equals(basePath)) {
- return false;
- }
- return assetPath.getParent()!=null;
- }
-
- @Override
- public StorageAsset getParent( )
- {
- Path parentPath;
- if (basePath!=null && assetPath.equals( basePath )) {
- parentPath=null;
- } else
- {
- parentPath = assetPath.getParent( );
- }
- String relativeParent = StringUtils.substringBeforeLast( relativePath,"/");
- if (parentPath!=null) {
- return new FilesystemAsset(storage, relativeParent, parentPath, basePath, true, setPermissionsForNew );
- } else {
- return null;
- }
- }
-
- @Override
- public StorageAsset resolve(String toPath) {
- return storage.getAsset(this.getPath()+"/"+toPath);
- }
-
-
- public void setDefaultFileAcls(List<AclEntry> acl) {
- defaultFileAcls = acl;
- }
-
- public List<AclEntry> getDefaultFileAcls() {
- return defaultFileAcls;
- }
-
- public void setDefaultPosixFilePermissions(Set<PosixFilePermission> perms) {
- defaultPosixFilePermissions = perms;
- }
-
- public Set<PosixFilePermission> getDefaultPosixFilePermissions() {
- return defaultPosixFilePermissions;
- }
-
- public void setDefaultDirectoryAcls(List<AclEntry> acl) {
- defaultDirectoryAcls = acl;
- }
-
- public List<AclEntry> getDefaultDirectoryAcls() {
- return defaultDirectoryAcls;
- }
-
- public void setDefaultPosixDirectoryPermissions(Set<PosixFilePermission> perms) {
- defaultPosixDirectoryPermissions = perms;
- }
-
- public Set<PosixFilePermission> getDefaultPosixDirectoryPermissions() {
- return defaultPosixDirectoryPermissions;
- }
-
- @Override
- public void create() throws IOException {
- if (!Files.exists(assetPath)) {
- if (directoryHint) {
- Files.createDirectories(assetPath);
- } else {
- if (!Files.exists( assetPath.getParent() )) {
- Files.createDirectories( assetPath.getParent( ) );
- }
- Files.createFile(assetPath);
- }
- if (setPermissionsForNew) {
- applyDefaultPermissions(assetPath);
- }
- }
- }
-
- @Override
- public String toString() {
- return relativePath+":"+assetPath;
- }
-
- @Override
- public int compareTo(Object o) {
- if (o instanceof FilesystemAsset) {
- if (this.getPath()!=null) {
- return this.getPath().compareTo(((FilesystemAsset) o).getPath());
- } else {
- return 0;
- }
- }
- return 0;
- }
-}
+++ /dev/null
-package org.apache.archiva.repository.storage;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.apache.archiva.common.filelock.FileLockException;
-import org.apache.archiva.common.filelock.FileLockManager;
-import org.apache.archiva.common.filelock.FileLockTimeoutException;
-import org.apache.archiva.common.filelock.Lock;
-import org.apache.archiva.common.utils.PathUtil;
-import org.apache.commons.io.FileUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URI;
-import java.nio.channels.FileChannel;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.WritableByteChannel;
-import java.nio.file.*;
-import java.util.function.Consumer;
-
-/**
- * Implementation of <code>{@link RepositoryStorage}</code> where data is stored in the filesystem.
- *
- * All files are relative to a given base path. Path values are separated by '/', '..' is allowed to navigate
- * to a parent directory, but navigation out of the base path will lead to a exception.
- */
-public class FilesystemStorage implements RepositoryStorage {
-
- private static final Logger log = LoggerFactory.getLogger(FilesystemStorage.class);
-
- private Path basePath;
- private final FileLockManager fileLockManager;
-
- public FilesystemStorage(Path basePath, FileLockManager fileLockManager) throws IOException {
- if (!Files.exists(basePath)) {
- Files.createDirectories(basePath);
- }
- this.basePath = basePath.normalize().toRealPath();
- this.fileLockManager = fileLockManager;
- }
-
- private Path normalize(final String path) {
- String nPath = path;
- while (nPath.startsWith("/")) {
- nPath = nPath.substring(1);
- }
- return Paths.get(nPath);
- }
-
- private Path getAssetPath(String path) throws IOException {
- Path assetPath = basePath.resolve(normalize(path)).normalize();
- if (!assetPath.startsWith(basePath))
- {
- throw new IOException("Path navigation out of allowed scope: "+path);
- }
- return assetPath;
- }
-
- @Override
- public void consumeData(StorageAsset asset, Consumer<InputStream> consumerFunction, boolean readLock ) throws IOException
- {
- final Path path = asset.getFilePath();
- try {
- if (readLock) {
- consumeDataLocked( path, consumerFunction );
- } else
- {
- try ( InputStream is = Files.newInputStream( path ) )
- {
- consumerFunction.accept( is );
- }
- catch ( IOException e )
- {
- log.error("Could not read the input stream from file {}", path);
- throw e;
- }
- }
- } catch (RuntimeException e)
- {
- log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
- throw new IOException( e );
- }
-
- }
-
- @Override
- public void consumeDataFromChannel( StorageAsset asset, Consumer<ReadableByteChannel> consumerFunction, boolean readLock ) throws IOException
- {
- final Path path = asset.getFilePath();
- try {
- if (readLock) {
- consumeDataFromChannelLocked( path, consumerFunction );
- } else
- {
- try ( FileChannel is = FileChannel.open( path, StandardOpenOption.READ ) )
- {
- consumerFunction.accept( is );
- }
- catch ( IOException e )
- {
- log.error("Could not read the input stream from file {}", path);
- throw e;
- }
- }
- } catch (RuntimeException e)
- {
- log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
- throw new IOException( e );
- }
- }
-
- @Override
- public void writeData( StorageAsset asset, Consumer<OutputStream> consumerFunction, boolean writeLock ) throws IOException
- {
- final Path path = asset.getFilePath();
- try {
- if (writeLock) {
- writeDataLocked( path, consumerFunction );
- } else
- {
- try ( OutputStream is = Files.newOutputStream( path ) )
- {
- consumerFunction.accept( is );
- }
- catch ( IOException e )
- {
- log.error("Could not write the output stream to file {}", path);
- throw e;
- }
- }
- } catch (RuntimeException e)
- {
- log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
- throw new IOException( e );
- }
-
- }
-
- @Override
- public void writeDataToChannel( StorageAsset asset, Consumer<WritableByteChannel> consumerFunction, boolean writeLock ) throws IOException
- {
- final Path path = asset.getFilePath();
- try {
- if (writeLock) {
- writeDataToChannelLocked( path, consumerFunction );
- } else
- {
- try ( FileChannel os = FileChannel.open( path, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE ))
- {
- consumerFunction.accept( os );
- }
- catch ( IOException e )
- {
- log.error("Could not write the data to file {}", path);
- throw e;
- }
- }
- } catch (RuntimeException e)
- {
- log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
- throw new IOException( e );
- }
- }
-
- private void consumeDataLocked( Path file, Consumer<InputStream> consumerFunction) throws IOException
- {
-
- final Lock lock;
- try
- {
- lock = fileLockManager.readFileLock( file );
- try ( InputStream is = Files.newInputStream( lock.getFile()))
- {
- consumerFunction.accept( is );
- }
- catch ( IOException e )
- {
- log.error("Could not read the input stream from file {}", file);
- throw e;
- } finally
- {
- fileLockManager.release( lock );
- }
- }
- catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
- {
- log.error("Locking error on file {}", file);
- throw new IOException(e);
- }
- }
-
- private void consumeDataFromChannelLocked( Path file, Consumer<ReadableByteChannel> consumerFunction) throws IOException
- {
-
- final Lock lock;
- try
- {
- lock = fileLockManager.readFileLock( file );
- try ( FileChannel is = FileChannel.open( lock.getFile( ), StandardOpenOption.READ ))
- {
- consumerFunction.accept( is );
- }
- catch ( IOException e )
- {
- log.error("Could not read the input stream from file {}", file);
- throw e;
- } finally
- {
- fileLockManager.release( lock );
- }
- }
- catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
- {
- log.error("Locking error on file {}", file);
- throw new IOException(e);
- }
- }
-
-
- private void writeDataLocked( Path file, Consumer<OutputStream> consumerFunction) throws IOException
- {
-
- final Lock lock;
- try
- {
- lock = fileLockManager.writeFileLock( file );
- try ( OutputStream is = Files.newOutputStream( lock.getFile()))
- {
- consumerFunction.accept( is );
- }
- catch ( IOException e )
- {
- log.error("Could not write the output stream to file {}", file);
- throw e;
- } finally
- {
- fileLockManager.release( lock );
- }
- }
- catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
- {
- log.error("Locking error on file {}", file);
- throw new IOException(e);
- }
- }
-
- private void writeDataToChannelLocked( Path file, Consumer<WritableByteChannel> consumerFunction) throws IOException
- {
-
- final Lock lock;
- try
- {
- lock = fileLockManager.writeFileLock( file );
- try ( FileChannel is = FileChannel.open( lock.getFile( ), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE ))
- {
- consumerFunction.accept( is );
- }
- catch ( IOException e )
- {
- log.error("Could not write to file {}", file);
- throw e;
- } finally
- {
- fileLockManager.release( lock );
- }
- }
- catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
- {
- log.error("Locking error on file {}", file);
- throw new IOException(e);
- }
- }
-
- @Override
- public URI getLocation() {
- return basePath.toUri();
- }
-
- /**
- * Updates the location and releases all locks.
- *
- * @param newLocation The URI to the new location
- *
- * @throws IOException If the directory cannot be created.
- */
- @Override
- public void updateLocation(URI newLocation) throws IOException {
- Path newPath = PathUtil.getPathFromUri(newLocation).toAbsolutePath();
- if (!Files.exists(newPath)) {
- Files.createDirectories(newPath);
- }
- basePath = newPath;
- if (fileLockManager!=null) {
- fileLockManager.clearLockFiles();
- }
- }
-
- @Override
- public StorageAsset getAsset( String path )
- {
- try {
- return new FilesystemAsset(this, path, getAssetPath(path));
- } catch (IOException e) {
- throw new IllegalArgumentException("Path navigates outside of base directory "+path);
- }
- }
-
- @Override
- public StorageAsset addAsset( String path, boolean container )
- {
- try {
- return new FilesystemAsset(this, path, getAssetPath(path), basePath, container);
- } catch (IOException e) {
- throw new IllegalArgumentException("Path navigates outside of base directory "+path);
- }
- }
-
- @Override
- public void removeAsset( StorageAsset asset ) throws IOException
- {
- Files.delete(asset.getFilePath());
- }
-
- @Override
- public StorageAsset moveAsset( StorageAsset origin, String destination, CopyOption... copyOptions ) throws IOException
- {
- boolean container = origin.isContainer();
- FilesystemAsset newAsset = new FilesystemAsset(this, destination, getAssetPath(destination), basePath, container );
- moveAsset( origin, newAsset, copyOptions );
- return newAsset;
- }
-
- @Override
- public void moveAsset( StorageAsset origin, StorageAsset destination, CopyOption... copyOptions ) throws IOException
- {
- if (origin.getStorage()!=this) {
- throw new IOException("The origin asset does not belong to this storage instance. Cannot copy between different storage instances.");
- }
- if (destination.getStorage()!=this) {
- throw new IOException("The destination asset does not belong to this storage instance. Cannot copy between different storage instances.");
- }
- Files.move(origin.getFilePath(), destination.getFilePath(), copyOptions);
- }
-
- @Override
- public StorageAsset copyAsset( StorageAsset origin, String destination, CopyOption... copyOptions ) throws IOException
- {
- boolean container = origin.isContainer();
- FilesystemAsset newAsset = new FilesystemAsset(this, destination, getAssetPath(destination), basePath, container );
- copyAsset( origin, newAsset, copyOptions );
- return newAsset;
- }
-
- @Override
- public void copyAsset( StorageAsset origin, StorageAsset destination, CopyOption... copyOptions ) throws IOException
- {
- if (origin.getStorage()!=this) {
- throw new IOException("The origin asset does not belong to this storage instance. Cannot copy between different storage instances.");
- }
- if (destination.getStorage()!=this) {
- throw new IOException("The destination asset does not belong to this storage instance. Cannot copy between different storage instances.");
- }
- Path destinationPath = destination.getFilePath();
- boolean overwrite = false;
- for (int i=0; i<copyOptions.length; i++) {
- if (copyOptions[i].equals( StandardCopyOption.REPLACE_EXISTING )) {
- overwrite=true;
- }
- }
- if (Files.exists(destinationPath) && !overwrite) {
- throw new IOException("Destination file exists already "+ destinationPath);
- }
- if (Files.isDirectory( origin.getFilePath() ))
- {
- FileUtils.copyDirectory(origin.getFilePath( ).toFile(), destinationPath.toFile() );
- } else if (Files.isRegularFile( origin.getFilePath() )) {
- if (!Files.exists( destinationPath )) {
- Files.createDirectories( destinationPath );
- }
- Files.copy( origin.getFilePath( ), destinationPath, copyOptions );
- }
- }
-
- public FileLockManager getFileLockManager() {
- return fileLockManager;
- }
-
-}
+++ /dev/null
-package org.apache.archiva.repository.storage;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.apache.archiva.common.filelock.FileLockException;
-import org.apache.archiva.common.filelock.FileLockManager;
-import org.apache.archiva.common.filelock.Lock;
-import org.apache.archiva.repository.storage.util.StorageUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.nio.channels.FileChannel;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.file.*;
-import java.util.HashSet;
-
-/**
- *
- * Utility class for assets. Allows to copy, move between different storage instances and
- * recursively consume the tree.
- *
- * @author Martin Stockhammer <martin_s@apache.org>
- */
-public class FsStorageUtil
-{
- private static final Logger log = LoggerFactory.getLogger( FsStorageUtil.class);
-
- /**
- * Copies the source asset to the target. The assets may be from different RepositoryStorage instances.
- * If you know that source and asset are from the same storage instance, the copy method of the storage
- * instance may be faster.
- *
- * @param source The source asset
- * @param target The target asset
- * @param locked If true, a readlock is set on the source and a write lock is set on the target.
- * @param copyOptions Copy options
- * @throws IOException
- */
- public static final void copyAsset( final StorageAsset source,
- final StorageAsset target,
- boolean locked,
- final CopyOption... copyOptions ) throws IOException
- {
- if (source.isFileBased() && target.isFileBased()) {
- // Short cut for FS operations
- final Path sourcePath = source.getFilePath();
- final Path targetPath = target.getFilePath( );
- if (locked) {
- final FileLockManager lmSource = ((FilesystemStorage)source.getStorage()).getFileLockManager();
- final FileLockManager lmTarget = ((FilesystemStorage)target.getStorage()).getFileLockManager();
- Lock lockRead = null;
- Lock lockWrite = null;
- try {
- lockRead = lmSource.readFileLock(sourcePath);
- } catch (Exception e) {
- log.error("Could not create read lock on {}", sourcePath);
- throw new IOException(e);
- }
- try {
- lockWrite = lmTarget.writeFileLock(targetPath);
- } catch (Exception e) {
- log.error("Could not create write lock on {}", targetPath);
- throw new IOException(e);
- }
- try {
- Files.copy(sourcePath, targetPath, copyOptions);
- } finally {
- if (lockRead!=null) {
- try {
- lmSource.release(lockRead);
- } catch (FileLockException e) {
- log.error("Error during lock release of read lock {}", lockRead.getFile());
- }
- }
- if (lockWrite!=null) {
- try {
- lmTarget.release(lockWrite);
- } catch (FileLockException e) {
- log.error("Error during lock release of write lock {}", lockWrite.getFile());
- }
- }
- }
- } else
- {
- Files.copy( sourcePath, targetPath, copyOptions );
- }
- } else {
- try {
- final RepositoryStorage sourceStorage = source.getStorage();
- final RepositoryStorage targetStorage = target.getStorage();
- sourceStorage.consumeDataFromChannel( source, is -> wrapWriteFunction( is, targetStorage, target, locked ), locked);
- } catch (IOException e) {
- throw e;
- } catch (Throwable e) {
- Throwable cause = e.getCause();
- if (cause instanceof IOException) {
- throw (IOException)cause;
- } else
- {
- throw new IOException( e );
- }
- }
- }
- }
-
- private static final void wrapWriteFunction( ReadableByteChannel is, RepositoryStorage targetStorage, StorageAsset target, boolean locked) {
- try {
- targetStorage.writeDataToChannel( target, os -> StorageUtil.copy(is, os), locked );
- } catch (Exception e) {
- throw new RuntimeException( e );
- }
- }
-
-
- public static final void copyToLocalFile(StorageAsset asset, Path destination, CopyOption... copyOptions) throws IOException {
- if (asset.isFileBased()) {
- Files.copy(asset.getFilePath(), destination, copyOptions);
- } else {
- try {
-
- HashSet<OpenOption> openOptions = new HashSet<>();
- for (CopyOption option : copyOptions) {
- if (option == StandardCopyOption.REPLACE_EXISTING) {
- openOptions.add(StandardOpenOption.CREATE);
- openOptions.add(StandardOpenOption.TRUNCATE_EXISTING);
- openOptions.add(StandardOpenOption.WRITE);
- } else {
- openOptions.add(StandardOpenOption.WRITE);
- openOptions.add(StandardOpenOption.CREATE_NEW);
- }
- }
- asset.getStorage().consumeDataFromChannel(asset, channel -> {
- try {
- FileChannel.open(destination, openOptions).transferFrom(channel, 0, Long.MAX_VALUE);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }, false);
- } catch (Throwable e) {
- if (e.getCause() instanceof IOException) {
- throw (IOException)e.getCause();
- } else {
- throw new IOException(e);
- }
- }
- }
- }
-
- public static class PathInformation {
- final Path path ;
- final boolean tmpFile;
-
- PathInformation(Path path, boolean tmpFile) {
- this.path = path;
- this.tmpFile = tmpFile;
- }
-
- public Path getPath() {
- return path;
- }
-
- public boolean isTmpFile() {
- return tmpFile;
- }
-
- }
-
- public static final PathInformation getAssetDataAsPath(StorageAsset asset) throws IOException {
- if (!asset.exists()) {
- throw new IOException("Asset does not exist");
- }
- if (asset.isFileBased()) {
- return new PathInformation(asset.getFilePath(), false);
- } else {
- Path tmpFile = Files.createTempFile(asset.getName(), org.apache.archiva.repository.storage.util.StorageUtil.getExtension(asset));
- copyToLocalFile(asset, tmpFile, StandardCopyOption.REPLACE_EXISTING);
- return new PathInformation(tmpFile, true);
- }
- }
-
-}
--- /dev/null
+package org.apache.archiva.repository.storage.fs;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.repository.storage.RepositoryStorage;
+import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Implementation of an asset that is stored on the filesystem.
+ * <p>
+ * The implementation does not check the given paths. Caller should normalize the asset path
+ * and check, if the base path is a parent of the resulting path.
+ * <p>
+ * The file must not exist for all operations.
+ *
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+public class FilesystemAsset implements StorageAsset, Comparable {
+
+ private final static Logger log = LoggerFactory.getLogger(FilesystemAsset.class);
+
+ private final Path basePath;
+ private final Path assetPath;
+ private final String relativePath;
+
+ public static final String DEFAULT_POSIX_FILE_PERMS = "rw-rw----";
+ public static final String DEFAULT_POSIX_DIR_PERMS = "rwxrwx---";
+
+ public static final Set<PosixFilePermission> DEFAULT_POSIX_FILE_PERMISSIONS;
+ public static final Set<PosixFilePermission> DEFAULT_POSIX_DIR_PERMISSIONS;
+
+ public static final AclEntryPermission[] DEFAULT_ACL_FILE_PERMISSIONS = new AclEntryPermission[]{
+ AclEntryPermission.DELETE, AclEntryPermission.READ_ACL, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_DATA, AclEntryPermission.WRITE_ACL,
+ AclEntryPermission.WRITE_ATTRIBUTES, AclEntryPermission.WRITE_DATA, AclEntryPermission.APPEND_DATA
+ };
+
+ public static final AclEntryPermission[] DEFAULT_ACL_DIR_PERMISSIONS = new AclEntryPermission[]{
+ AclEntryPermission.ADD_FILE, AclEntryPermission.ADD_SUBDIRECTORY, AclEntryPermission.DELETE_CHILD,
+ AclEntryPermission.DELETE, AclEntryPermission.READ_ACL, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_DATA, AclEntryPermission.WRITE_ACL,
+ AclEntryPermission.WRITE_ATTRIBUTES, AclEntryPermission.WRITE_DATA, AclEntryPermission.APPEND_DATA
+ };
+
+ static {
+
+ DEFAULT_POSIX_FILE_PERMISSIONS = PosixFilePermissions.fromString(DEFAULT_POSIX_FILE_PERMS);
+ DEFAULT_POSIX_DIR_PERMISSIONS = PosixFilePermissions.fromString(DEFAULT_POSIX_DIR_PERMS);
+ }
+
+ Set<PosixFilePermission> defaultPosixFilePermissions = DEFAULT_POSIX_FILE_PERMISSIONS;
+ Set<PosixFilePermission> defaultPosixDirectoryPermissions = DEFAULT_POSIX_DIR_PERMISSIONS;
+
+ List<AclEntry> defaultFileAcls;
+ List<AclEntry> defaultDirectoryAcls;
+
+ boolean supportsAcl = false;
+ boolean supportsPosix = false;
+ final boolean setPermissionsForNew;
+ final RepositoryStorage storage;
+
+ boolean directoryHint = false;
+
+ private static final OpenOption[] REPLACE_OPTIONS = new OpenOption[]{StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE};
+ private static final OpenOption[] APPEND_OPTIONS = new OpenOption[]{StandardOpenOption.APPEND};
+
+
+ FilesystemAsset(RepositoryStorage storage, String path, Path assetPath, Path basePath) {
+ this.assetPath = assetPath;
+ this.relativePath = normalizePath(path);
+ this.setPermissionsForNew=false;
+ this.basePath = basePath;
+ this.storage = storage;
+ init();
+ }
+
+ /**
+ * Creates an asset for the given path. The given paths are not checked.
+ * The base path should be an absolute path.
+ *
+ * @param path The logical path for the asset relative to the repository.
+ * @param assetPath The asset path.
+ */
+ public FilesystemAsset(RepositoryStorage storage, String path, Path assetPath) {
+ this.assetPath = assetPath;
+ this.relativePath = normalizePath(path);
+ this.setPermissionsForNew = false;
+ this.basePath = null;
+ this.storage = storage;
+ // The base directory is always a directory
+ if ("".equals(path) || "/".equals(path)) {
+ this.directoryHint = true;
+ }
+ init();
+ }
+
+ /**
+ * Creates an asset for the given path. The given paths are not checked.
+ * The base path should be an absolute path.
+ *
+ * @param path The logical path for the asset relative to the repository
+ * @param assetPath The asset path.
+ * @param directory This is only relevant, if the represented file or directory does not exist yet and
+ * is a hint.
+ */
+ public FilesystemAsset(RepositoryStorage storage, String path, Path assetPath, Path basePath, boolean directory) {
+ this.assetPath = assetPath;
+ this.relativePath = normalizePath(path);
+ this.directoryHint = directory;
+ this.setPermissionsForNew = false;
+ this.basePath = basePath;
+ this.storage = storage;
+ init();
+ }
+
+ /**
+ * Creates an asset for the given path. The given paths are not checked.
+ * The base path should be an absolute path.
+ *
+ * @param path The logical path for the asset relative to the repository
+ * @param assetPath The asset path.
+ * @param directory This is only relevant, if the represented file or directory does not exist yet and
+ * is a hint.
+ */
+ public FilesystemAsset(RepositoryStorage storage, String path, Path assetPath, Path basePath, boolean directory, boolean setPermissionsForNew) {
+ this.assetPath = assetPath;
+ this.relativePath = normalizePath(path);
+ this.directoryHint = directory;
+ this.setPermissionsForNew = setPermissionsForNew;
+ this.basePath = basePath;
+ this.storage = storage;
+ init();
+ }
+
+ private String normalizePath(final String path) {
+ if (!path.startsWith("/")) {
+ return "/"+path;
+ } else {
+ String tmpPath = path;
+ while (tmpPath.startsWith("//")) {
+ tmpPath = tmpPath.substring(1);
+ }
+ return tmpPath;
+ }
+ }
+
+ private void init() {
+
+ if (setPermissionsForNew) {
+ try {
+ supportsAcl = Files.getFileStore(assetPath.getRoot()).supportsFileAttributeView(AclFileAttributeView.class);
+ } catch (IOException e) {
+ log.error("Could not check filesystem capabilities {}", e.getMessage());
+ }
+ try {
+ supportsPosix = Files.getFileStore(assetPath.getRoot()).supportsFileAttributeView(PosixFileAttributeView.class);
+ } catch (IOException e) {
+ log.error("Could not check filesystem capabilities {}", e.getMessage());
+ }
+
+ if (supportsAcl) {
+ AclFileAttributeView aclView = Files.getFileAttributeView(assetPath.getParent(), AclFileAttributeView.class);
+ UserPrincipal owner = null;
+ try {
+ owner = aclView.getOwner();
+ setDefaultFileAcls(processPermissions(owner, DEFAULT_ACL_FILE_PERMISSIONS));
+ setDefaultDirectoryAcls(processPermissions(owner, DEFAULT_ACL_DIR_PERMISSIONS));
+
+ } catch (IOException e) {
+ supportsAcl = false;
+ }
+
+
+ }
+ }
+ }
+
+ private List<AclEntry> processPermissions(UserPrincipal owner, AclEntryPermission[] defaultAclFilePermissions) {
+ AclEntry.Builder aclBuilder = AclEntry.newBuilder();
+ aclBuilder.setPermissions(defaultAclFilePermissions);
+ aclBuilder.setType(AclEntryType.ALLOW);
+ aclBuilder.setPrincipal(owner);
+ ArrayList<AclEntry> aclList = new ArrayList<>();
+ aclList.add(aclBuilder.build());
+ return aclList;
+ }
+
+
+ @Override
+ public RepositoryStorage getStorage( )
+ {
+ return storage;
+ }
+
+ @Override
+ public String getPath() {
+ return relativePath;
+ }
+
+ @Override
+ public String getName() {
+ return assetPath.getFileName().toString();
+ }
+
+ @Override
+ public Instant getModificationTime() {
+ try {
+ return Files.getLastModifiedTime(assetPath).toInstant();
+ } catch (IOException e) {
+ log.error("Could not read modification time of {}", assetPath);
+ return Instant.now();
+ }
+ }
+
+ /**
+ * Returns true, if the path of this asset points to a directory
+ *
+ * @return
+ */
+ @Override
+ public boolean isContainer() {
+ if (Files.exists(assetPath)) {
+ return Files.isDirectory(assetPath);
+ } else {
+ return directoryHint;
+ }
+ }
+
+ @Override
+ public boolean isLeaf( )
+ {
+ if (Files.exists( assetPath )) {
+ return Files.isRegularFile( assetPath );
+ } else {
+ return !directoryHint;
+ }
+ }
+
+ /**
+ * Returns the list of directory entries, if this asset represents a directory.
+ * Otherwise a empty list will be returned.
+ *
+ * @return The list of entries in the directory, if it exists.
+ */
+ @Override
+ public List<StorageAsset> list() {
+ try {
+ return Files.list(assetPath).map(p -> new FilesystemAsset(storage, relativePath + "/" + p.getFileName().toString(), assetPath.resolve(p)))
+ .collect(Collectors.toList());
+ } catch (IOException e) {
+ return Collections.EMPTY_LIST;
+ }
+ }
+
+ /**
+ * Returns the size of the represented file. If it cannot be determined, -1 is returned.
+ *
+ * @return
+ */
+ @Override
+ public long getSize() {
+ try {
+ return Files.size(assetPath);
+ } catch (IOException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Returns a input stream to the underlying file, if it exists. The caller has to make sure, that
+ * the stream is closed after it was used.
+ *
+ * @return
+ * @throws IOException
+ */
+ @Override
+ public InputStream getReadStream() throws IOException {
+ if (isContainer()) {
+ throw new IOException("Can not create input stream for container");
+ }
+ return Files.newInputStream(assetPath);
+ }
+
+ @Override
+ public ReadableByteChannel getReadChannel( ) throws IOException
+ {
+ return FileChannel.open( assetPath, StandardOpenOption.READ );
+ }
+
+ private OpenOption[] getOpenOptions(boolean replace) {
+ return replace ? REPLACE_OPTIONS : APPEND_OPTIONS;
+ }
+
+ @Override
+ public OutputStream getWriteStream( boolean replace) throws IOException {
+ OpenOption[] options = getOpenOptions( replace );
+ if (!Files.exists( assetPath )) {
+ create();
+ }
+ return Files.newOutputStream(assetPath, options);
+ }
+
+ @Override
+ public WritableByteChannel getWriteChannel( boolean replace ) throws IOException
+ {
+ OpenOption[] options = getOpenOptions( replace );
+ return FileChannel.open( assetPath, options );
+ }
+
+ @Override
+ public boolean replaceDataFromFile( Path newData) throws IOException {
+ final boolean createNew = !Files.exists(assetPath);
+ Path backup = null;
+ if (!createNew) {
+ backup = findBackupFile(assetPath);
+ }
+ try {
+ if (!createNew) {
+ Files.move(assetPath, backup);
+ }
+ Files.move(newData, assetPath, StandardCopyOption.REPLACE_EXISTING);
+ applyDefaultPermissions(assetPath);
+ return true;
+ } catch (IOException e) {
+ log.error("Could not overwrite file {}", assetPath);
+ // Revert if possible
+ if (backup != null && Files.exists(backup)) {
+ Files.move(backup, assetPath, StandardCopyOption.REPLACE_EXISTING);
+ }
+ throw e;
+ } finally {
+ if (backup != null) {
+ try {
+ Files.deleteIfExists(backup);
+ } catch (IOException e) {
+ log.error("Could not delete backup file {}", backup);
+ }
+ }
+ }
+
+ }
+
+ private void applyDefaultPermissions(Path filePath) {
+ try {
+ if (supportsPosix) {
+ Set<PosixFilePermission> perms;
+ if (Files.isDirectory(filePath)) {
+ perms = defaultPosixFilePermissions;
+ } else {
+ perms = defaultPosixDirectoryPermissions;
+ }
+ Files.setPosixFilePermissions(filePath, perms);
+ } else if (supportsAcl) {
+ List<AclEntry> perms;
+ if (Files.isDirectory(filePath)) {
+ perms = getDefaultDirectoryAcls();
+ } else {
+ perms = getDefaultFileAcls();
+ }
+ AclFileAttributeView aclAttr = Files.getFileAttributeView(filePath, AclFileAttributeView.class);
+ aclAttr.setAcl(perms);
+ }
+ } catch (IOException e) {
+ log.error("Could not set permissions for {}: {}", filePath, e.getMessage());
+ }
+ }
+
+ private Path findBackupFile(Path file) {
+ String ext = ".bak";
+ Path backupPath = file.getParent().resolve(file.getFileName().toString() + ext);
+ int idx = 0;
+ while (Files.exists(backupPath)) {
+ backupPath = file.getParent().resolve(file.getFileName().toString() + ext + idx++);
+ }
+ return backupPath;
+ }
+
+ @Override
+ public boolean exists() {
+ return Files.exists(assetPath);
+ }
+
+ @Override
+ public Path getFilePath() throws UnsupportedOperationException {
+ return assetPath;
+ }
+
+ @Override
+ public boolean isFileBased( )
+ {
+ return true;
+ }
+
+ @Override
+ public boolean hasParent( )
+ {
+ if (basePath!=null && assetPath.equals(basePath)) {
+ return false;
+ }
+ return assetPath.getParent()!=null;
+ }
+
+ @Override
+ public StorageAsset getParent( )
+ {
+ Path parentPath;
+ if (basePath!=null && assetPath.equals( basePath )) {
+ parentPath=null;
+ } else
+ {
+ parentPath = assetPath.getParent( );
+ }
+ String relativeParent = StringUtils.substringBeforeLast( relativePath,"/");
+ if (parentPath!=null) {
+ return new FilesystemAsset(storage, relativeParent, parentPath, basePath, true, setPermissionsForNew );
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public StorageAsset resolve(String toPath) {
+ return storage.getAsset(this.getPath()+"/"+toPath);
+ }
+
+
+ public void setDefaultFileAcls(List<AclEntry> acl) {
+ defaultFileAcls = acl;
+ }
+
+ public List<AclEntry> getDefaultFileAcls() {
+ return defaultFileAcls;
+ }
+
+ public void setDefaultPosixFilePermissions(Set<PosixFilePermission> perms) {
+ defaultPosixFilePermissions = perms;
+ }
+
+ public Set<PosixFilePermission> getDefaultPosixFilePermissions() {
+ return defaultPosixFilePermissions;
+ }
+
+ public void setDefaultDirectoryAcls(List<AclEntry> acl) {
+ defaultDirectoryAcls = acl;
+ }
+
+ public List<AclEntry> getDefaultDirectoryAcls() {
+ return defaultDirectoryAcls;
+ }
+
+ public void setDefaultPosixDirectoryPermissions(Set<PosixFilePermission> perms) {
+ defaultPosixDirectoryPermissions = perms;
+ }
+
+ public Set<PosixFilePermission> getDefaultPosixDirectoryPermissions() {
+ return defaultPosixDirectoryPermissions;
+ }
+
+ @Override
+ public void create() throws IOException {
+ if (!Files.exists(assetPath)) {
+ if (directoryHint) {
+ Files.createDirectories(assetPath);
+ } else {
+ if (!Files.exists( assetPath.getParent() )) {
+ Files.createDirectories( assetPath.getParent( ) );
+ }
+ Files.createFile(assetPath);
+ }
+ if (setPermissionsForNew) {
+ applyDefaultPermissions(assetPath);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return relativePath+":"+assetPath;
+ }
+
+ @Override
+ public int compareTo(Object o) {
+ if (o instanceof FilesystemAsset) {
+ if (this.getPath()!=null) {
+ return this.getPath().compareTo(((FilesystemAsset) o).getPath());
+ } else {
+ return 0;
+ }
+ }
+ return 0;
+ }
+}
--- /dev/null
+package org.apache.archiva.repository.storage.fs;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.common.filelock.FileLockException;
+import org.apache.archiva.common.filelock.FileLockManager;
+import org.apache.archiva.common.filelock.FileLockTimeoutException;
+import org.apache.archiva.common.filelock.Lock;
+import org.apache.archiva.common.utils.PathUtil;
+import org.apache.archiva.repository.storage.RepositoryStorage;
+import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.commons.io.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.nio.file.*;
+import java.util.function.Consumer;
+
+/**
+ * Implementation of <code>{@link RepositoryStorage}</code> where data is stored in the filesystem.
+ *
+ * All files are relative to a given base path. Path values are separated by '/', '..' is allowed to navigate
+ * to a parent directory, but navigation out of the base path will lead to a exception.
+ */
+public class FilesystemStorage implements RepositoryStorage {
+
+ private static final Logger log = LoggerFactory.getLogger(FilesystemStorage.class);
+
+ private Path basePath;
+ private final FileLockManager fileLockManager;
+
+ public FilesystemStorage(Path basePath, FileLockManager fileLockManager) throws IOException {
+ if (!Files.exists(basePath)) {
+ Files.createDirectories(basePath);
+ }
+ this.basePath = basePath.normalize().toRealPath();
+ this.fileLockManager = fileLockManager;
+ }
+
+ private Path normalize(final String path) {
+ String nPath = path;
+ while (nPath.startsWith("/")) {
+ nPath = nPath.substring(1);
+ }
+ return Paths.get(nPath);
+ }
+
+ private Path getAssetPath(String path) throws IOException {
+ Path assetPath = basePath.resolve(normalize(path)).normalize();
+ if (!assetPath.startsWith(basePath))
+ {
+ throw new IOException("Path navigation out of allowed scope: "+path);
+ }
+ return assetPath;
+ }
+
+ @Override
+ public void consumeData( StorageAsset asset, Consumer<InputStream> consumerFunction, boolean readLock ) throws IOException
+ {
+ final Path path = asset.getFilePath();
+ try {
+ if (readLock) {
+ consumeDataLocked( path, consumerFunction );
+ } else
+ {
+ try ( InputStream is = Files.newInputStream( path ) )
+ {
+ consumerFunction.accept( is );
+ }
+ catch ( IOException e )
+ {
+ log.error("Could not read the input stream from file {}", path);
+ throw e;
+ }
+ }
+ } catch (RuntimeException e)
+ {
+ log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
+ throw new IOException( e );
+ }
+
+ }
+
+ @Override
+ public void consumeDataFromChannel( StorageAsset asset, Consumer<ReadableByteChannel> consumerFunction, boolean readLock ) throws IOException
+ {
+ final Path path = asset.getFilePath();
+ try {
+ if (readLock) {
+ consumeDataFromChannelLocked( path, consumerFunction );
+ } else
+ {
+ try ( FileChannel is = FileChannel.open( path, StandardOpenOption.READ ) )
+ {
+ consumerFunction.accept( is );
+ }
+ catch ( IOException e )
+ {
+ log.error("Could not read the input stream from file {}", path);
+ throw e;
+ }
+ }
+ } catch (RuntimeException e)
+ {
+ log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
+ throw new IOException( e );
+ }
+ }
+
+ @Override
+ public void writeData( StorageAsset asset, Consumer<OutputStream> consumerFunction, boolean writeLock ) throws IOException
+ {
+ final Path path = asset.getFilePath();
+ try {
+ if (writeLock) {
+ writeDataLocked( path, consumerFunction );
+ } else
+ {
+ try ( OutputStream is = Files.newOutputStream( path ) )
+ {
+ consumerFunction.accept( is );
+ }
+ catch ( IOException e )
+ {
+ log.error("Could not write the output stream to file {}", path);
+ throw e;
+ }
+ }
+ } catch (RuntimeException e)
+ {
+ log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
+ throw new IOException( e );
+ }
+
+ }
+
+ @Override
+ public void writeDataToChannel( StorageAsset asset, Consumer<WritableByteChannel> consumerFunction, boolean writeLock ) throws IOException
+ {
+ final Path path = asset.getFilePath();
+ try {
+ if (writeLock) {
+ writeDataToChannelLocked( path, consumerFunction );
+ } else
+ {
+ try ( FileChannel os = FileChannel.open( path, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE ))
+ {
+ consumerFunction.accept( os );
+ }
+ catch ( IOException e )
+ {
+ log.error("Could not write the data to file {}", path);
+ throw e;
+ }
+ }
+ } catch (RuntimeException e)
+ {
+ log.error( "Runtime exception during data consume from artifact {}. Error: {}", path, e.getMessage() );
+ throw new IOException( e );
+ }
+ }
+
+ private void consumeDataLocked( Path file, Consumer<InputStream> consumerFunction) throws IOException
+ {
+
+ final Lock lock;
+ try
+ {
+ lock = fileLockManager.readFileLock( file );
+ try ( InputStream is = Files.newInputStream( lock.getFile()))
+ {
+ consumerFunction.accept( is );
+ }
+ catch ( IOException e )
+ {
+ log.error("Could not read the input stream from file {}", file);
+ throw e;
+ } finally
+ {
+ fileLockManager.release( lock );
+ }
+ }
+ catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
+ {
+ log.error("Locking error on file {}", file);
+ throw new IOException(e);
+ }
+ }
+
+ private void consumeDataFromChannelLocked( Path file, Consumer<ReadableByteChannel> consumerFunction) throws IOException
+ {
+
+ final Lock lock;
+ try
+ {
+ lock = fileLockManager.readFileLock( file );
+ try ( FileChannel is = FileChannel.open( lock.getFile( ), StandardOpenOption.READ ))
+ {
+ consumerFunction.accept( is );
+ }
+ catch ( IOException e )
+ {
+ log.error("Could not read the input stream from file {}", file);
+ throw e;
+ } finally
+ {
+ fileLockManager.release( lock );
+ }
+ }
+ catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
+ {
+ log.error("Locking error on file {}", file);
+ throw new IOException(e);
+ }
+ }
+
+
+ private void writeDataLocked( Path file, Consumer<OutputStream> consumerFunction) throws IOException
+ {
+
+ final Lock lock;
+ try
+ {
+ lock = fileLockManager.writeFileLock( file );
+ try ( OutputStream is = Files.newOutputStream( lock.getFile()))
+ {
+ consumerFunction.accept( is );
+ }
+ catch ( IOException e )
+ {
+ log.error("Could not write the output stream to file {}", file);
+ throw e;
+ } finally
+ {
+ fileLockManager.release( lock );
+ }
+ }
+ catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
+ {
+ log.error("Locking error on file {}", file);
+ throw new IOException(e);
+ }
+ }
+
+ private void writeDataToChannelLocked( Path file, Consumer<WritableByteChannel> consumerFunction) throws IOException
+ {
+
+ final Lock lock;
+ try
+ {
+ lock = fileLockManager.writeFileLock( file );
+ try ( FileChannel is = FileChannel.open( lock.getFile( ), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE ))
+ {
+ consumerFunction.accept( is );
+ }
+ catch ( IOException e )
+ {
+ log.error("Could not write to file {}", file);
+ throw e;
+ } finally
+ {
+ fileLockManager.release( lock );
+ }
+ }
+ catch ( FileLockException | FileNotFoundException | FileLockTimeoutException e)
+ {
+ log.error("Locking error on file {}", file);
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public URI getLocation() {
+ return basePath.toUri();
+ }
+
+ /**
+ * Updates the location and releases all locks.
+ *
+ * @param newLocation The URI to the new location
+ *
+ * @throws IOException If the directory cannot be created.
+ */
+ @Override
+ public void updateLocation(URI newLocation) throws IOException {
+ Path newPath = PathUtil.getPathFromUri(newLocation).toAbsolutePath();
+ if (!Files.exists(newPath)) {
+ Files.createDirectories(newPath);
+ }
+ basePath = newPath;
+ if (fileLockManager!=null) {
+ fileLockManager.clearLockFiles();
+ }
+ }
+
+ @Override
+ public StorageAsset getAsset( String path )
+ {
+ try {
+ return new FilesystemAsset(this, path, getAssetPath(path));
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Path navigates outside of base directory "+path);
+ }
+ }
+
+ @Override
+ public StorageAsset addAsset( String path, boolean container )
+ {
+ try {
+ return new FilesystemAsset(this, path, getAssetPath(path), basePath, container);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Path navigates outside of base directory "+path);
+ }
+ }
+
+ @Override
+ public void removeAsset( StorageAsset asset ) throws IOException
+ {
+ Files.delete(asset.getFilePath());
+ }
+
+ @Override
+ public StorageAsset moveAsset( StorageAsset origin, String destination, CopyOption... copyOptions ) throws IOException
+ {
+ boolean container = origin.isContainer();
+ FilesystemAsset newAsset = new FilesystemAsset(this, destination, getAssetPath(destination), basePath, container );
+ moveAsset( origin, newAsset, copyOptions );
+ return newAsset;
+ }
+
+ @Override
+ public void moveAsset( StorageAsset origin, StorageAsset destination, CopyOption... copyOptions ) throws IOException
+ {
+ if (origin.getStorage()!=this) {
+ throw new IOException("The origin asset does not belong to this storage instance. Cannot copy between different storage instances.");
+ }
+ if (destination.getStorage()!=this) {
+ throw new IOException("The destination asset does not belong to this storage instance. Cannot copy between different storage instances.");
+ }
+ Files.move(origin.getFilePath(), destination.getFilePath(), copyOptions);
+ }
+
+ @Override
+ public StorageAsset copyAsset( StorageAsset origin, String destination, CopyOption... copyOptions ) throws IOException
+ {
+ boolean container = origin.isContainer();
+ FilesystemAsset newAsset = new FilesystemAsset(this, destination, getAssetPath(destination), basePath, container );
+ copyAsset( origin, newAsset, copyOptions );
+ return newAsset;
+ }
+
+ @Override
+ public void copyAsset( StorageAsset origin, StorageAsset destination, CopyOption... copyOptions ) throws IOException
+ {
+ if (origin.getStorage()!=this) {
+ throw new IOException("The origin asset does not belong to this storage instance. Cannot copy between different storage instances.");
+ }
+ if (destination.getStorage()!=this) {
+ throw new IOException("The destination asset does not belong to this storage instance. Cannot copy between different storage instances.");
+ }
+ Path destinationPath = destination.getFilePath();
+ boolean overwrite = false;
+ for (int i=0; i<copyOptions.length; i++) {
+ if (copyOptions[i].equals( StandardCopyOption.REPLACE_EXISTING )) {
+ overwrite=true;
+ }
+ }
+ if (Files.exists(destinationPath) && !overwrite) {
+ throw new IOException("Destination file exists already "+ destinationPath);
+ }
+ if (Files.isDirectory( origin.getFilePath() ))
+ {
+ FileUtils.copyDirectory(origin.getFilePath( ).toFile(), destinationPath.toFile() );
+ } else if (Files.isRegularFile( origin.getFilePath() )) {
+ if (!Files.exists( destinationPath )) {
+ Files.createDirectories( destinationPath );
+ }
+ Files.copy( origin.getFilePath( ), destinationPath, copyOptions );
+ }
+ }
+
+ public FileLockManager getFileLockManager() {
+ return fileLockManager;
+ }
+
+}
--- /dev/null
+package org.apache.archiva.repository.storage.fs;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.common.filelock.FileLockException;
+import org.apache.archiva.common.filelock.FileLockManager;
+import org.apache.archiva.common.filelock.Lock;
+import org.apache.archiva.repository.storage.RepositoryStorage;
+import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.archiva.repository.storage.util.StorageUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.*;
+import java.util.HashSet;
+
+/**
+ *
+ * Utility class for assets. Allows to copy, move between different storage instances and
+ * recursively consume the tree.
+ *
+ * @author Martin Stockhammer <martin_s@apache.org>
+ */
+public class FsStorageUtil
+{
+ private static final Logger log = LoggerFactory.getLogger( FsStorageUtil.class);
+
+ /**
+ * Copies the source asset to the target. The assets may be from different RepositoryStorage instances.
+ * If you know that source and asset are from the same storage instance, the copy method of the storage
+ * instance may be faster.
+ *
+ * @param source The source asset
+ * @param target The target asset
+ * @param locked If true, a readlock is set on the source and a write lock is set on the target.
+ * @param copyOptions Copy options
+ * @throws IOException
+ */
+ public static final void copyAsset( final StorageAsset source,
+ final StorageAsset target,
+ boolean locked,
+ final CopyOption... copyOptions ) throws IOException
+ {
+ if (source.isFileBased() && target.isFileBased()) {
+ // Short cut for FS operations
+ final Path sourcePath = source.getFilePath();
+ final Path targetPath = target.getFilePath( );
+ if (locked) {
+ final FileLockManager lmSource = ((FilesystemStorage)source.getStorage()).getFileLockManager();
+ final FileLockManager lmTarget = ((FilesystemStorage)target.getStorage()).getFileLockManager();
+ Lock lockRead = null;
+ Lock lockWrite = null;
+ try {
+ lockRead = lmSource.readFileLock(sourcePath);
+ } catch (Exception e) {
+ log.error("Could not create read lock on {}", sourcePath);
+ throw new IOException(e);
+ }
+ try {
+ lockWrite = lmTarget.writeFileLock(targetPath);
+ } catch (Exception e) {
+ log.error("Could not create write lock on {}", targetPath);
+ throw new IOException(e);
+ }
+ try {
+ Files.copy(sourcePath, targetPath, copyOptions);
+ } finally {
+ if (lockRead!=null) {
+ try {
+ lmSource.release(lockRead);
+ } catch (FileLockException e) {
+ log.error("Error during lock release of read lock {}", lockRead.getFile());
+ }
+ }
+ if (lockWrite!=null) {
+ try {
+ lmTarget.release(lockWrite);
+ } catch (FileLockException e) {
+ log.error("Error during lock release of write lock {}", lockWrite.getFile());
+ }
+ }
+ }
+ } else
+ {
+ Files.copy( sourcePath, targetPath, copyOptions );
+ }
+ } else {
+ try {
+ final RepositoryStorage sourceStorage = source.getStorage();
+ final RepositoryStorage targetStorage = target.getStorage();
+ sourceStorage.consumeDataFromChannel( source, is -> wrapWriteFunction( is, targetStorage, target, locked ), locked);
+ } catch (IOException e) {
+ throw e;
+ } catch (Throwable e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException)cause;
+ } else
+ {
+ throw new IOException( e );
+ }
+ }
+ }
+ }
+
+ private static final void wrapWriteFunction( ReadableByteChannel is, RepositoryStorage targetStorage, StorageAsset target, boolean locked) {
+ try {
+ targetStorage.writeDataToChannel( target, os -> StorageUtil.copy(is, os), locked );
+ } catch (Exception e) {
+ throw new RuntimeException( e );
+ }
+ }
+
+
+ public static final void copyToLocalFile(StorageAsset asset, Path destination, CopyOption... copyOptions) throws IOException {
+ if (asset.isFileBased()) {
+ Files.copy(asset.getFilePath(), destination, copyOptions);
+ } else {
+ try {
+
+ HashSet<OpenOption> openOptions = new HashSet<>();
+ for (CopyOption option : copyOptions) {
+ if (option == StandardCopyOption.REPLACE_EXISTING) {
+ openOptions.add(StandardOpenOption.CREATE);
+ openOptions.add(StandardOpenOption.TRUNCATE_EXISTING);
+ openOptions.add(StandardOpenOption.WRITE);
+ } else {
+ openOptions.add(StandardOpenOption.WRITE);
+ openOptions.add(StandardOpenOption.CREATE_NEW);
+ }
+ }
+ asset.getStorage().consumeDataFromChannel(asset, channel -> {
+ try {
+ FileChannel.open(destination, openOptions).transferFrom(channel, 0, Long.MAX_VALUE);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }, false);
+ } catch (Throwable e) {
+ if (e.getCause() instanceof IOException) {
+ throw (IOException)e.getCause();
+ } else {
+ throw new IOException(e);
+ }
+ }
+ }
+ }
+
+ public static class PathInformation {
+ final Path path ;
+ final boolean tmpFile;
+
+ PathInformation(Path path, boolean tmpFile) {
+ this.path = path;
+ this.tmpFile = tmpFile;
+ }
+
+ public Path getPath() {
+ return path;
+ }
+
+ public boolean isTmpFile() {
+ return tmpFile;
+ }
+
+ }
+
+ public static final PathInformation getAssetDataAsPath(StorageAsset asset) throws IOException {
+ if (!asset.exists()) {
+ throw new IOException("Asset does not exist");
+ }
+ if (asset.isFileBased()) {
+ return new PathInformation(asset.getFilePath(), false);
+ } else {
+ Path tmpFile = Files.createTempFile(asset.getName(), org.apache.archiva.repository.storage.util.StorageUtil.getExtension(asset));
+ copyToLocalFile(asset, tmpFile, StandardCopyOption.REPLACE_EXISTING);
+ return new PathInformation(tmpFile, true);
+ }
+ }
+
+}
+++ /dev/null
-package org.apache.archiva.repository.storage;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.apache.archiva.common.filelock.DefaultFileLockManager;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.time.Instant;
-
-public class FilesystemAssetTest {
-
- Path assetPathFile;
- Path assetPathDir;
- FilesystemStorage filesystemStorage;
-
- @Before
- public void init() throws IOException {
- assetPathDir = Files.createTempDirectory("assetDir");
- assetPathFile = Files.createTempFile(assetPathDir,"assetFile", "dat");
- filesystemStorage = new FilesystemStorage(assetPathDir, new DefaultFileLockManager());
- }
-
- @After
- public void cleanup() {
-
- try {
- Files.deleteIfExists(assetPathFile);
- } catch (IOException e) {
- e.printStackTrace();
- }
- FileUtils.deleteQuietly(assetPathDir.toFile());
- }
-
-
- @Test
- public void getPath() {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, assetPathFile.getFileName().toString(), assetPathFile);
- Assert.assertEquals("/"+assetPathFile.getFileName().toString(), asset.getPath());
- }
-
- @Test
- public void getName() {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/"+assetPathFile.getFileName().toString(), assetPathFile);
- Assert.assertEquals(assetPathFile.getFileName().toString(), asset.getName());
-
- }
-
- @Test
- public void getModificationTime() throws IOException {
- Instant modTime = Files.getLastModifiedTime(assetPathFile).toInstant();
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test123", assetPathFile);
- Assert.assertTrue(modTime.equals(asset.getModificationTime()));
- }
-
- @Test
- public void isContainer() {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1323", assetPathFile);
- Assert.assertFalse(asset.isContainer());
- FilesystemAsset asset2 = new FilesystemAsset(filesystemStorage, "/test1234", assetPathDir);
- Assert.assertTrue(asset2.isContainer());
- }
-
- @Test
- public void list() throws IOException {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
- Assert.assertEquals(0, asset.list().size());
-
- FilesystemAsset asset2 = new FilesystemAsset(filesystemStorage, "/test1235", assetPathDir);
- Assert.assertEquals(1, asset2.list().size());
- Path f1 = Files.createTempFile(assetPathDir, "testfile", "dat");
- Path f2 = Files.createTempFile(assetPathDir, "testfile", "dat");
- Path d1 = Files.createTempDirectory(assetPathDir, "testdir");
- Assert.assertEquals(4, asset2.list().size());
- Assert.assertTrue(asset2.list().stream().anyMatch(p -> p.getName().equals(f1.getFileName().toString())));
- Assert.assertTrue(asset2.list().stream().anyMatch(p -> p.getName().equals(f2.getFileName().toString())));
- Assert.assertTrue(asset2.list().stream().anyMatch(p -> p.getName().equals(d1.getFileName().toString())));
- Files.deleteIfExists(f1);
- Files.deleteIfExists(f2);
- Files.deleteIfExists(d1);
-
-
- }
-
- @Test
- public void getSize() throws IOException {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
- Assert.assertEquals(0, asset.getSize());
-
- Files.write(assetPathFile, new String("abcdef").getBytes("ASCII"));
- Assert.assertTrue(asset.getSize()>=6);
-
-
- }
-
- @Test
- public void getData() throws IOException {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
- Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
- try(InputStream is = asset.getReadStream()) {
- Assert.assertEquals("abcdef", IOUtils.toString(is, "ASCII"));
- }
-
- }
-
- @Test
- public void getDataExceptionOnDir() throws IOException {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathDir);
- Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
- try {
- InputStream is = asset.getReadStream();
- Assert.assertFalse("Exception expected for data on dir", true);
- } catch (IOException e) {
- // fine
- }
-
- }
-
- @Test
- public void writeData() throws IOException {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
- Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
- try(OutputStream os = asset.getWriteStream(true)) {
- IOUtils.write("test12345", os, "ASCII");
- }
- Assert.assertEquals("test12345", IOUtils.toString(assetPathFile.toUri().toURL(), "ASCII"));
- }
-
- @Test
- public void writeDataAppend() throws IOException {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
- Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
- try(OutputStream os = asset.getWriteStream(false)) {
- IOUtils.write("test12345", os, "ASCII");
- }
- Assert.assertEquals("abcdeftest12345", IOUtils.toString(assetPathFile.toUri().toURL(), "ASCII"));
- }
-
- @Test
- public void writeDataExceptionOnDir() throws IOException {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathDir);
- try {
-
- OutputStream os = asset.getWriteStream(true);
- Assert.assertTrue("Writing to a directory should throw a IOException", false);
- } catch (IOException e) {
- // Fine
- }
- }
-
- @Test
- public void storeDataFile() throws IOException {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
- Path dataFile = Files.createTempFile("testdata", "dat");
- try(OutputStream os = Files.newOutputStream(dataFile)) {
- IOUtils.write("testkdkdkd", os, "ASCII");
- }
- asset.replaceDataFromFile(dataFile);
- Assert.assertEquals("testkdkdkd", IOUtils.toString(assetPathFile.toUri().toURL(), "ASCII"));
- }
-
- @Test
- public void exists() {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
- Assert.assertTrue(asset.exists());
- FilesystemAsset asset2 = new FilesystemAsset(filesystemStorage, "/test1234", Paths.get("abcdefgkdkdk"));
- Assert.assertFalse(asset2.exists());
-
- }
-
- @Test
- public void getFilePath() {
- FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
- Assert.assertEquals(assetPathFile, asset.getFilePath());
- }
-}
\ No newline at end of file
+++ /dev/null
-package org.apache.archiva.repository.storage;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.apache.archiva.common.filelock.DefaultFileLockManager;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
-import org.apache.archiva.repository.storage.StorageAsset;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-
-import static org.junit.Assert.*;
-
-public class FilesystemStorageTest {
-
- private FilesystemStorage fsStorage;
- private FilesystemAsset file1Asset;
- private FilesystemAsset dir1Asset;
- private Path baseDir;
- private Path file1;
- private Path dir1;
-
- @Before
- public void init() throws IOException {
- baseDir = Files.createTempDirectory("FsStorageTest");
- DefaultFileLockManager fl = new DefaultFileLockManager();
- fsStorage = new FilesystemStorage(baseDir,fl);
- Files.createDirectories(baseDir.resolve("dir1"));
- Files.createDirectories(baseDir.resolve("dir2"));
- file1 = Files.createFile(baseDir.resolve("dir1/testfile1.dat"));
- dir1 = Files.createDirectories(baseDir.resolve("dir1/testdir"));
- file1Asset = new FilesystemAsset(fsStorage, "/dir1/testfile1.dat", file1);
- dir1Asset = new FilesystemAsset(fsStorage, "/dir1/testdir", dir1);
- }
-
- private class StringResult {
- public String getData() {
- return data;
- }
-
- public void setData(String data) {
- this.data = data;
- }
-
- String data;
- }
-
-
- @After
- public void cleanup() {
- FileUtils.deleteQuietly(file1.toFile());
- FileUtils.deleteQuietly(dir1.toFile());
- FileUtils.deleteQuietly(baseDir.resolve("dir1").toFile());
- FileUtils.deleteQuietly(baseDir.resolve("dir2").toFile());
- FileUtils.deleteQuietly(baseDir.toFile());
- }
-
-
-
-
- @Test
- public void consumeData() throws IOException {
- try(OutputStream os = Files.newOutputStream(file1)) {
- IOUtils.write("abcdefghijkl", os, "ASCII");
- }
- StringResult result = new StringResult();
- fsStorage.consumeData(file1Asset, is -> consume(is, result), false );
- Assert.assertEquals("abcdefghijkl" ,result.getData());
- }
-
- private void consume(InputStream is, StringResult result) {
- try {
- result.setData(IOUtils.toString(is, "ASCII"));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
-
- @Test
- public void getAsset() {
- StorageAsset asset = fsStorage.getAsset("/dir1/testfile1.dat");
- Assert.assertEquals(file1, asset.getFilePath());
- }
-
- @Test
- public void addAsset() {
- StorageAsset newAsset = fsStorage.addAsset("dir2/test", false);
- Assert.assertNotNull(newAsset);
- Assert.assertFalse(newAsset.isContainer());
- Assert.assertFalse(newAsset.exists());
-
- StorageAsset newDirAsset = fsStorage.addAsset("/dir2/testdir2", true);
- Assert.assertNotNull(newDirAsset);
- Assert.assertTrue(newDirAsset.isContainer());
- Assert.assertFalse(newDirAsset.exists());
- }
-
- @Test
- public void removeAsset() throws IOException {
- Assert.assertTrue(Files.exists(file1));
- fsStorage.removeAsset(file1Asset);
- Assert.assertFalse(Files.exists(file1));
-
- Assert.assertTrue(Files.exists(dir1));
- fsStorage.removeAsset(dir1Asset);
- Assert.assertFalse(Files.exists(dir1));
- }
-
- @Test
- public void moveAsset() throws IOException {
- Path newFile=null;
- Path newDir=null;
- try {
- Assert.assertTrue(Files.exists(file1));
- try (OutputStream os = Files.newOutputStream(file1)) {
- IOUtils.write("testakdkkdkdkdk", os, "ASCII");
- }
- long fileSize = Files.size(file1);
- fsStorage.moveAsset(file1Asset, "/dir2/testfile2.dat");
- Assert.assertFalse(Files.exists(file1));
- newFile = baseDir.resolve("dir2/testfile2.dat");
- Assert.assertTrue(Files.exists(newFile));
- Assert.assertEquals(fileSize, Files.size(newFile));
-
-
- Assert.assertTrue(Files.exists(dir1));
- newDir = baseDir.resolve("dir2/testdir2");
- fsStorage.moveAsset(dir1Asset, "dir2/testdir2");
- Assert.assertFalse(Files.exists(dir1));
- Assert.assertTrue(Files.exists(newDir));
- } finally {
- if (newFile!=null) Files.deleteIfExists(newFile);
- if (newDir!=null) Files.deleteIfExists(newDir);
- }
- }
-
- @Test
- public void copyAsset() throws IOException {
- Path newFile=null;
- Path newDir=null;
- try {
- Assert.assertTrue(Files.exists(file1));
- try (OutputStream os = Files.newOutputStream(file1)) {
- IOUtils.write("testakdkkdkdkdk", os, "ASCII");
- }
- long fileSize = Files.size(file1);
- fsStorage.copyAsset(file1Asset, "/dir2/testfile2.dat", StandardCopyOption.REPLACE_EXISTING);
- Assert.assertTrue(Files.exists(file1));
- Assert.assertEquals(fileSize, Files.size(file1));
- newFile = baseDir.resolve("dir2/testfile2.dat");
- Assert.assertTrue(Files.exists(newFile));
- Assert.assertEquals(fileSize, Files.size(newFile));
-
- try {
- fsStorage.copyAsset(file1Asset, "/dir2/testfile2.dat");
- Assert.assertTrue("IOException should be thrown (File exists)", false);
- } catch (IOException ex) {
- Assert.assertTrue("Exception must contain 'file exists'", ex.getMessage().contains("file exists"));
- }
-
- Assert.assertTrue(Files.exists(dir1));
- newDir = baseDir.resolve("dir2/testdir2");
- fsStorage.copyAsset(dir1Asset, "dir2/testdir2");
- Assert.assertTrue(Files.exists(dir1));
- Assert.assertTrue(Files.exists(newDir));
- } finally {
- if (newFile!=null) Files.deleteIfExists(newFile);
- if (newDir!=null) FileUtils.deleteQuietly(newDir.toFile());
- }
- }
-}
\ No newline at end of file
--- /dev/null
+package org.apache.archiva.repository.storage.fs;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.common.filelock.DefaultFileLockManager;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Instant;
+
+public class FilesystemAssetTest {
+
+ Path assetPathFile;
+ Path assetPathDir;
+ FilesystemStorage filesystemStorage;
+
+ @Before
+ public void init() throws IOException {
+ assetPathDir = Files.createTempDirectory("assetDir");
+ assetPathFile = Files.createTempFile(assetPathDir,"assetFile", "dat");
+ filesystemStorage = new FilesystemStorage(assetPathDir, new DefaultFileLockManager());
+ }
+
+ @After
+ public void cleanup() {
+
+ try {
+ Files.deleteIfExists(assetPathFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ FileUtils.deleteQuietly(assetPathDir.toFile());
+ }
+
+
+ @Test
+ public void getPath() {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, assetPathFile.getFileName().toString(), assetPathFile);
+ Assert.assertEquals("/"+assetPathFile.getFileName().toString(), asset.getPath());
+ }
+
+ @Test
+ public void getName() {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/"+assetPathFile.getFileName().toString(), assetPathFile);
+ Assert.assertEquals(assetPathFile.getFileName().toString(), asset.getName());
+
+ }
+
+ @Test
+ public void getModificationTime() throws IOException {
+ Instant modTime = Files.getLastModifiedTime(assetPathFile).toInstant();
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test123", assetPathFile);
+ Assert.assertTrue(modTime.equals(asset.getModificationTime()));
+ }
+
+ @Test
+ public void isContainer() {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1323", assetPathFile);
+ Assert.assertFalse(asset.isContainer());
+ FilesystemAsset asset2 = new FilesystemAsset(filesystemStorage, "/test1234", assetPathDir);
+ Assert.assertTrue(asset2.isContainer());
+ }
+
+ @Test
+ public void list() throws IOException {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
+ Assert.assertEquals(0, asset.list().size());
+
+ FilesystemAsset asset2 = new FilesystemAsset(filesystemStorage, "/test1235", assetPathDir);
+ Assert.assertEquals(1, asset2.list().size());
+ Path f1 = Files.createTempFile(assetPathDir, "testfile", "dat");
+ Path f2 = Files.createTempFile(assetPathDir, "testfile", "dat");
+ Path d1 = Files.createTempDirectory(assetPathDir, "testdir");
+ Assert.assertEquals(4, asset2.list().size());
+ Assert.assertTrue(asset2.list().stream().anyMatch(p -> p.getName().equals(f1.getFileName().toString())));
+ Assert.assertTrue(asset2.list().stream().anyMatch(p -> p.getName().equals(f2.getFileName().toString())));
+ Assert.assertTrue(asset2.list().stream().anyMatch(p -> p.getName().equals(d1.getFileName().toString())));
+ Files.deleteIfExists(f1);
+ Files.deleteIfExists(f2);
+ Files.deleteIfExists(d1);
+
+
+ }
+
+ @Test
+ public void getSize() throws IOException {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
+ Assert.assertEquals(0, asset.getSize());
+
+ Files.write(assetPathFile, new String("abcdef").getBytes("ASCII"));
+ Assert.assertTrue(asset.getSize()>=6);
+
+
+ }
+
+ @Test
+ public void getData() throws IOException {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
+ Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
+ try(InputStream is = asset.getReadStream()) {
+ Assert.assertEquals("abcdef", IOUtils.toString(is, "ASCII"));
+ }
+
+ }
+
+ @Test
+ public void getDataExceptionOnDir() throws IOException {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathDir);
+ Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
+ try {
+ InputStream is = asset.getReadStream();
+ Assert.assertFalse("Exception expected for data on dir", true);
+ } catch (IOException e) {
+ // fine
+ }
+
+ }
+
+ @Test
+ public void writeData() throws IOException {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
+ Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
+ try(OutputStream os = asset.getWriteStream(true)) {
+ IOUtils.write("test12345", os, "ASCII");
+ }
+ Assert.assertEquals("test12345", IOUtils.toString(assetPathFile.toUri().toURL(), "ASCII"));
+ }
+
+ @Test
+ public void writeDataAppend() throws IOException {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
+ Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
+ try(OutputStream os = asset.getWriteStream(false)) {
+ IOUtils.write("test12345", os, "ASCII");
+ }
+ Assert.assertEquals("abcdeftest12345", IOUtils.toString(assetPathFile.toUri().toURL(), "ASCII"));
+ }
+
+ @Test
+ public void writeDataExceptionOnDir() throws IOException {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathDir);
+ try {
+
+ OutputStream os = asset.getWriteStream(true);
+ Assert.assertTrue("Writing to a directory should throw a IOException", false);
+ } catch (IOException e) {
+ // Fine
+ }
+ }
+
+ @Test
+ public void storeDataFile() throws IOException {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
+ Path dataFile = Files.createTempFile("testdata", "dat");
+ try(OutputStream os = Files.newOutputStream(dataFile)) {
+ IOUtils.write("testkdkdkd", os, "ASCII");
+ }
+ asset.replaceDataFromFile(dataFile);
+ Assert.assertEquals("testkdkdkd", IOUtils.toString(assetPathFile.toUri().toURL(), "ASCII"));
+ }
+
+ @Test
+ public void exists() {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
+ Assert.assertTrue(asset.exists());
+ FilesystemAsset asset2 = new FilesystemAsset(filesystemStorage, "/test1234", Paths.get("abcdefgkdkdk"));
+ Assert.assertFalse(asset2.exists());
+
+ }
+
+ @Test
+ public void getFilePath() {
+ FilesystemAsset asset = new FilesystemAsset(filesystemStorage, "/test1234", assetPathFile);
+ Assert.assertEquals(assetPathFile, asset.getFilePath());
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.apache.archiva.repository.storage.fs;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.common.filelock.DefaultFileLockManager;
+import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+
+public class FilesystemStorageTest {
+
+ private FilesystemStorage fsStorage;
+ private FilesystemAsset file1Asset;
+ private FilesystemAsset dir1Asset;
+ private Path baseDir;
+ private Path file1;
+ private Path dir1;
+
+ @Before
+ public void init() throws IOException {
+ baseDir = Files.createTempDirectory("FsStorageTest");
+ DefaultFileLockManager fl = new DefaultFileLockManager();
+ fsStorage = new FilesystemStorage(baseDir,fl);
+ Files.createDirectories(baseDir.resolve("dir1"));
+ Files.createDirectories(baseDir.resolve("dir2"));
+ file1 = Files.createFile(baseDir.resolve("dir1/testfile1.dat"));
+ dir1 = Files.createDirectories(baseDir.resolve("dir1/testdir"));
+ file1Asset = new FilesystemAsset(fsStorage, "/dir1/testfile1.dat", file1);
+ dir1Asset = new FilesystemAsset(fsStorage, "/dir1/testdir", dir1);
+ }
+
+ private class StringResult {
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ String data;
+ }
+
+
+ @After
+ public void cleanup() {
+ FileUtils.deleteQuietly(file1.toFile());
+ FileUtils.deleteQuietly(dir1.toFile());
+ FileUtils.deleteQuietly(baseDir.resolve("dir1").toFile());
+ FileUtils.deleteQuietly(baseDir.resolve("dir2").toFile());
+ FileUtils.deleteQuietly(baseDir.toFile());
+ }
+
+
+
+
+ @Test
+ public void consumeData() throws IOException {
+ try(OutputStream os = Files.newOutputStream(file1)) {
+ IOUtils.write("abcdefghijkl", os, "ASCII");
+ }
+ StringResult result = new StringResult();
+ fsStorage.consumeData(file1Asset, is -> consume(is, result), false );
+ Assert.assertEquals("abcdefghijkl" ,result.getData());
+ }
+
+ private void consume(InputStream is, StringResult result) {
+ try {
+ result.setData(IOUtils.toString(is, "ASCII"));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ @Test
+ public void getAsset() {
+ StorageAsset asset = fsStorage.getAsset("/dir1/testfile1.dat");
+ Assert.assertEquals(file1, asset.getFilePath());
+ }
+
+ @Test
+ public void addAsset() {
+ StorageAsset newAsset = fsStorage.addAsset("dir2/test", false);
+ Assert.assertNotNull(newAsset);
+ Assert.assertFalse(newAsset.isContainer());
+ Assert.assertFalse(newAsset.exists());
+
+ StorageAsset newDirAsset = fsStorage.addAsset("/dir2/testdir2", true);
+ Assert.assertNotNull(newDirAsset);
+ Assert.assertTrue(newDirAsset.isContainer());
+ Assert.assertFalse(newDirAsset.exists());
+ }
+
+ @Test
+ public void removeAsset() throws IOException {
+ Assert.assertTrue(Files.exists(file1));
+ fsStorage.removeAsset(file1Asset);
+ Assert.assertFalse(Files.exists(file1));
+
+ Assert.assertTrue(Files.exists(dir1));
+ fsStorage.removeAsset(dir1Asset);
+ Assert.assertFalse(Files.exists(dir1));
+ }
+
+ @Test
+ public void moveAsset() throws IOException {
+ Path newFile=null;
+ Path newDir=null;
+ try {
+ Assert.assertTrue(Files.exists(file1));
+ try (OutputStream os = Files.newOutputStream(file1)) {
+ IOUtils.write("testakdkkdkdkdk", os, "ASCII");
+ }
+ long fileSize = Files.size(file1);
+ fsStorage.moveAsset(file1Asset, "/dir2/testfile2.dat");
+ Assert.assertFalse(Files.exists(file1));
+ newFile = baseDir.resolve("dir2/testfile2.dat");
+ Assert.assertTrue(Files.exists(newFile));
+ Assert.assertEquals(fileSize, Files.size(newFile));
+
+
+ Assert.assertTrue(Files.exists(dir1));
+ newDir = baseDir.resolve("dir2/testdir2");
+ fsStorage.moveAsset(dir1Asset, "dir2/testdir2");
+ Assert.assertFalse(Files.exists(dir1));
+ Assert.assertTrue(Files.exists(newDir));
+ } finally {
+ if (newFile!=null) Files.deleteIfExists(newFile);
+ if (newDir!=null) Files.deleteIfExists(newDir);
+ }
+ }
+
+ @Test
+ public void copyAsset() throws IOException {
+ Path newFile=null;
+ Path newDir=null;
+ try {
+ Assert.assertTrue(Files.exists(file1));
+ try (OutputStream os = Files.newOutputStream(file1)) {
+ IOUtils.write("testakdkkdkdkdk", os, "ASCII");
+ }
+ long fileSize = Files.size(file1);
+ fsStorage.copyAsset(file1Asset, "/dir2/testfile2.dat", StandardCopyOption.REPLACE_EXISTING);
+ Assert.assertTrue(Files.exists(file1));
+ Assert.assertEquals(fileSize, Files.size(file1));
+ newFile = baseDir.resolve("dir2/testfile2.dat");
+ Assert.assertTrue(Files.exists(newFile));
+ Assert.assertEquals(fileSize, Files.size(newFile));
+
+ try {
+ fsStorage.copyAsset(file1Asset, "/dir2/testfile2.dat");
+ Assert.assertTrue("IOException should be thrown (File exists)", false);
+ } catch (IOException ex) {
+ Assert.assertTrue("Exception must contain 'file exists'", ex.getMessage().contains("file exists"));
+ }
+
+ Assert.assertTrue(Files.exists(dir1));
+ newDir = baseDir.resolve("dir2/testdir2");
+ fsStorage.copyAsset(dir1Asset, "dir2/testdir2");
+ Assert.assertTrue(Files.exists(dir1));
+ Assert.assertTrue(Files.exists(newDir));
+ } finally {
+ if (newFile!=null) Files.deleteIfExists(newFile);
+ if (newDir!=null) FileUtils.deleteQuietly(newDir.toFile());
+ }
+ }
+}
\ No newline at end of file
*/
import org.apache.archiva.common.filelock.DefaultFileLockManager;
-import org.apache.archiva.common.filelock.FileLockManager;
import org.apache.archiva.indexer.ArchivaIndexingContext;
import org.apache.archiva.repository.Repository;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.maven.index.context.IndexingContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
-import java.net.URI;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import org.apache.archiva.repository.Repository;
import org.apache.archiva.repository.RepositoryType;
import org.apache.archiva.repository.UnsupportedRepositoryTypeException;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.RepositoryStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.repository.features.IndexCreationFeature;
import org.apache.archiva.common.filelock.DefaultFileLockManager;
import org.apache.archiva.common.utils.VersionUtil;
import org.apache.archiva.configuration.ProxyConnectorConfiguration;
-import org.apache.archiva.maven2.metadata.MavenMetadataReader;
import org.apache.archiva.model.ArchivaRepositoryMetadata;
import org.apache.archiva.model.Plugin;
import org.apache.archiva.model.ProjectReference;
import org.apache.archiva.repository.metadata.base.MetadataTools;
import org.apache.archiva.repository.metadata.RepositoryMetadataException;
import org.apache.archiva.repository.metadata.base.RepositoryMetadataWriter;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.wagon.TransferFailedException;
import org.easymock.EasyMock;
import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.xmlunit.builder.DiffBuilder;
import org.xmlunit.diff.Diff;
import org.xmlunit.diff.Difference;
import org.apache.archiva.repository.content.Namespace;
import org.apache.archiva.repository.content.Project;
import org.apache.archiva.repository.content.Version;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.RepositoryStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.commons.lang3.StringUtils;
import org.apache.archiva.indexer.ArchivaIndexingContext;
import org.apache.archiva.repository.*;
import org.apache.archiva.repository.base.AbstractManagedRepository;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.content.maven2.MavenRepositoryRequestInfo;
import org.apache.archiva.repository.features.ArtifactCleanupFeature;
import org.apache.archiva.repository.features.IndexCreationFeature;
import org.apache.archiva.repository.RepositoryType;
import org.apache.archiva.repository.StandardCapabilities;
import org.apache.archiva.repository.UnsupportedFeatureException;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.features.IndexCreationFeature;
import org.apache.archiva.repository.features.RemoteIndexFeature;
import org.apache.archiva.repository.features.RepositoryFeature;
import org.apache.archiva.common.filelock.FileLockManager;
import org.apache.archiva.repository.*;
import org.apache.archiva.repository.base.AbstractRepositoryGroup;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.features.IndexCreationFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.archiva.repository.features.StagingRepositoryFeature;
import org.apache.archiva.repository.base.BasicManagedRepository;
import org.apache.archiva.repository.base.PasswordCredentials;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.archiva.repository.RepositoryType;
import org.apache.archiva.repository.metadata.RepositoryMetadataException;
import org.apache.archiva.repository.metadata.base.RepositoryMetadataWriter;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.apache.archiva.repository.Repository;
import org.apache.archiva.repository.RepositoryType;
import org.apache.archiva.repository.UnsupportedRepositoryTypeException;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.repository.features.IndexCreationFeature;
import org.apache.archiva.repository.features.RemoteIndexFeature;
import org.apache.archiva.common.filelock.DefaultFileLockManager;
import org.apache.archiva.indexer.ArchivaIndexingContext;
import org.apache.archiva.repository.Repository;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.maven.index.context.IndexingContext;
import org.apache.archiva.repository.Repository;
import org.apache.archiva.repository.RepositoryType;
import org.apache.archiva.repository.UnsupportedRepositoryTypeException;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.repository.features.IndexCreationFeature;
import org.apache.archiva.repository.features.RemoteIndexFeature;
import org.apache.archiva.common.filelock.DefaultFileLockManager;
import org.apache.archiva.indexer.ArchivaIndexingContext;
import org.apache.archiva.repository.Repository;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.maven.index.context.IndexingContext;
import java.io.IOException;
-import java.net.URI;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.sql.Date;
import org.apache.archiva.repository.RepositoryRegistry;
import org.apache.archiva.repository.metadata.MetadataReader;
import org.apache.archiva.repository.metadata.base.MetadataTools;
-import org.apache.archiva.repository.storage.FsStorageUtil;
+import org.apache.archiva.repository.storage.fs.FsStorageUtil;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.rest.api.model.*;
import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
import org.apache.archiva.repository.RepositoryNotFoundException;
import org.apache.archiva.repository.RepositoryRegistry;
import org.apache.archiva.repository.RepositoryType;
-import org.apache.archiva.repository.storage.FsStorageUtil;
+import org.apache.archiva.repository.storage.fs.FsStorageUtil;
import org.apache.archiva.repository.storage.RepositoryStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.metadata.audit.RepositoryListener;
import junit.framework.TestCase;
import org.apache.archiva.common.filelock.DefaultFileLockManager;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.rest.api.model.ArtifactContentEntry;
import org.apache.archiva.test.utils.ArchivaBlockJUnit4ClassRunner;
import org.junit.Test;
*/
import org.apache.archiva.common.filelock.DefaultFileLockManager;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.easymock.TestSubject;
import org.junit.Test;
import org.apache.archiva.repository.base.BasicManagedRepository;
import org.apache.archiva.repository.Repository;
import org.apache.archiva.repository.RepositoryRegistry;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.rss.RssFeedGenerator;
import org.apache.archiva.test.utils.ArchivaBlockJUnit4ClassRunner;
import org.easymock.EasyMock;
import org.apache.archiva.configuration.ArchivaConfiguration;
import org.apache.archiva.repository.base.BasicManagedRepository;
import org.apache.archiva.repository.RepositoryRegistry;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
import org.apache.commons.codec.Encoder;
import org.apache.commons.codec.binary.Base64;
import org.apache.archiva.repository.RepositoryGroup;
import org.apache.archiva.repository.RepositoryRegistry;
import org.apache.archiva.repository.RepositoryRequestInfo;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
import org.apache.archiva.repository.storage.StorageAsset;
import org.apache.archiva.metadata.audit.AuditListener;
import org.apache.archiva.repository.features.IndexCreationFeature;
import org.apache.archiva.common.utils.FileUtils;
import org.apache.archiva.repository.LayoutException;
import org.apache.archiva.repository.RepositoryRegistry;
-import org.apache.archiva.repository.storage.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
import org.apache.archiva.metadata.audit.AuditListener;
import org.apache.archiva.repository.maven2.MavenManagedRepository;
import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;