You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2015/03/17 18:35:34 UTC

[4/4] mina-sshd git commit: [SSHD-431] Use a Builder to initialize and create an SftpSubsystem Factory

[SSHD-431] Use a Builder to initialize and create an SftpSubsystem Factory

Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/4039a11a
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/4039a11a
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/4039a11a

Branch: refs/heads/master
Commit: 4039a11ae5ee37a4ee90157123d7c7f0f8b4d434
Parents: eac1704
Author: Guillaume Nodet <gn...@apache.org>
Authored: Tue Mar 17 18:21:19 2015 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Tue Mar 17 18:35:19 2015 +0100

----------------------------------------------------------------------
 .../main/java/org/apache/sshd/SshServer.java    |   4 +-
 .../apache/sshd/server/sftp/SftpSubsystem.java  |  91 ++-----------
 .../sshd/server/sftp/SftpSubsystemFactory.java  | 130 +++++++++++++++++++
 .../server/sftp/UnsupportedAttributePolicy.java |  36 +++++
 .../test/java/org/apache/sshd/ServerTest.java   |   4 +-
 .../org/apache/sshd/SftpFileSystemTest.java     |  12 +-
 .../src/test/java/org/apache/sshd/SftpTest.java |  16 +--
 .../server/sftp/SftpSubsystemFactoryTest.java   |  96 ++++++++++++++
 .../sshd/git/pack/GitPackCommandTest.java       |   3 +-
 .../apache/sshd/git/pgm/GitPgmCommandTest.java  |   3 +-
 10 files changed, 296 insertions(+), 99 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4039a11a/sshd-core/src/main/java/org/apache/sshd/SshServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
index e72750d..a98f5b0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
@@ -64,7 +64,7 @@ import org.apache.sshd.server.session.ServerConnectionService;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.server.session.ServerUserAuthService;
 import org.apache.sshd.server.session.SessionFactory;
-import org.apache.sshd.server.sftp.SftpSubsystem;
+import org.apache.sshd.server.sftp.SftpSubsystemFactory;
 import org.apache.sshd.server.shell.ProcessShellFactory;
 
 /**
@@ -483,7 +483,7 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
                 return new ProcessShellFactory(command.split(" "), ttyOptions).create();
             }
         }).build());
-        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
+        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystemFactory()));
         sshd.start();
 
         Thread.sleep(Long.MAX_VALUE);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4039a11a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
index 721fe8a..77f09b5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
@@ -75,7 +75,6 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.file.FileSystemAware;
 import org.apache.sshd.common.util.Buffer;
 import org.apache.sshd.common.util.IoUtils;
@@ -101,66 +100,6 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
 
     protected final Logger log = LoggerFactory.getLogger(getClass());
 
-    public static class Factory implements NamedFactory<Command> {
-
-        public static final String NAME = "sftp";
-
-    	private final ExecutorService	executors;
-    	private final boolean shutdownExecutor;
-
-    	public Factory() {
-    		this(null);
-    	}
-
-        /**
-         * @param executorService The {@link ExecutorService} to be used by
-         *                        the {@link SftpSubsystem} command when starting execution. If
-         *                        {@code null} then a single-threaded ad-hoc service is used.
-         *                        <B>Note:</B> the service will <U>not</U> be shutdown when the
-         *                        subsystem is closed - unless it is the ad-hoc service, which will be
-         *                        shutdown regardless
-         * @see Factory(ExecutorService, boolean)}
-         */
-        public Factory(ExecutorService executorService) {
-        	this(executorService, false);
-        }
-
-        /**
-         * @param executorService The {@link ExecutorService} to be used by
-         *                        the {@link SftpSubsystem} command when starting execution. If
-         *                        {@code null} then a single-threaded ad-hoc service is used.
-         * @param shutdownOnExit  If {@code true} the {@link ExecutorService#shutdownNow()}
-         *                        will be called when subsystem terminates - unless it is the ad-hoc
-         *                        service, which will be shutdown regardless
-         */
-        public Factory(ExecutorService executorService, boolean shutdownOnExit) {
-        	executors = executorService;
-        	shutdownExecutor = shutdownOnExit;
-        }
-
-        public ExecutorService getExecutorService() {
-        	return executors;
-        }
-        
-        public boolean isShutdownOnExit() {
-        	return shutdownExecutor;
-        }
-
-        public Command create() {
-            return new SftpSubsystem(getExecutorService(), isShutdownOnExit());
-        }
-
-        public String getName() {
-            return NAME;
-        }
-    }
-
-    public enum UnsupportedAttributePolicy {
-        Ignore,
-        Warn,
-        ThrowException
-    }
-
     /**
      * Properties key for the maximum of available open handles per session.
      */
@@ -194,7 +133,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
     private final Map<String, byte[]> extensions = new HashMap<>();
     private final Map<String, Handle> handles = new HashMap<>();
 
-    private UnsupportedAttributePolicy unsupportedAttributePolicy = UnsupportedAttributePolicy.Warn;
+    private final UnsupportedAttributePolicy unsupportedAttributePolicy;
 
     protected static abstract class Handle implements java.io.Closeable {
         private Path file;
@@ -378,22 +317,6 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
         }
     }
 
-    public SftpSubsystem() {
-        this(null);
-    }
-
-    /**
-     * @param executorService The {@link ExecutorService} to be used by
-     *                        the {@link SftpSubsystem} command when starting execution. If
-     *                        {@code null} then a single-threaded ad-hoc service is used.
-     *                        <b>Note:</b> the service will <U>not</U> be shutdown when the
-     *                        subsystem is closed - unless it is the ad-hoc service
-     * @see #SftpSubsystem(ExecutorService, boolean)
-     */
-    public SftpSubsystem(ExecutorService executorService) {
-        this(executorService, false);
-    }
-
     /**
      * @param executorService The {@link ExecutorService} to be used by
      *                        the {@link SftpSubsystem} command when starting execution. If
@@ -401,15 +324,25 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
      * @param shutdownOnExit  If {@code true} the {@link ExecutorService#shutdownNow()}
      *                        will be called when subsystem terminates - unless it is the ad-hoc
      *                        service, which will be shutdown regardless
+     * @param policy The {@link UnsupportedAttributePolicy} to use if failed to access
+     * some local file attributes
      * @see ThreadUtils#newSingleThreadExecutor(String)
      */
-    public SftpSubsystem(ExecutorService executorService, boolean shutdownOnExit) {
+    public SftpSubsystem(ExecutorService executorService, boolean shutdownOnExit, UnsupportedAttributePolicy policy) {
         if ((executors = executorService) == null) {
             executors = ThreadUtils.newSingleThreadExecutor(getClass().getSimpleName());
             shutdownExecutor = true;    // we always close the ad-hoc executor service
         } else {
             shutdownExecutor = shutdownOnExit;
         }
+        
+        if ((unsupportedAttributePolicy=policy) == null) {
+            throw new IllegalArgumentException("No policy provided");
+        }
+    }
+
+    public final UnsupportedAttributePolicy getUnsupportedAttributePolicy() {
+        return unsupportedAttributePolicy;
     }
 
     public void setSession(ServerSession session) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4039a11a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystemFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystemFactory.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystemFactory.java
new file mode 100644
index 0000000..2b98fb3
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystemFactory.java
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+package org.apache.sshd.server.sftp;
+
+import java.util.concurrent.ExecutorService;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.util.ObjectBuilder;
+import org.apache.sshd.server.Command;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpSubsystemFactory implements NamedFactory<Command>, Cloneable {
+    public static final String NAME = "sftp";
+    public static final UnsupportedAttributePolicy DEFAULT_POLICY = UnsupportedAttributePolicy.Warn;
+
+    public static class Builder implements ObjectBuilder<SftpSubsystemFactory> {
+        private final SftpSubsystemFactory factory = new SftpSubsystemFactory();
+
+        public Builder() {
+            super();
+        }
+
+        public Builder withExecutorService(ExecutorService service) {
+            factory.setExecutorService(service);
+            return this;
+        }
+
+        public Builder withShutdownOnExit(boolean shutdown) {
+            factory.setShutdownOnExit(shutdown);
+            return this;
+        }
+
+        public Builder withUnsupportedAttributePolicy(UnsupportedAttributePolicy p) {
+            factory.setUnsupportedAttributePolicy(p);
+            return this;
+        }
+
+        public SftpSubsystemFactory build() {
+            // return a clone so that each invocation returns a different instance - avoid shared instances
+            return factory.clone();
+        }
+    }
+
+    private ExecutorService executors;
+    private boolean shutdownExecutor;
+    private UnsupportedAttributePolicy policy = DEFAULT_POLICY;
+
+    public SftpSubsystemFactory() {
+        super();
+    }
+
+    public String getName() {
+        return NAME;
+    }
+
+    public ExecutorService getExecutorService() {
+        return executors;
+    }
+
+    /**
+     * @param service The {@link ExecutorService} to be used by the {@link SftpSubsystem}
+     *                command when starting execution. If {@code null} then a single-threaded ad-hoc service is used.
+     */
+    public void setExecutorService(ExecutorService service) {
+        executors = service;
+    }
+
+    public boolean isShutdownOnExit() {
+        return shutdownExecutor;
+    }
+
+    /**
+     * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
+     *                       will be called when subsystem terminates - unless it is the ad-hoc service, which
+     *                       will be shutdown regardless
+     */
+    public void setShutdownOnExit(boolean shutdownOnExit) {
+        shutdownExecutor = shutdownOnExit;
+    }
+
+    public UnsupportedAttributePolicy getUnsupportedAttributePolicy() {
+        return policy;
+    }
+
+    /**
+     * @param p The {@link UnsupportedAttributePolicy} to use if failed to access
+     *          some local file attributes
+     */
+    public void setUnsupportedAttributePolicy(UnsupportedAttributePolicy p) {
+        if (p == null) {
+            throw new IllegalArgumentException("No policy provided");
+        }
+
+        policy = p;
+    }
+
+    public Command create() {
+        return new SftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy());
+    }
+
+    @Override
+    public SftpSubsystemFactory clone() {
+        try {
+            return getClass().cast(super.clone());  // shallow clone is good enough
+        } catch (CloneNotSupportedException e) {
+            throw new UnsupportedOperationException("Unexpected clone exception", e);   // unexpected since we implement cloneable
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4039a11a/sshd-core/src/main/java/org/apache/sshd/server/sftp/UnsupportedAttributePolicy.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/UnsupportedAttributePolicy.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/UnsupportedAttributePolicy.java
new file mode 100644
index 0000000..f9a1472
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/UnsupportedAttributePolicy.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package org.apache.sshd.server.sftp;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum UnsupportedAttributePolicy {
+    Ignore,
+    Warn,
+    ThrowException;
+
+    public static final Set<UnsupportedAttributePolicy> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(UnsupportedAttributePolicy.class));
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4039a11a/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
index 8e2ccee..3c7e5bb 100644
--- a/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
@@ -55,7 +55,7 @@ import org.apache.sshd.server.CommandFactory;
 import org.apache.sshd.server.Environment;
 import org.apache.sshd.server.ExitCallback;
 import org.apache.sshd.server.command.ScpCommandFactory;
-import org.apache.sshd.server.sftp.SftpSubsystem;
+import org.apache.sshd.server.sftp.SftpSubsystemFactory;
 import org.apache.sshd.util.BaseTest;
 import org.apache.sshd.util.BogusPasswordAuthenticator;
 import org.apache.sshd.util.EchoShellFactory;
@@ -419,7 +419,7 @@ public class ServerTest extends BaseTest {
         sshd.getProperties().put(SshServer.IDLE_TIMEOUT, "10000");
         sshd.setPort(8001);
         sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
-        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
+        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystemFactory()));
         sshd.setShellFactory(new EchoShellFactory());
         sshd.setCommandFactory(new ScpCommandFactory());
         sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4039a11a/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java b/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
index f550045..2eb9309 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SftpFileSystemTest.java
@@ -18,6 +18,10 @@
  */
 package org.apache.sshd;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.io.File;
 import java.io.IOException;
 import java.net.URI;
@@ -46,7 +50,7 @@ import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.util.OsUtils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.command.ScpCommandFactory;
-import org.apache.sshd.server.sftp.SftpSubsystem;
+import org.apache.sshd.server.sftp.SftpSubsystemFactory;
 import org.apache.sshd.util.BaseTest;
 import org.apache.sshd.util.BogusPasswordAuthenticator;
 import org.apache.sshd.util.EchoShellFactory;
@@ -55,10 +59,6 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
 public class SftpFileSystemTest extends BaseTest {
 
     private SshServer sshd;
@@ -68,7 +68,7 @@ public class SftpFileSystemTest extends BaseTest {
     public void setUp() throws Exception {
         sshd = SshServer.setUpDefaultServer();
         sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
-        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
+        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystemFactory()));
         sshd.setCommandFactory(new ScpCommandFactory());
         sshd.setShellFactory(new EchoShellFactory());
         sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4039a11a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java b/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
index e39bd57..c2b4832 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
@@ -38,7 +38,7 @@ import org.apache.sshd.common.util.Buffer;
 import org.apache.sshd.common.util.OsUtils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.command.ScpCommandFactory;
-import org.apache.sshd.server.sftp.SftpSubsystem;
+import org.apache.sshd.server.sftp.SftpSubsystemFactory;
 import org.apache.sshd.util.BaseTest;
 import org.apache.sshd.util.BogusPasswordAuthenticator;
 import org.apache.sshd.util.EchoShellFactory;
@@ -72,7 +72,7 @@ public class SftpTest extends BaseTest {
     public void setUp() throws Exception {
         sshd = SshServer.setUpDefaultServer();
         sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
-        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
+        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystemFactory()));
         sshd.setCommandFactory(new ScpCommandFactory());
         sshd.setShellFactory(new EchoShellFactory());
         sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());
@@ -125,7 +125,7 @@ public class SftpTest extends BaseTest {
                     h = sftp.open(file, EnumSet.of(SftpClient.OpenMode.Read));
                     // NOTE: on Windows files are always readable
                     // see https://svn.apache.org/repos/asf/harmony/enhanced/java/branches/java6/classlib/modules/luni/src/test/api/windows/org/apache/harmony/luni/tests/java/io/WinFileTest.java
-                    Assert.assertTrue("Empty read should have failed", isWindows);
+                    assertTrue("Empty read should have failed", isWindows);
                     sftp.close(h);
                 } catch (IOException e) {
                     if (isWindows) {
@@ -139,20 +139,20 @@ public class SftpTest extends BaseTest {
                 } catch (IOException e) {
                     // ok
                 }
-        
+
                 try {
                     h = sftp.open(file, EnumSet.of(SftpClient.OpenMode.Truncate));
                     // NOTE: on Windows files are always readable
-                    Assert.assertTrue("Empty truncate should have failed", isWindows);
+                    assertTrue("Empty truncate should have failed", isWindows);
                     sftp.close(h);
                 } catch (IOException e) {
                     // ok
                 }
-        
+
                 // NOTE: on Windows files are always readable
                 int	perms=sftp.stat(file).perms;
                 int	permsMask=S_IWUSR | (isWindows ? 0 : S_IRUSR);
-                Assert.assertEquals("Mismatched permissions - 0x" + Integer.toHexString(perms), 0, (perms & permsMask));
+                assertEquals("Mismatched permissions - 0x" + Integer.toHexString(perms), 0, (perms & permsMask));
         
                 javaFile.setWritable(true, false);
         
@@ -174,7 +174,7 @@ public class SftpTest extends BaseTest {
                 try {
                     h = sftp.open(file, EnumSet.of(SftpClient.OpenMode.Read));
                     // NOTE: on Windows files are always readable
-                    Assert.assertTrue("Data read should have failed", isWindows);
+                    assertTrue("Data read should have failed", isWindows);
                     sftp.close(h);
                 } catch (IOException e) {
                     if (isWindows) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4039a11a/sshd-core/src/test/java/org/apache/sshd/server/sftp/SftpSubsystemFactoryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/sftp/SftpSubsystemFactoryTest.java b/sshd-core/src/test/java/org/apache/sshd/server/sftp/SftpSubsystemFactoryTest.java
new file mode 100644
index 0000000..dcd9fcb
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/server/sftp/SftpSubsystemFactoryTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package org.apache.sshd.server.sftp;
+
+import java.util.concurrent.ExecutorService;
+
+import org.apache.sshd.util.BaseTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpSubsystemFactoryTest extends BaseTest {
+    public SftpSubsystemFactoryTest() {
+        super();
+    }
+
+    /**
+     * Make sure that the builder returns a factory with the default values
+     * if no {@code withXXX} method is invoked
+     */
+    @Test
+    public void testBuilderDefaultFactoryValues() {
+        SftpSubsystemFactory factory = new SftpSubsystemFactory.Builder().build();
+        Assert.assertNull("Mismatched executor", factory.getExecutorService());
+        Assert.assertFalse("Mismatched shutdown state", factory.isShutdownOnExit());
+        Assert.assertSame("Mismatched unsupported attribute policy", SftpSubsystemFactory.DEFAULT_POLICY, factory.getUnsupportedAttributePolicy());
+    }
+
+    /**
+     * Make sure that the builder initializes correctly the built factory
+     */
+    @Test
+    public void testBuilderCorrectlyInitializesFactory() {
+        SftpSubsystemFactory.Builder builder = new SftpSubsystemFactory.Builder();
+        ExecutorService service = dummyExecutor();
+        SftpSubsystemFactory factory = builder.withExecutorService(service)
+                .withShutdownOnExit(true)
+                .build();
+        Assert.assertSame("Mismatched executor", service, factory.getExecutorService());
+        Assert.assertTrue("Mismatched shutdown state", factory.isShutdownOnExit());
+
+        for (UnsupportedAttributePolicy policy : UnsupportedAttributePolicy.VALUES) {
+            SftpSubsystemFactory actual = builder.withUnsupportedAttributePolicy(policy).build();
+            Assert.assertSame("Mismatched unsupported attribute policy", policy, actual.getUnsupportedAttributePolicy());
+        }
+    }
+
+    /**
+     * <UL>
+     * <LI>
+     * Make sure the builder returns new instances on every call to
+     * {@link SftpSubsystemFactory.Builder#build()} method
+     * </LI>
+     * <p/>
+     * <LI>
+     * Make sure values are preserved between successive invocations
+     * of the {@link SftpSubsystemFactory.Builder#build()} method
+     * </LI>
+     * </UL
+     */
+    @Test
+    public void testBuilderUniqueInstance() {
+        SftpSubsystemFactory.Builder builder = new SftpSubsystemFactory.Builder();
+        SftpSubsystemFactory f1 = builder.withExecutorService(dummyExecutor()).build();
+        SftpSubsystemFactory f2 = builder.build();
+        Assert.assertNotSame("No new instance built", f1, f2);
+        Assert.assertSame("Mismatched executors", f1.getExecutorService(), f2.getExecutorService());
+
+        SftpSubsystemFactory f3 = builder.withExecutorService(dummyExecutor()).build();
+        Assert.assertNotSame("Executor service not changed", f1.getExecutorService(), f3.getExecutorService());
+    }
+
+    private static ExecutorService dummyExecutor() {
+        return Mockito.mock(ExecutorService.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4039a11a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
index b27becb..fa3c5ec 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
@@ -30,6 +30,7 @@ import org.apache.sshd.git.util.EchoShellFactory;
 import org.apache.sshd.git.util.Utils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.sftp.SftpSubsystem;
+import org.apache.sshd.server.sftp.SftpSubsystemFactory;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.transport.CredentialsProvider;
 import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
@@ -44,7 +45,7 @@ public class GitPackCommandTest {
         SshServer sshd = SshServer.setUpDefaultServer();
         sshd.setPort(8001);
         sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
-        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
+        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystemFactory()));
         sshd.setShellFactory(new EchoShellFactory());
         sshd.setCommandFactory(new GitPackCommandFactory("target/git/server"));
         sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4039a11a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
index f45f60d..3d13af1 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
@@ -32,6 +32,7 @@ import org.apache.sshd.git.util.EchoShellFactory;
 import org.apache.sshd.git.util.Utils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.sftp.SftpSubsystem;
+import org.apache.sshd.server.sftp.SftpSubsystemFactory;
 import org.eclipse.jgit.api.Git;
 import org.junit.Test;
 
@@ -50,7 +51,7 @@ public class GitPgmCommandTest {
         SshServer sshd = SshServer.setUpDefaultServer();
         sshd.setPort(8001);
         sshd.setKeyPairProvider(Utils.createTestHostKeyProvider());
-        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
+        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystemFactory()));
         sshd.setShellFactory(new EchoShellFactory());
         sshd.setCommandFactory(new GitPgmCommandFactory("target/git/pgm"));
         sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());