starting work on jadaptive ssh client
This commit is contained in:
parent
0f0f6bfa09
commit
f39c974e74
9 changed files with 625 additions and 0 deletions
5
files-sftp-jadaptive-fs/build.gradle
Normal file
5
files-sftp-jadaptive-fs/build.gradle
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
dependencies {
|
||||||
|
api project(':files-api')
|
||||||
|
api libs.maverick.synergy.client
|
||||||
|
testImplementation libs.net.security
|
||||||
|
}
|
3
files-sftp-jadaptive-fs/src/main/java/module-info.java
Normal file
3
files-sftp-jadaptive-fs/src/main/java/module-info.java
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module org.xbib.files.sftp.jadaptive.fs {
|
||||||
|
requires org.xbib.files;
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package org.xbib.files.sftp.jadaptive.fs;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileStore;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.PathMatcher;
|
||||||
|
import java.nio.file.WatchService;
|
||||||
|
import java.nio.file.attribute.UserPrincipalLookupService;
|
||||||
|
import java.nio.file.spi.FileSystemProvider;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class SftpFileSystem extends FileSystem {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileSystemProvider provider() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOpen() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReadOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSeparator() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Path> getRootDirectories() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<FileStore> getFileStores() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> supportedFileAttributeViews() {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path getPath(String s, String... strings) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PathMatcher getPathMatcher(String s) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserPrincipalLookupService getUserPrincipalLookupService() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WatchService newWatchService() throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
package org.xbib.files.sftp.jadaptive.fs;
|
||||||
|
|
||||||
|
public class SftpFileSystemProvider {
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.xbib.files.sftp.jadaptive.fs.spi;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.xbib.files.sftp.jadaptive.fs.SftpFileSystem;
|
||||||
|
import org.xbib.files.sftp.jadaptive.fs.SftpFileSystemProvider;
|
||||||
|
|
||||||
|
public class SFTPContext implements Closeable {
|
||||||
|
|
||||||
|
final SftpFileSystemProvider provider;
|
||||||
|
|
||||||
|
final SftpFileSystem fileSystem;
|
||||||
|
|
||||||
|
public 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
sshClient.stop();
|
||||||
|
sshClient.close();
|
||||||
|
fileSystem.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,476 @@
|
||||||
|
package org.xbib.files.sftp.jadaptive.fs.spi;
|
||||||
|
|
||||||
|
import org.xbib.files.FileService;
|
||||||
|
import org.xbib.files.FileWalker;
|
||||||
|
import org.xbib.files.WrappedDirectoryStream;
|
||||||
|
|
||||||
|
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.FileVisitOption;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.LinkOption;
|
||||||
|
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.PosixFileAttributes;
|
||||||
|
import java.nio.file.attribute.PosixFilePermission;
|
||||||
|
import java.nio.file.attribute.PosixFilePermissions;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class SFTPFileService implements FileService {
|
||||||
|
|
||||||
|
private static final int 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 SFTPFileService(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, Instant lastModified) throws IOException {
|
||||||
|
performWithContext(ctx -> Files.setLastModifiedTime(ctx.fileSystem.getPath(path), FileTime.from(lastModified)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Instant getLastModifiedTime(String path) throws IOException{
|
||||||
|
return performWithContext(ctx -> Files.getLastModifiedTime(ctx.fileSystem.getPath(path)).toInstant());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPosixFileAttributes(String path,
|
||||||
|
String owner,
|
||||||
|
String group,
|
||||||
|
Instant lastModifiedTime,
|
||||||
|
Instant lastAccessTime,
|
||||||
|
Instant createTime) throws IOException {
|
||||||
|
performWithContext(ctx -> {
|
||||||
|
PosixFileAttributeView view = Files.getFileAttributeView(ctx.fileSystem.getPath(path),
|
||||||
|
PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
|
||||||
|
view.setOwner(ctx.fileSystem.getUserPrincipalLookupService().lookupPrincipalByName(owner));
|
||||||
|
view.setGroup(ctx.fileSystem.getUserPrincipalLookupService().lookupPrincipalByGroupName(group));
|
||||||
|
view.setTimes(FileTime.from(lastModifiedTime),
|
||||||
|
FileTime.from(lastAccessTime),
|
||||||
|
FileTime.from(createTime));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PosixFileAttributes getPosixFileAttributes(String path) throws IOException {
|
||||||
|
return performWithContext(ctx -> Files.getFileAttributeView(ctx.fileSystem.getPath(path),
|
||||||
|
PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS).readAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOwner(String path, String owner) throws IOException {
|
||||||
|
performWithContext(ctx -> Files.setOwner(ctx.fileSystem.getPath(path),
|
||||||
|
ctx.fileSystem.getUserPrincipalLookupService().lookupPrincipalByName(owner)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOwner(String path) throws IOException {
|
||||||
|
return performWithContext(ctx -> Files.getOwner(ctx.fileSystem.getPath(path)).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setGroup(String path, String group) throws IOException {
|
||||||
|
performWithContext(ctx -> {
|
||||||
|
PosixFileAttributeView view = Files.getFileAttributeView(ctx.fileSystem.getPath(path),
|
||||||
|
PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
|
||||||
|
view.setGroup(ctx.fileSystem.getUserPrincipalLookupService().lookupPrincipalByGroupName(group));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getGroup(String path) throws IOException {
|
||||||
|
return performWithContext(ctx -> Files.getFileAttributeView(ctx.fileSystem.getPath(path),
|
||||||
|
PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS).readAttributes().group().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@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(Path source,
|
||||||
|
String target,
|
||||||
|
Set<PosixFilePermission> dirPermissions,
|
||||||
|
Set<PosixFilePermission> filePermissions,
|
||||||
|
CopyOption... copyOptions) throws IOException {
|
||||||
|
performWithContext(ctx -> {
|
||||||
|
doUpload(ctx, Files.newByteChannel(source), ctx.fileSystem.getPath(target),
|
||||||
|
dirPermissions, filePermissions, copyOptions);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@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,
|
||||||
|
Path target,
|
||||||
|
Set<PosixFilePermission> dirPermissions,
|
||||||
|
Set<PosixFilePermission> filePermissions,
|
||||||
|
CopyOption... copyOptions) throws IOException {
|
||||||
|
performWithContext(ctx -> {
|
||||||
|
doUpload(ctx, Files.newByteChannel(source), target, dirPermissions, filePermissions, copyOptions);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 upload(InputStream source,
|
||||||
|
Path 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, copyOptions);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void download(String source,
|
||||||
|
Path target,
|
||||||
|
CopyOption... copyOptions) throws IOException {
|
||||||
|
performWithContext(ctx -> {
|
||||||
|
download(ctx, ctx.fileSystem.getPath(source), target, copyOptions);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void download(Path source,
|
||||||
|
OutputStream target) throws IOException {
|
||||||
|
performWithContext(ctx -> {
|
||||||
|
download(ctx, source, target);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void download(String source,
|
||||||
|
OutputStream target) throws IOException {
|
||||||
|
performWithContext(ctx -> {
|
||||||
|
download(ctx, ctx.fileSystem.getPath(source), target);
|
||||||
|
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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DirectoryStream<Path> stream(String path, String glob) throws IOException {
|
||||||
|
SFTPContext ctx = new SFTPContext(uri, env);
|
||||||
|
return new WrappedDirectoryStream<>(ctx, Files.newDirectoryStream(ctx.fileSystem.getPath(path), glob));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DirectoryStream<Path> stream(String path, DirectoryStream.Filter<Path> filter) throws IOException {
|
||||||
|
SFTPContext ctx = new SFTPContext(uri, env);
|
||||||
|
return new WrappedDirectoryStream<>(ctx, Files.newDirectoryStream(ctx.fileSystem.getPath(path), filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<Path> list(String path) throws IOException {
|
||||||
|
SFTPContext ctx = new SFTPContext(uri, env);
|
||||||
|
return FileWalker.list(new WrappedDirectoryStream<>(ctx, Files.newDirectoryStream(ctx.fileSystem.getPath(path))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<Path> walk(String path, FileVisitOption... options) throws IOException {
|
||||||
|
SFTPContext ctx = new SFTPContext(uri, env);
|
||||||
|
return FileWalker.walk(ctx, ctx.fileSystem.getPath(path), Integer.MAX_VALUE, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<Path> walk(String path, int maxdepth, FileVisitOption... options) throws IOException {
|
||||||
|
SFTPContext ctx = new SFTPContext(uri, env);
|
||||||
|
return FileWalker.walk(ctx, ctx.fileSystem.getPath(path), maxdepth, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void upload(InputStream source,
|
||||||
|
Path target,
|
||||||
|
Set<PosixFilePermission> dirPermissions,
|
||||||
|
Set<PosixFilePermission> filePermissions,
|
||||||
|
CopyOption... copyOptions) throws IOException {
|
||||||
|
performWithContext(ctx -> {
|
||||||
|
doUpload(ctx, Channels.newChannel(source), target,
|
||||||
|
dirPermissions, filePermissions, copyOptions);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void upload(InputStream source,
|
||||||
|
String target,
|
||||||
|
Set<PosixFilePermission> dirPermissions,
|
||||||
|
Set<PosixFilePermission> filePermissions,
|
||||||
|
CopyOption... copyOptions) throws IOException {
|
||||||
|
performWithContext(ctx -> {
|
||||||
|
doUpload(ctx, Channels.newChannel(source), ctx.fileSystem.getPath(target),
|
||||||
|
dirPermissions, filePermissions, copyOptions);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doUpload(SFTPContext ctx,
|
||||||
|
ReadableByteChannel source,
|
||||||
|
Path target,
|
||||||
|
Set<PosixFilePermission> dirPerms,
|
||||||
|
Set<PosixFilePermission> filePerms,
|
||||||
|
CopyOption... copyOptions) throws IOException {
|
||||||
|
prepareForWrite(target, dirPerms, filePerms);
|
||||||
|
transfer(source, ctx.provider.newByteChannel(target, prepareWriteOptions(copyOptions)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void download(SFTPContext ctx,
|
||||||
|
Path source,
|
||||||
|
OutputStream outputStream) throws IOException {
|
||||||
|
download(ctx, source, Channels.newChannel(outputStream));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void download(SFTPContext ctx,
|
||||||
|
Path source,
|
||||||
|
WritableByteChannel writableByteChannel) throws IOException {
|
||||||
|
transfer(ctx.provider.newByteChannel(source, prepareReadOptions()), writableByteChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void download(SFTPContext ctx,
|
||||||
|
Path source,
|
||||||
|
Path target,
|
||||||
|
CopyOption... copyOptions) throws IOException {
|
||||||
|
prepareForRead(target);
|
||||||
|
transfer(ctx.provider.newByteChannel(source, prepareReadOptions(copyOptions)),
|
||||||
|
Files.newByteChannel(target, prepareWriteOptions(copyOptions)));
|
||||||
|
}
|
||||||
|
|
||||||
|
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) throws IOException {
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
|
||||||
|
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,15 @@
|
||||||
|
package org.xbib.files.sftp.jadaptive.fs.spi;
|
||||||
|
|
||||||
|
import org.xbib.files.FileService;
|
||||||
|
import org.xbib.files.FileServiceProvider;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class SFTPFileServiceProvider implements FileServiceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileService provide(URI uri, Map<String, ?> env) {
|
||||||
|
return uri.isAbsolute() && uri.getScheme().equals("sftp") ? new SFTPFileService(uri, env) : null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.xbib.files.sftp.jadaptive.fs.spi;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
interface WithContext<T> {
|
||||||
|
T perform(SFTPContext ctx) throws IOException;
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ dependencyResolutionManagement {
|
||||||
version('gradle', '8.7')
|
version('gradle', '8.7')
|
||||||
version('net', '4.4.0')
|
version('net', '4.4.0')
|
||||||
library('net-security', 'org.xbib', 'net-security').versionRef('net')
|
library('net-security', 'org.xbib', 'net-security').versionRef('net')
|
||||||
|
library('maverick-synergy-client', 'com.sshtools', 'maverick-synergy-client').version('3.1.1')
|
||||||
}
|
}
|
||||||
testLibs {
|
testLibs {
|
||||||
version('junit', '5.10.2')
|
version('junit', '5.10.2')
|
||||||
|
@ -41,5 +42,6 @@ include 'files-ftp-fs'
|
||||||
include 'files-ftp-mock'
|
include 'files-ftp-mock'
|
||||||
include 'files-sftp'
|
include 'files-sftp'
|
||||||
include 'files-sftp-fs'
|
include 'files-sftp-fs'
|
||||||
|
include 'files-sftp-jadaptive-fs'
|
||||||
include 'files-webdav'
|
include 'files-webdav'
|
||||||
include 'files-webdav-fs'
|
include 'files-webdav-fs'
|
||||||
|
|
Loading…
Reference in a new issue