Skip to content

Commit 1285419

Browse files
committed
GH-743: Use a singleton SftpFileSystemProvider for the ServiceLoader
When SFTP file systems are created through the standard Java mechanism via java.nio.file.FileSystems.newFileSystem(URI, Map, Classloader), the JDK uses the ServiceLoader to find registered file system providers. It remembers any it can find via the system classloader or via the platform classloader as "installed providers" and creates only exactly one instance of them. When it cannot find a provider matching the URI scheme in the "installed providers" and a classloader is given, it then tries to find a matching file system provider using that classloader. The ServiceLoader instantiates each provider it finds to check its scheme against the URI scheme. But it doesn't remember these file system providers, so if FileSystems.newFileSystem() is called again, another instance of SftpFileSystemProvider got created. When the returned file system is used, the SftpFileSystemProvider creates an SshClient, and an I/O thread pool is set up. If multiple SftpFileSystemProviders get created, there will also be multiple SshClients, and multiple thread pools. Avoid this by registering via the FileSystemProvider SPI only a stateless facade class. All instance of this facade class delegate to a singleton SftpFileSystemProvider.
1 parent 4e820c9 commit 1285419

4 files changed

Lines changed: 185 additions & 2 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
## Bug Fixes
3333

34+
* [GH-743](https://github.com/apache/mina-sshd/issues/743) Ensure the Java `ServiceLoader` use a singleton `SftpFileSystemProvider`
3435
* [GH-879](https://github.com/apache/mina-sshd/issues/879) Close SSH channel gracefully on exception in port forwarding
3536

3637
## New Features

sshd-sftp/src/main/filtered-resources/META-INF/services/java.nio.file.spi.FileSystemProvider

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
## under the License.
1818
##
1919

20-
org.apache.sshd.sftp.client.fs.SftpFileSystemProvider
20+
org.apache.sshd.sftp.client.fs.SftpFileSystemProviderFacade
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.sshd.sftp.client.fs;
20+
21+
import java.io.IOException;
22+
import java.io.InputStream;
23+
import java.io.OutputStream;
24+
import java.net.URI;
25+
import java.nio.channels.FileChannel;
26+
import java.nio.channels.SeekableByteChannel;
27+
import java.nio.file.AccessMode;
28+
import java.nio.file.CopyOption;
29+
import java.nio.file.DirectoryStream;
30+
import java.nio.file.DirectoryStream.Filter;
31+
import java.nio.file.FileStore;
32+
import java.nio.file.FileSystem;
33+
import java.nio.file.LinkOption;
34+
import java.nio.file.OpenOption;
35+
import java.nio.file.Path;
36+
import java.nio.file.attribute.BasicFileAttributes;
37+
import java.nio.file.attribute.FileAttribute;
38+
import java.nio.file.attribute.FileAttributeView;
39+
import java.nio.file.spi.FileSystemProvider;
40+
import java.util.Map;
41+
import java.util.Set;
42+
43+
import org.apache.sshd.sftp.common.SftpConstants;
44+
45+
/**
46+
* SPI-registered SFTP file system provider using a singleton instance.
47+
*
48+
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
49+
*/
50+
public class SftpFileSystemProviderFacade extends FileSystemProvider {
51+
52+
private static final class DefaultSftpFileSystemSingleton {
53+
static final SftpFileSystemProvider INSTANCE = new SftpFileSystemProvider();
54+
55+
private DefaultSftpFileSystemSingleton() {
56+
// No instantiation
57+
}
58+
}
59+
60+
public SftpFileSystemProviderFacade() {
61+
// Nothing
62+
}
63+
64+
@Override
65+
public String getScheme() {
66+
return SftpConstants.SFTP_SUBSYSTEM_NAME;
67+
}
68+
69+
@Override
70+
public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
71+
return DefaultSftpFileSystemSingleton.INSTANCE.newFileSystem(uri, env);
72+
}
73+
74+
@Override
75+
public FileSystem getFileSystem(URI uri) {
76+
return DefaultSftpFileSystemSingleton.INSTANCE.getFileSystem(uri);
77+
}
78+
79+
@Override
80+
public Path getPath(URI uri) {
81+
return DefaultSftpFileSystemSingleton.INSTANCE.getPath(uri);
82+
}
83+
84+
@Override
85+
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
86+
throws IOException {
87+
return DefaultSftpFileSystemSingleton.INSTANCE.newByteChannel(path, options, attrs);
88+
}
89+
90+
@Override
91+
public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
92+
throws IOException {
93+
return DefaultSftpFileSystemSingleton.INSTANCE.newFileChannel(path, options, attrs);
94+
}
95+
96+
@Override
97+
public InputStream newInputStream(Path path, OpenOption... options) throws IOException {
98+
return DefaultSftpFileSystemSingleton.INSTANCE.newInputStream(path, options);
99+
}
100+
101+
@Override
102+
public OutputStream newOutputStream(Path path, OpenOption... options) throws IOException {
103+
return DefaultSftpFileSystemSingleton.INSTANCE.newOutputStream(path, options);
104+
}
105+
106+
@Override
107+
public DirectoryStream<Path> newDirectoryStream(Path dir, Filter<? super Path> filter) throws IOException {
108+
return DefaultSftpFileSystemSingleton.INSTANCE.newDirectoryStream(dir, filter);
109+
}
110+
111+
@Override
112+
public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException {
113+
DefaultSftpFileSystemSingleton.INSTANCE.createSymbolicLink(link, target, attrs);
114+
}
115+
116+
@Override
117+
public Path readSymbolicLink(Path link) throws IOException {
118+
return DefaultSftpFileSystemSingleton.INSTANCE.readSymbolicLink(link);
119+
}
120+
121+
@Override
122+
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
123+
DefaultSftpFileSystemSingleton.INSTANCE.createDirectory(dir, attrs);
124+
}
125+
126+
@Override
127+
public void delete(Path path) throws IOException {
128+
DefaultSftpFileSystemSingleton.INSTANCE.delete(path);
129+
}
130+
131+
@Override
132+
public void copy(Path source, Path target, CopyOption... options) throws IOException {
133+
DefaultSftpFileSystemSingleton.INSTANCE.copy(source, target, options);
134+
}
135+
136+
@Override
137+
public void move(Path source, Path target, CopyOption... options) throws IOException {
138+
DefaultSftpFileSystemSingleton.INSTANCE.move(source, target, options);
139+
}
140+
141+
@Override
142+
public boolean isSameFile(Path path, Path path2) throws IOException {
143+
return DefaultSftpFileSystemSingleton.INSTANCE.isSameFile(path, path2);
144+
}
145+
146+
@Override
147+
public boolean isHidden(Path path) throws IOException {
148+
return DefaultSftpFileSystemSingleton.INSTANCE.isHidden(path);
149+
}
150+
151+
@Override
152+
public FileStore getFileStore(Path path) throws IOException {
153+
return DefaultSftpFileSystemSingleton.INSTANCE.getFileStore(path);
154+
}
155+
156+
@Override
157+
public void checkAccess(Path path, AccessMode... modes) throws IOException {
158+
DefaultSftpFileSystemSingleton.INSTANCE.checkAccess(path, modes);
159+
}
160+
161+
@Override
162+
public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
163+
return DefaultSftpFileSystemSingleton.INSTANCE.getFileAttributeView(path, type, options);
164+
}
165+
166+
@Override
167+
public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options)
168+
throws IOException {
169+
return DefaultSftpFileSystemSingleton.INSTANCE.readAttributes(path, type, options);
170+
}
171+
172+
@Override
173+
public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
174+
return DefaultSftpFileSystemSingleton.INSTANCE.readAttributes(path, attributes, options);
175+
}
176+
177+
@Override
178+
public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
179+
DefaultSftpFileSystemSingleton.INSTANCE.setAttribute(path, attribute, value, options);
180+
}
181+
182+
}

sshd-sftp/src/test/java/org/apache/sshd/sftp/client/fs/SftpFileSystemTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1081,7 +1081,7 @@ void fileSystemProviderServiceEntry() throws IOException {
10811081
}
10821082

10831083
assertFalse(found, "Multiple configurations: " + line);
1084-
assertEquals(SftpFileSystemProvider.class.getName(), line, "Mismatched configuration");
1084+
assertEquals(SftpFileSystemProviderFacade.class.getName(), line, "Mismatched configuration");
10851085
found = true;
10861086
}
10871087
}

0 commit comments

Comments
 (0)