add files API and provders
This commit is contained in:
parent
6eca3c6d2b
commit
2a724f1b37
22 changed files with 1374 additions and 2 deletions
8
files-api/src/main/java/module-info.java
Normal file
8
files-api/src/main/java/module-info.java
Normal file
|
@ -0,0 +1,8 @@
|
|||
import org.xbib.files.DefaultFilesProvider;
|
||||
import org.xbib.files.Provider;
|
||||
|
||||
module org.xbib.files {
|
||||
exports org.xbib.files;
|
||||
uses Provider;
|
||||
provides Provider with DefaultFilesProvider;
|
||||
}
|
332
files-api/src/main/java/org/xbib/files/DefaultFiles.java
Normal file
332
files-api/src/main/java/org/xbib/files/DefaultFiles.java
Normal file
|
@ -0,0 +1,332 @@
|
|||
package org.xbib.files;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.nio.file.attribute.PosixFileAttributeView;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.nio.file.attribute.UserPrincipal;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class DefaultFiles implements org.xbib.files.Files {
|
||||
|
||||
private static final int READ_BUFFER_SIZE = 128 * 1024;
|
||||
|
||||
private static final int WRITE_BUFFER_SIZE = 128 * 1024;
|
||||
|
||||
private static final Set<PosixFilePermission> DEFAULT_DIR_PERMISSIONS =
|
||||
PosixFilePermissions.fromString("rwxr-xr-x");
|
||||
|
||||
private static final Set<PosixFilePermission> DEFAULT_FILE_PERMISSIONS =
|
||||
PosixFilePermissions.fromString("rw-r--r--");
|
||||
|
||||
private final URI uri;
|
||||
|
||||
private final Map<String, ?> env;
|
||||
|
||||
public DefaultFiles(URI uri, Map<String, ?> env) {
|
||||
this.uri = uri;
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean exists(String path) {
|
||||
return Files.exists(Paths.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isExecutable(String path) {
|
||||
return Files.isExecutable(Paths.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isDirectory(String path) {
|
||||
return Files.isDirectory(Paths.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isRegularFile(String path) {
|
||||
return Files.isRegularFile(Paths.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isHidden(String path) throws IOException {
|
||||
return Files.isHidden(Paths.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isSameFile(String path1, String path2) throws IOException {
|
||||
return Files.isSameFile(Paths.get(path1), Paths.get(path2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isSymbolicLink(String path) {
|
||||
return Files.isSymbolicLink(Paths.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isReadable(String path) {
|
||||
return Files.isReadable(Paths.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isWritable(String path) {
|
||||
return Files.isWritable(Paths.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String path, String attribute, Object value) throws IOException {
|
||||
Files.setAttribute(Paths.get(path), attribute, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String path, String attribute) throws IOException {
|
||||
return Files.getAttribute(Paths.get(path), attribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPermissions(String path, Set<PosixFilePermission> permissions) throws IOException {
|
||||
Files.setPosixFilePermissions(Paths.get(path), permissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PosixFilePermission> getPermissions(String path) throws IOException {
|
||||
return Files.getPosixFilePermissions(Paths.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwner(String path, UserPrincipal userPrincipal) throws IOException {
|
||||
Files.setOwner(Paths.get(path), userPrincipal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserPrincipal getOwner(String path) throws IOException {
|
||||
return Files.getOwner(Paths.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModifiedTime(String path, FileTime fileTime) throws IOException {
|
||||
Files.setLastModifiedTime(Paths.get(path), fileTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime getLastModifiedTime(String path) throws IOException {
|
||||
return Files.getLastModifiedTime(Paths.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createFile(String path, FileAttribute<?>... attributes) throws IOException {
|
||||
Files.createFile(Paths.get(path), attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectory(String path, FileAttribute<?>... attributes) throws IOException {
|
||||
Files.createDirectory(Paths.get(path), attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectories(String path, FileAttribute<?>... attributes) throws IOException {
|
||||
Files.createDirectories(Paths.get(path), attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(Path source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, target, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(Path source, String target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, Paths.get(target), DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(InputStream source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, target, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(InputStream source, String target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, Paths.get(target), DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(Path source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
download(source, target, READ_BUFFER_SIZE, copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(String source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
download(Paths.get(source), target, READ_BUFFER_SIZE, copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(Path source, OutputStream target) throws IOException {
|
||||
download(source, target, READ_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(String source, OutputStream target) throws IOException {
|
||||
download(Paths.get(source), target, READ_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String source, String target, CopyOption... copyOptions) throws IOException {
|
||||
Files.move(Paths.get(source), Paths.get(target), copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String source) throws IOException {
|
||||
Files.deleteIfExists(Paths.get(source));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(String source, String target, CopyOption... copyOptions) throws IOException {
|
||||
Files.copy(Paths.get(source), Paths.get(target), copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> stream(String path, String glob) throws IOException {
|
||||
return Files.newDirectoryStream(Paths.get(path), glob);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> filter(String path, DirectoryStream.Filter<Path> filter) throws IOException {
|
||||
return Files.newDirectoryStream(Paths.get(path), filter);
|
||||
}
|
||||
|
||||
private void upload(Path source, Path target,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
upload(Files.newByteChannel(source), target, WRITE_BUFFER_SIZE, dirPerms, filePerms, copyOptions);
|
||||
}
|
||||
|
||||
private void upload(InputStream source, Path target,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
upload(Channels.newChannel(source), target, WRITE_BUFFER_SIZE, dirPerms, filePerms, copyOptions);
|
||||
}
|
||||
|
||||
private void upload(ReadableByteChannel source,
|
||||
Path target,
|
||||
int bufferSize,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
prepareForWrite(target, dirPerms, filePerms);
|
||||
transfer(source, Files.newByteChannel(target, prepareWriteOptions(copyOptions)), bufferSize);
|
||||
}
|
||||
|
||||
private void download(Path source,
|
||||
OutputStream outputStream,
|
||||
int bufferSize) throws IOException {
|
||||
download(source, Channels.newChannel(outputStream), bufferSize);
|
||||
}
|
||||
|
||||
private void download(Path source,
|
||||
WritableByteChannel writableByteChannel,
|
||||
int bufferSize) throws IOException {
|
||||
transfer(Files.newByteChannel(source, prepareReadOptions()), writableByteChannel,
|
||||
bufferSize);
|
||||
}
|
||||
|
||||
private void download(Path source,
|
||||
Path target,
|
||||
int bufferSize,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
prepareForWrite(target);
|
||||
transfer(Files.newByteChannel(source, prepareReadOptions(copyOptions)),
|
||||
Files.newByteChannel(target, prepareWriteOptions(copyOptions)), bufferSize);
|
||||
}
|
||||
|
||||
private void prepareForWrite(Path path) throws IOException {
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
Path parent = path.getParent();
|
||||
if (parent != null) {
|
||||
if (!Files.exists(parent)) {
|
||||
Files.createDirectories(parent);
|
||||
}
|
||||
}
|
||||
if (!Files.exists(path)) {
|
||||
Files.createFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareForWrite(Path path,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms) throws IOException {
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
Path parent = path.getParent();
|
||||
if (parent != null) {
|
||||
if (!Files.exists(parent)) {
|
||||
Files.createDirectories(parent);
|
||||
}
|
||||
PosixFileAttributeView posixFileAttributeView =
|
||||
Files.getFileAttributeView(parent, PosixFileAttributeView.class);
|
||||
posixFileAttributeView.setPermissions(dirPerms);
|
||||
}
|
||||
if (!Files.exists(path)) {
|
||||
Files.createFile(path);
|
||||
}
|
||||
PosixFileAttributeView posixFileAttributeView =
|
||||
Files.getFileAttributeView(path, PosixFileAttributeView.class);
|
||||
posixFileAttributeView.setPermissions(filePerms);
|
||||
}
|
||||
|
||||
private Set<? extends OpenOption> prepareReadOptions(CopyOption... copyOptions) {
|
||||
// ignore user copy options
|
||||
return EnumSet.of(StandardOpenOption.READ);
|
||||
}
|
||||
|
||||
private Set<? extends OpenOption> prepareWriteOptions(CopyOption... copyOptions) {
|
||||
Set<? extends OpenOption> options = null;
|
||||
for (CopyOption copyOption : copyOptions) {
|
||||
if (copyOption == StandardCopyOption.REPLACE_EXISTING) {
|
||||
options = EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
}
|
||||
if (options == null) {
|
||||
// we can not use CREATE_NEW, file is already there because of prepareForWrite() -> Files.createFile()
|
||||
options = EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
private void transfer(ReadableByteChannel readableByteChannel,
|
||||
WritableByteChannel writableByteChannel,
|
||||
int bufferSize) throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
|
||||
int read;
|
||||
while ((read = readableByteChannel.read(buffer)) > 0) {
|
||||
buffer.flip();
|
||||
while (read > 0) {
|
||||
read -= writableByteChannel.write(buffer);
|
||||
}
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.xbib.files;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
public class DefaultFilesProvider implements Provider {
|
||||
@Override
|
||||
public Files provide(URI uri, Map<String, ?> env) {
|
||||
return !uri.isAbsolute() || uri.getScheme().equals("file") ? new DefaultFiles(uri, env) : null;
|
||||
}
|
||||
}
|
105
files-api/src/main/java/org/xbib/files/Files.java
Normal file
105
files-api/src/main/java/org/xbib/files/Files.java
Normal file
|
@ -0,0 +1,105 @@
|
|||
package org.xbib.files;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.nio.file.attribute.UserPrincipal;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
|
||||
public interface Files {
|
||||
|
||||
class Holder {
|
||||
|
||||
private static Files createFiles(URI uri, Map<String, ?> env) {
|
||||
ServiceLoader<Provider> serviceLoader = ServiceLoader.load(Provider.class);
|
||||
Optional<Files> first = serviceLoader.stream()
|
||||
.map(ServiceLoader.Provider::get)
|
||||
.filter(Objects::nonNull)
|
||||
.map(p -> p.provide(uri, env))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst();
|
||||
return first.orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
default Files newInstance(URI uri, Map<String, ?> env) {
|
||||
return Holder.createFiles(uri, env);
|
||||
}
|
||||
|
||||
Boolean exists(String path) throws IOException;
|
||||
|
||||
Boolean isExecutable(String path) throws IOException;
|
||||
|
||||
Boolean isDirectory(String path) throws IOException;
|
||||
|
||||
Boolean isRegularFile(String path) throws IOException;
|
||||
|
||||
Boolean isHidden(String path) throws IOException;
|
||||
|
||||
Boolean isSameFile(String path1, String path2) throws IOException;
|
||||
|
||||
Boolean isSymbolicLink(String path) throws IOException;
|
||||
|
||||
Boolean isReadable(String path) throws IOException;
|
||||
|
||||
Boolean isWritable(String path) throws IOException;
|
||||
|
||||
void setAttribute(String path, String attribute, Object value) throws IOException;
|
||||
|
||||
Object getAttribute(String path, String attribute) throws IOException;
|
||||
|
||||
void setPermissions(String path, Set<PosixFilePermission> permissions) throws IOException;
|
||||
|
||||
Set<PosixFilePermission> getPermissions(String path) throws IOException;
|
||||
|
||||
void setOwner(String path, UserPrincipal userPrincipal) throws IOException;
|
||||
|
||||
UserPrincipal getOwner(String path) throws IOException;
|
||||
|
||||
void setLastModifiedTime(String path, FileTime fileTime) throws IOException;
|
||||
|
||||
FileTime getLastModifiedTime(String path) throws IOException;
|
||||
|
||||
void createFile(String path, FileAttribute<?>... attributes) throws IOException;
|
||||
|
||||
void createDirectory(String path, FileAttribute<?>... attributes) throws IOException;
|
||||
|
||||
void createDirectories(String path, FileAttribute<?>... attributes) throws IOException;
|
||||
|
||||
void upload(Path source, Path target, CopyOption... copyOptions) throws IOException;
|
||||
|
||||
void upload(Path source, String target, CopyOption... copyOptions) throws Exception;
|
||||
|
||||
void upload(InputStream source, Path target, CopyOption... copyOptions) throws IOException;
|
||||
|
||||
void upload(InputStream source, String target, CopyOption... copyOptions) throws IOException;
|
||||
|
||||
void download(Path source, Path target, CopyOption... copyOptions) throws IOException;
|
||||
|
||||
void download(String source, Path target, CopyOption... copyOptions) throws IOException;
|
||||
|
||||
void download(Path source, OutputStream target) throws IOException;
|
||||
|
||||
void download(String source, OutputStream target) throws IOException;
|
||||
|
||||
void rename(String source, String target, CopyOption... copyOptions) throws IOException;
|
||||
|
||||
void remove(String source) throws IOException;
|
||||
|
||||
void copy(String source, String target, CopyOption... copyOptions) throws IOException;
|
||||
|
||||
DirectoryStream<Path> stream(String path, String glob) throws IOException;
|
||||
|
||||
DirectoryStream<Path> filter(String path, DirectoryStream.Filter<Path> filter) throws IOException;
|
||||
}
|
9
files-api/src/main/java/org/xbib/files/Provider.java
Normal file
9
files-api/src/main/java/org/xbib/files/Provider.java
Normal file
|
@ -0,0 +1,9 @@
|
|||
package org.xbib.files;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
public interface Provider {
|
||||
|
||||
Files provide(URI uri, Map<String, ?> env);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.xbib.files.DefaultFilesProvider
|
|
@ -1,4 +1,5 @@
|
|||
dependencies {
|
||||
api project(':files-api')
|
||||
api project(':files-ftp')
|
||||
testImplementation "org.mockftpserver:MockFtpServer:${project.property('mockftpserver.version')}"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params:${project.property('junit.version')}"
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import java.nio.file.spi.FileSystemProvider;
|
||||
import org.xbib.files.Provider;
|
||||
import org.xbib.io.ftp.fs.FTPFileSystemProvider;
|
||||
import org.xbib.io.ftp.fs.spi.FTPFilesProvider;
|
||||
|
||||
module org.xbib.files.ftp.fs {
|
||||
requires org.xbib.files;
|
||||
requires org.xbib.files.ftp;
|
||||
provides java.nio.file.spi.FileSystemProvider
|
||||
with org.xbib.io.ftp.fs.FTPFileSystemProvider;
|
||||
provides FileSystemProvider with FTPFileSystemProvider;
|
||||
provides Provider with FTPFilesProvider;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package org.xbib.io.ftp.fs.spi;
|
||||
|
||||
import org.xbib.io.ftp.fs.FTPEnvironment;
|
||||
import org.xbib.io.ftp.fs.FTPFileSystemProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.util.Map;
|
||||
|
||||
class FTPContext {
|
||||
|
||||
final FTPFileSystemProvider provider;
|
||||
|
||||
final FileSystem fileSystem;
|
||||
|
||||
FTPContext(URI uri, Map<String, ?> env) throws IOException {
|
||||
this.provider = new FTPFileSystemProvider();
|
||||
this.fileSystem = provider.newFileSystem(uri, env != null ? env : new FTPEnvironment());
|
||||
}
|
||||
|
||||
void close() throws IOException {
|
||||
fileSystem.close();
|
||||
}
|
||||
}
|
391
files-ftp-fs/src/main/java/org/xbib/io/ftp/fs/spi/FTPFiles.java
Normal file
391
files-ftp-fs/src/main/java/org/xbib/io/ftp/fs/spi/FTPFiles.java
Normal file
|
@ -0,0 +1,391 @@
|
|||
package org.xbib.io.ftp.fs.spi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.nio.file.attribute.PosixFileAttributeView;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.nio.file.attribute.UserPrincipal;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class FTPFiles implements org.xbib.files.Files {
|
||||
|
||||
private static final int READ_BUFFER_SIZE = 128 * 1024;
|
||||
|
||||
private static final int WRITE_BUFFER_SIZE = 128 * 1024;
|
||||
|
||||
private static final Set<PosixFilePermission> DEFAULT_DIR_PERMISSIONS =
|
||||
PosixFilePermissions.fromString("rwxr-xr-x");
|
||||
|
||||
private static final Set<PosixFilePermission> DEFAULT_FILE_PERMISSIONS =
|
||||
PosixFilePermissions.fromString("rw-r--r--");
|
||||
|
||||
private final URI uri;
|
||||
|
||||
private final Map<String, ?> env;
|
||||
|
||||
public FTPFiles(URI uri, Map<String, ?> env) {
|
||||
this.uri = uri;
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean exists(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.exists(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isExecutable(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isExecutable(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isDirectory(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isDirectory(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isRegularFile(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isRegularFile(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isHidden(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isHidden(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isSameFile(String path1, String path2) throws IOException {
|
||||
return performWithContext(ctx -> Files.isSameFile(ctx.fileSystem.getPath(path1), ctx.fileSystem.getPath(path2)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isSymbolicLink(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isSymbolicLink(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isReadable(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isReadable(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isWritable(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isWritable(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createFile(String path, FileAttribute<?>... attributes) throws IOException {
|
||||
performWithContext(ctx -> Files.createFile(ctx.fileSystem.getPath(path), attributes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectory(String path, FileAttribute<?>... attributes) throws IOException {
|
||||
performWithContext(ctx -> Files.createDirectory(ctx.fileSystem.getPath(path), attributes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectories(String path, FileAttribute<?>... attributes) throws IOException {
|
||||
performWithContext(ctx -> Files.createDirectories(ctx.fileSystem.getPath(path), attributes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String path, String attribute, Object value) throws IOException {
|
||||
performWithContext(ctx -> Files.setAttribute(ctx.fileSystem.getPath(path), attribute, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String path, String attribute) throws IOException {
|
||||
return performWithContext(ctx -> Files.getAttribute(ctx.fileSystem.getPath(path), attribute));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPermissions(String path, Set<PosixFilePermission> permissions) throws IOException {
|
||||
performWithContext(ctx -> Files.setPosixFilePermissions(ctx.fileSystem.getPath(path), permissions));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PosixFilePermission> getPermissions(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.getPosixFilePermissions(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModifiedTime(String path, FileTime fileTime) throws IOException {
|
||||
performWithContext(ctx -> Files.setLastModifiedTime(ctx.fileSystem.getPath(path), fileTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime getLastModifiedTime(String path) throws IOException{
|
||||
return performWithContext(ctx -> Files.getLastModifiedTime(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwner(String path, UserPrincipal userPrincipal) throws IOException {
|
||||
performWithContext(ctx -> Files.setOwner(ctx.fileSystem.getPath(path), userPrincipal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserPrincipal getOwner(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.getOwner(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> stream(String path, String glob) throws IOException {
|
||||
return performWithContext(ctx -> Files.newDirectoryStream(ctx.fileSystem.getPath(path), glob));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> filter(String path, DirectoryStream.Filter<Path> filter) throws IOException {
|
||||
return performWithContext(ctx -> Files.newDirectoryStream(ctx.fileSystem.getPath(path), filter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(Path source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, target, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
public void upload(Path source, Path target,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
upload(ctx, Files.newByteChannel(source), target, WRITE_BUFFER_SIZE,
|
||||
dirPerms, filePerms, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void upload(Path source, String target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, target, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
public void upload(Path source, String target,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
upload(ctx, Files.newByteChannel(source), ctx.fileSystem.getPath(target), WRITE_BUFFER_SIZE,
|
||||
dirPerms, filePerms, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void upload(InputStream source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, target, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
public void upload(InputStream source, Path target,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
upload(ctx, Channels.newChannel(source), target, WRITE_BUFFER_SIZE,
|
||||
dirPerms, filePerms, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void upload(InputStream source, String target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, target, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
public void upload(InputStream source, String target,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
upload(ctx, Channels.newChannel(source), ctx.fileSystem.getPath(target), WRITE_BUFFER_SIZE,
|
||||
dirPerms, filePerms, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void download(Path source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
download(ctx, source, target, READ_BUFFER_SIZE, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void download(String source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
download(ctx, ctx.fileSystem.getPath(source), target, READ_BUFFER_SIZE, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void download(Path source, OutputStream target) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
download(ctx, source, target, READ_BUFFER_SIZE);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void download(String source, OutputStream target) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
Files.copy(ctx.fileSystem.getPath(source), target);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void copy(String source, String target, CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
Files.copy(ctx.fileSystem.getPath(source), ctx.fileSystem.getPath(target), copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void rename(String source, String target, CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
Files.move(ctx.fileSystem.getPath(source), ctx.fileSystem.getPath(target), copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void remove(String source) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
Files.deleteIfExists(ctx.fileSystem.getPath(source));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void upload(FTPContext ctx,
|
||||
ReadableByteChannel source,
|
||||
Path target,
|
||||
int bufferSize,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
prepareForWrite(target, dirPerms, filePerms);
|
||||
transfer(source, ctx.provider.newByteChannel(target, prepareWriteOptions(copyOptions)), bufferSize);
|
||||
}
|
||||
|
||||
private void download(FTPContext ctx,
|
||||
Path source,
|
||||
OutputStream outputStream,
|
||||
int bufferSize) throws IOException {
|
||||
download(ctx, source, Channels.newChannel(outputStream), bufferSize);
|
||||
}
|
||||
|
||||
private void download(FTPContext ctx,
|
||||
Path source,
|
||||
WritableByteChannel writableByteChannel,
|
||||
int bufferSize) throws IOException {
|
||||
transfer(ctx.provider.newByteChannel(source, prepareReadOptions()), writableByteChannel,
|
||||
bufferSize);
|
||||
}
|
||||
|
||||
private void download(FTPContext ctx,
|
||||
Path source,
|
||||
Path target,
|
||||
int bufferSize,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
prepareForWrite(target);
|
||||
transfer(ctx.provider.newByteChannel(source, prepareReadOptions(copyOptions)),
|
||||
Files.newByteChannel(target, prepareWriteOptions(copyOptions)), bufferSize);
|
||||
}
|
||||
|
||||
private void prepareForWrite(Path path) throws IOException {
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
Path parent = path.getParent();
|
||||
if (parent != null) {
|
||||
if (!Files.exists(parent)) {
|
||||
Files.createDirectories(parent);
|
||||
}
|
||||
}
|
||||
if (!Files.exists(path)) {
|
||||
Files.createFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareForWrite(Path path,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms) throws IOException {
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
Path parent = path.getParent();
|
||||
if (parent != null) {
|
||||
if (!Files.exists(parent)) {
|
||||
Files.createDirectories(parent);
|
||||
}
|
||||
PosixFileAttributeView posixFileAttributeView =
|
||||
Files.getFileAttributeView(parent, PosixFileAttributeView.class);
|
||||
posixFileAttributeView.setPermissions(dirPerms);
|
||||
}
|
||||
if (!Files.exists(path)) {
|
||||
Files.createFile(path);
|
||||
}
|
||||
PosixFileAttributeView posixFileAttributeView =
|
||||
Files.getFileAttributeView(path, PosixFileAttributeView.class);
|
||||
posixFileAttributeView.setPermissions(filePerms);
|
||||
}
|
||||
|
||||
private Set<? extends OpenOption> prepareReadOptions(CopyOption... copyOptions) {
|
||||
// ignore user copy options
|
||||
return EnumSet.of(StandardOpenOption.READ);
|
||||
}
|
||||
|
||||
private Set<? extends OpenOption> prepareWriteOptions(CopyOption... copyOptions) {
|
||||
Set<? extends OpenOption> options = null;
|
||||
for (CopyOption copyOption : copyOptions) {
|
||||
if (copyOption == StandardCopyOption.REPLACE_EXISTING) {
|
||||
options = EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
}
|
||||
if (options == null) {
|
||||
// we can not use CREATE_NEW, file is already there because of prepareForWrite() -> Files.createFile()
|
||||
options = EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
private void transfer(ReadableByteChannel readableByteChannel,
|
||||
WritableByteChannel writableByteChannel,
|
||||
int bufferSize) throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
|
||||
int read;
|
||||
while ((read = readableByteChannel.read(buffer)) > 0) {
|
||||
buffer.flip();
|
||||
while (read > 0) {
|
||||
read -= writableByteChannel.write(buffer);
|
||||
}
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T performWithContext(WithContext<T> action) throws IOException {
|
||||
FTPContext ctx = null;
|
||||
try {
|
||||
if (uri != null) {
|
||||
ctx = new FTPContext(uri, env);
|
||||
return action.perform(ctx);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
if (ctx != null) {
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.xbib.io.ftp.fs.spi;
|
||||
|
||||
import org.xbib.files.Files;
|
||||
import org.xbib.files.Provider;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
public class FTPFilesProvider implements Provider {
|
||||
@Override
|
||||
public Files provide(URI uri, Map<String, ?> env) {
|
||||
return !uri.isAbsolute() || uri.getScheme().equals("file") ? new FTPFiles(uri, env) : null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package org.xbib.io.ftp.fs.spi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
interface WithContext<T> {
|
||||
T perform(FTPContext ctx) throws IOException;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.xbib.io.ftp.fs.spi.FTPFilesProvider
|
0
files-ftp/build.gradle
Normal file
0
files-ftp/build.gradle
Normal file
|
@ -1,4 +1,5 @@
|
|||
dependencies {
|
||||
api project(':files-api')
|
||||
api project(':files-sftp')
|
||||
testImplementation "org.bouncycastle:bcpkix-jdk15on:${project.property('bouncycastle.version')}"
|
||||
}
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import org.apache.sshd.fs.SftpFileSystemProvider;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import org.xbib.files.Provider;
|
||||
import org.apache.sshd.fs.spi.SFTPFilesProvider;
|
||||
|
||||
module org.xbib.files.sftp.fs {
|
||||
exports org.apache.sshd.fs;
|
||||
exports org.apache.sshd.fs.spi;
|
||||
requires org.xbib.files;
|
||||
requires transitive org.xbib.files.sftp;
|
||||
provides FileSystemProvider with SftpFileSystemProvider;
|
||||
provides Provider with SFTPFilesProvider;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package org.apache.sshd.fs.spi;
|
||||
|
||||
import org.apache.sshd.client.ClientBuilder;
|
||||
import org.apache.sshd.client.SshClient;
|
||||
import org.apache.sshd.fs.SftpFileSystem;
|
||||
import org.apache.sshd.fs.SftpFileSystemProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
class SFTPContext {
|
||||
|
||||
private final SshClient sshClient;
|
||||
|
||||
final SftpFileSystemProvider provider;
|
||||
|
||||
final SftpFileSystem fileSystem;
|
||||
|
||||
SFTPContext(URI uri, Map<String, ?> env) throws IOException {
|
||||
this.sshClient = ClientBuilder.builder().build();
|
||||
Object object = env.get("workers");
|
||||
if (object instanceof Integer) {
|
||||
sshClient.setNioWorkers((Integer) object);
|
||||
} else if (object instanceof String) {
|
||||
sshClient.setNioWorkers(Integer.parseInt((String) object));
|
||||
} else {
|
||||
// we do not require a vast pool of threads
|
||||
sshClient.setNioWorkers(1);
|
||||
}
|
||||
sshClient.start();
|
||||
this.provider = new SftpFileSystemProvider(sshClient);
|
||||
this.fileSystem = provider.newFileSystem(uri, env);
|
||||
}
|
||||
|
||||
void close() throws IOException {
|
||||
sshClient.stop();
|
||||
fileSystem.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,392 @@
|
|||
package org.apache.sshd.fs.spi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.nio.file.attribute.PosixFileAttributeView;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.nio.file.attribute.UserPrincipal;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class SFTPFiles implements org.xbib.files.Files {
|
||||
|
||||
private static final int READ_BUFFER_SIZE = 128 * 1024;
|
||||
|
||||
private static final int WRITE_BUFFER_SIZE = 128 * 1024;
|
||||
|
||||
private static final Set<PosixFilePermission> DEFAULT_DIR_PERMISSIONS =
|
||||
PosixFilePermissions.fromString("rwxr-xr-x");
|
||||
|
||||
private static final Set<PosixFilePermission> DEFAULT_FILE_PERMISSIONS =
|
||||
PosixFilePermissions.fromString("rw-r--r--");
|
||||
|
||||
private final URI uri;
|
||||
|
||||
private final Map<String, ?> env;
|
||||
|
||||
public SFTPFiles(URI uri, Map<String, ?> env) {
|
||||
this.uri = uri;
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean exists(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.exists(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isExecutable(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isExecutable(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isDirectory(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isDirectory(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isRegularFile(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isRegularFile(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isHidden(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isHidden(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isSameFile(String path1, String path2) throws IOException {
|
||||
return performWithContext(ctx -> Files.isSameFile(ctx.fileSystem.getPath(path1), ctx.fileSystem.getPath(path2)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isSymbolicLink(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isSymbolicLink(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isReadable(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isReadable(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isWritable(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.isWritable(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createFile(String path, FileAttribute<?>... attributes) throws IOException {
|
||||
performWithContext(ctx -> Files.createFile(ctx.fileSystem.getPath(path), attributes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectory(String path, FileAttribute<?>... attributes) throws IOException {
|
||||
performWithContext(ctx -> Files.createDirectory(ctx.fileSystem.getPath(path), attributes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectories(String path, FileAttribute<?>... attributes) throws IOException {
|
||||
performWithContext(ctx -> Files.createDirectories(ctx.fileSystem.getPath(path), attributes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String path, String attribute, Object value) throws IOException {
|
||||
performWithContext(ctx -> Files.setAttribute(ctx.fileSystem.getPath(path), attribute, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String path, String attribute) throws IOException {
|
||||
return performWithContext(ctx -> Files.getAttribute(ctx.fileSystem.getPath(path), attribute));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPermissions(String path, Set<PosixFilePermission> permissions) throws IOException {
|
||||
performWithContext(ctx -> Files.setPosixFilePermissions(ctx.fileSystem.getPath(path), permissions));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PosixFilePermission> getPermissions(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.getPosixFilePermissions(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModifiedTime(String path, FileTime fileTime) throws IOException {
|
||||
performWithContext(ctx -> Files.setLastModifiedTime(ctx.fileSystem.getPath(path), fileTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime getLastModifiedTime(String path) throws IOException{
|
||||
return performWithContext(ctx -> Files.getLastModifiedTime(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwner(String path, UserPrincipal userPrincipal) throws IOException {
|
||||
performWithContext(ctx -> Files.setOwner(ctx.fileSystem.getPath(path), userPrincipal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserPrincipal getOwner(String path) throws IOException {
|
||||
return performWithContext(ctx -> Files.getOwner(ctx.fileSystem.getPath(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> stream(String path, String glob) throws IOException {
|
||||
return performWithContext(ctx -> Files.newDirectoryStream(ctx.fileSystem.getPath(path), glob));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> filter(String path, DirectoryStream.Filter<Path> filter) throws IOException {
|
||||
return performWithContext(ctx -> Files.newDirectoryStream(ctx.fileSystem.getPath(path), filter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(Path source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, target, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(Path source, String target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, target, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(InputStream source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, target, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(InputStream source, String target, CopyOption... copyOptions) throws IOException {
|
||||
upload(source, target, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, copyOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(Path source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
download(ctx, source, target, READ_BUFFER_SIZE, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(String source, Path target, CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
download(ctx, ctx.fileSystem.getPath(source), target, READ_BUFFER_SIZE, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(Path source, OutputStream target) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
download(ctx, source, target, READ_BUFFER_SIZE);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(String source, OutputStream target) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
download(ctx, ctx.fileSystem.getPath(source), target, READ_BUFFER_SIZE);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(String source, String target, CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> Files.copy(ctx.fileSystem.getPath(source), ctx.fileSystem.getPath(target), copyOptions));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String source, String target, CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> Files.move(ctx.fileSystem.getPath(source), ctx.fileSystem.getPath(target), copyOptions));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String source) throws IOException {
|
||||
performWithContext(ctx -> Files.deleteIfExists(ctx.fileSystem.getPath(source)));
|
||||
}
|
||||
|
||||
public void upload(Path source, Path target,
|
||||
Set<PosixFilePermission> dirPermissions,
|
||||
Set<PosixFilePermission> filePermissions,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
upload(ctx, Files.newByteChannel(source), target, WRITE_BUFFER_SIZE,
|
||||
dirPermissions, filePermissions, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void upload(Path source, String target,
|
||||
Set<PosixFilePermission> dirPermissions,
|
||||
Set<PosixFilePermission> filePermissions,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
upload(ctx, Files.newByteChannel(source), ctx.fileSystem.getPath(target), WRITE_BUFFER_SIZE,
|
||||
dirPermissions, filePermissions, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void upload(InputStream source, Path target,
|
||||
Set<PosixFilePermission> dirPermissions,
|
||||
Set<PosixFilePermission> filePermissions,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
upload(ctx, Channels.newChannel(source), target, WRITE_BUFFER_SIZE,
|
||||
dirPermissions, filePermissions, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void upload(InputStream source, String target,
|
||||
Set<PosixFilePermission> dirPermissions,
|
||||
Set<PosixFilePermission> filePermissions,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
performWithContext(ctx -> {
|
||||
upload(ctx, Channels.newChannel(source), ctx.fileSystem.getPath(target), WRITE_BUFFER_SIZE,
|
||||
dirPermissions, filePermissions, copyOptions);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void upload(SFTPContext ctx,
|
||||
ReadableByteChannel source,
|
||||
Path target,
|
||||
int bufferSize,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
prepareForWrite(target, dirPerms, filePerms);
|
||||
transfer(source, ctx.provider.newByteChannel(target, prepareWriteOptions(copyOptions)), bufferSize);
|
||||
}
|
||||
|
||||
private void download(SFTPContext ctx,
|
||||
Path source,
|
||||
OutputStream outputStream,
|
||||
int bufferSize) throws IOException {
|
||||
download(ctx, source, Channels.newChannel(outputStream), bufferSize);
|
||||
}
|
||||
|
||||
private void download(SFTPContext ctx,
|
||||
Path source,
|
||||
WritableByteChannel writableByteChannel,
|
||||
int bufferSize) throws IOException {
|
||||
transfer(ctx.provider.newByteChannel(source, prepareReadOptions()), writableByteChannel,
|
||||
bufferSize);
|
||||
}
|
||||
|
||||
private void download(SFTPContext ctx,
|
||||
Path source,
|
||||
Path target,
|
||||
int bufferSize,
|
||||
CopyOption... copyOptions) throws IOException {
|
||||
prepareForRead(target);
|
||||
transfer(ctx.provider.newByteChannel(source, prepareReadOptions(copyOptions)),
|
||||
Files.newByteChannel(target, prepareWriteOptions(copyOptions)), bufferSize);
|
||||
}
|
||||
|
||||
private void prepareForRead(Path path) throws IOException {
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
Path parent = path.getParent();
|
||||
if (parent != null) {
|
||||
if (!Files.exists(parent)) {
|
||||
Files.createDirectories(parent);
|
||||
}
|
||||
}
|
||||
if (!Files.exists(path)) {
|
||||
Files.createFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareForWrite(Path path,
|
||||
Set<PosixFilePermission> dirPerms,
|
||||
Set<PosixFilePermission> filePerms) throws IOException {
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
Path parent = path.getParent();
|
||||
if (parent != null) {
|
||||
if (!Files.exists(parent)) {
|
||||
Files.createDirectories(parent);
|
||||
}
|
||||
PosixFileAttributeView posixFileAttributeView =
|
||||
Files.getFileAttributeView(parent, PosixFileAttributeView.class);
|
||||
posixFileAttributeView.setPermissions(dirPerms);
|
||||
}
|
||||
if (!Files.exists(path)) {
|
||||
Files.createFile(path);
|
||||
}
|
||||
PosixFileAttributeView posixFileAttributeView =
|
||||
Files.getFileAttributeView(path, PosixFileAttributeView.class);
|
||||
posixFileAttributeView.setPermissions(filePerms);
|
||||
}
|
||||
|
||||
private Set<? extends OpenOption> prepareReadOptions(CopyOption... copyOptions) {
|
||||
// ignore user copy options
|
||||
return EnumSet.of(StandardOpenOption.READ);
|
||||
}
|
||||
|
||||
private Set<? extends OpenOption> prepareWriteOptions(CopyOption... copyOptions) {
|
||||
Set<? extends OpenOption> options = null;
|
||||
for (CopyOption copyOption : copyOptions) {
|
||||
if (copyOption == StandardCopyOption.REPLACE_EXISTING) {
|
||||
options = EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
}
|
||||
if (options == null) {
|
||||
// we can not use CREATE_NEW, file is already there because of prepareForWrite() -> Files.createFile()
|
||||
options = EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
private void transfer(ReadableByteChannel readableByteChannel,
|
||||
WritableByteChannel writableByteChannel,
|
||||
int bufferSize) throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
|
||||
int read;
|
||||
while ((read = readableByteChannel.read(buffer)) > 0) {
|
||||
buffer.flip();
|
||||
while (read > 0) {
|
||||
read -= writableByteChannel.write(buffer);
|
||||
}
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T performWithContext(WithContext<T> action) throws IOException {
|
||||
SFTPContext ctx = null;
|
||||
try {
|
||||
if (uri != null) {
|
||||
ctx = new SFTPContext(uri, env);
|
||||
return action.perform(ctx);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
if (ctx != null) {
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.apache.sshd.fs.spi;
|
||||
|
||||
import org.xbib.files.Files;
|
||||
import org.xbib.files.Provider;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
public class SFTPFilesProvider implements Provider {
|
||||
@Override
|
||||
public Files provide(URI uri, Map<String, ?> env) {
|
||||
return !uri.isAbsolute() || uri.getScheme().equals("file") ? new SFTPFiles(uri, env) : null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package org.apache.sshd.fs.spi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
interface WithContext<T> {
|
||||
T perform(SFTPContext ctx) throws IOException;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.apache.sshd.fs.spi.SFTPFilesProvider
|
|
@ -1,3 +1,4 @@
|
|||
include 'files-api'
|
||||
include 'files-eddsa'
|
||||
include 'files-zlib'
|
||||
include 'files-ftp'
|
||||
|
|
Loading…
Reference in a new issue