You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2015/05/07 13:59:00 UTC

[1/2] mina-sshd git commit: [SSHD-455] Allow users to register "extensions" to some of the builtin factories

Repository: mina-sshd
Updated Branches:
  refs/heads/master cca7cc743 -> 7ccbd0663


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index 345916a..ebc600a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -22,12 +22,15 @@ package org.apache.sshd.common.util;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.EnumSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -220,4 +223,41 @@ public class GenericUtils {
 
         return result;
     }
+
+    @SuppressWarnings("rawtypes")
+    private static final Comparator<Comparable> naturalOrderComparator=new Comparator<Comparable>() {
+        // TODO for JDK-8 use Comparator.naturalOrder() 
+        @Override
+        @SuppressWarnings("unchecked")
+        public int compare(Comparable c1, Comparable c2) {
+            return c1.compareTo(c2);
+        }
+    };
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static final <V extends Comparable<V>> Comparator<V> naturalComparator() {
+        // TODO for JDK-8 use Comparator.naturalOrder() 
+        return (Comparator) naturalOrderComparator;
+    }
+
+    public static final <V extends Comparable<V>> SortedSet<V> asSortedSet(Collection<? extends V> values) {
+        // TODO for JDK-8 use Comparator.naturalOrder() 
+        return asSortedSet(GenericUtils.<V>naturalComparator(), values);
+    }
+
+    /**
+     * @param comp The (non-{@code null}) {@link Comparator} to use
+     * @param values The values to be added (ignored if {@code null))
+     * @return A {@link SortedSet} containing the values (if any) sorted
+     * using the provided comparator
+     */
+    public static final <V> SortedSet<V> asSortedSet(Comparator<? super V> comp, Collection<? extends V> values) {
+        // TODO for JDK-8 return Collections.emptySortedSet()
+        SortedSet<V>    set=new TreeSet<V>(ValidateUtils.checkNotNull(comp, "No comparator", EMPTY_OBJECT_ARRAY));
+        if (size(values) > 0) {
+            set.addAll(values);
+        }
+        
+        return set;
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/test/java/org/apache/sshd/LoadTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/LoadTest.java b/sshd-core/src/test/java/org/apache/sshd/LoadTest.java
index ca07844..cc156b7 100644
--- a/sshd-core/src/test/java/org/apache/sshd/LoadTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/LoadTest.java
@@ -90,6 +90,7 @@ public class LoadTest extends BaseTest {
         final CountDownLatch latch = new CountDownLatch(nbThreads);
         for (int i = 0; i < nbThreads; i++) {
             Runnable r = new Runnable() {
+                @Override
                 public void run() {
                     try {
                         for (int i = 0; i < nbSessionsPerThread; i++) {
@@ -116,7 +117,7 @@ public class LoadTest extends BaseTest {
             FactoryManagerUtils.updateProperty(props, FactoryManager.MAX_PACKET_SIZE, 1024 * 16);
             FactoryManagerUtils.updateProperty(props, FactoryManager.WINDOW_SIZE, 1024 * 8);
             client.setKeyExchangeFactories(Arrays.asList(
-                    SshBuilder.ClientBuilder.getKeyExchangeFactory(BuiltinDHFactories.dhg1)));
+                    SshBuilder.ClientBuilder.DH2KEX.transform(BuiltinDHFactories.dhg1)));
             client.setCipherFactories(Arrays.<NamedFactory<Cipher>>asList(BuiltinCiphers.blowfishcbc));
             client.start();
             try {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
index 848dd20..e9ab522 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
@@ -93,7 +93,7 @@ public class KexTest extends BaseTest {
     }
 
     private void testClient(DHFactory factory) throws Exception {
-        testClient(SshBuilder.ClientBuilder.getKeyExchangeFactory(factory));
+        testClient(SshBuilder.ClientBuilder.DH2KEX.transform(factory));
     }
 
     private void testClient(NamedFactory<KeyExchange> kex) throws Exception {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
index 9abcbf7..b0814fb 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
@@ -42,7 +42,8 @@ public abstract class BaseCipherTest extends BaseTest {
 		super();
 	}
 
-	protected void ensureKeySizeSupported(int bsize, String algorithm, String transformation) throws GeneralSecurityException {
+	@SuppressWarnings("deprecation")
+    protected void ensureKeySizeSupported(int bsize, String algorithm, String transformation) throws GeneralSecurityException {
 		try {
 	        javax.crypto.Cipher	cipher=SecurityUtils.getCipher(transformation);
 	        byte[]				key=new byte[bsize];

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/test/java/org/apache/sshd/common/cipher/BuiltinCiphersTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/BuiltinCiphersTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/BuiltinCiphersTest.java
index 4a70140..1b46453 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/BuiltinCiphersTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/cipher/BuiltinCiphersTest.java
@@ -36,6 +36,7 @@ import org.apache.sshd.common.util.GenericUtils;
 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>
@@ -150,4 +151,72 @@ public class BuiltinCiphersTest extends BaseTest {
             assertListEquals(fullList + "[unsupported]", unknown, missing);
         }
     }
+
+    @Test
+    public void testResolveFactoryOnBuiltinValues() {
+        for (NamedFactory<Cipher> expected : BuiltinCiphers.VALUES) {
+            String                  name=expected.getName();
+            NamedFactory<Cipher>    actual=BuiltinCiphers.resolveFactory(name);
+            Assert.assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testNotAllowedToRegisterBuiltinFactories() {
+        for (CipherFactory expected : BuiltinCiphers.VALUES) {
+            try {
+                BuiltinCiphers.registerExtension(expected);
+                Assert.fail("Unexpected sucess for " + expected.getName());
+            } catch(IllegalArgumentException e) {
+                // expected - ignored
+            }
+        }
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testNotAllowedToOverrideRegisteredFactories() {
+        CipherFactory    expected=Mockito.mock(CipherFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String  name=expected.getName();
+        try {
+            for (int index=1; index <= Byte.SIZE; index++) {
+                BuiltinCiphers.registerExtension(expected);
+                Assert.assertEquals("Unexpected success at attempt #" + index, 1, index);
+            }
+        } finally {
+            BuiltinCiphers.unregisterExtension(name);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnRegisteredExtension() {
+        CipherFactory    expected=Mockito.mock(CipherFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String  name=expected.getName();
+        try {
+            Assert.assertNull("Extension already registered", BuiltinCiphers.resolveFactory(name));
+            BuiltinCiphers.registerExtension(expected);
+
+            NamedFactory<Cipher>    actual=BuiltinCiphers.resolveFactory(name);
+            Assert.assertSame("Mismatched resolved instance", expected, actual);
+        } finally {
+            NamedFactory<Cipher>    actual=BuiltinCiphers.unregisterExtension(name);
+            Assert.assertSame("Mismatched unregistered instance", expected, actual);
+            Assert.assertNull("Extension not un-registered", BuiltinCiphers.resolveFactory(name));
+        }
+    }
+
+    @Test
+    public void testFac2NamedTransformer() {
+        Assert.assertNull("Invalid null transformation", CipherFactory.FAC2NAMED.transform(null));
+        for (CipherFactory expected : BuiltinCiphers.VALUES) {
+            NamedFactory<Cipher>   actual=CipherFactory.FAC2NAMED.transform(expected);
+            Assert.assertSame("Mismatched transformed instance for " + expected.getName(), expected, actual);
+        }
+        
+        CipherFactory   mock=Mockito.mock(CipherFactory.class);
+        Assert.assertSame("Mismatched transformed mocked instance", mock, CipherFactory.FAC2NAMED.transform(mock));
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
index 7f9528c..601b28f 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
@@ -20,12 +20,22 @@
 package org.apache.sshd.common.compression;
 
 import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.EnumSet;
+import java.util.List;
+import java.util.Random;
 import java.util.Set;
 
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.compression.BuiltinCompressions.ParseResult;
+import org.apache.sshd.common.util.GenericUtils;
 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>
@@ -60,4 +70,108 @@ public class BuiltinCompressionsTest extends BaseTest {
         
         Assert.assertEquals("Incomplete coverage", BuiltinCompressions.VALUES, avail);
     }
+
+    @Test
+    public void testParseCompressionsList() {
+        List<String>    builtin=NamedResource.Utils.getNameList(BuiltinCompressions.VALUES);
+        List<String>    unknown=Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
+        Random          rnd=new Random();
+        for (int index=0; index < (builtin.size() + unknown.size()); index++) {
+            Collections.shuffle(builtin, rnd);
+            Collections.shuffle(unknown, rnd);
+            
+            List<String>    weavedList=new ArrayList<String>(builtin.size() + unknown.size());
+            for (int bIndex=0, uIndex=0; (bIndex < builtin.size()) || (uIndex < unknown.size()); ) {
+                boolean useBuiltin=false;
+                if (bIndex < builtin.size()) {
+                    useBuiltin = (uIndex < unknown.size()) ? rnd.nextBoolean() : true;
+                }
+
+                if (useBuiltin) {
+                    weavedList.add(builtin.get(bIndex));
+                    bIndex++;
+                } else if (uIndex < unknown.size()){
+                    weavedList.add(unknown.get(uIndex));
+                    uIndex++;
+                }
+            }
+
+            String          fullList=GenericUtils.join(weavedList, ',');
+            ParseResult     result=BuiltinCompressions.parseCompressionsList(fullList);
+            List<String>    parsed=NamedResource.Utils.getNameList(result.getParsedFactories());
+            List<String>    missing=result.getUnsupportedFactories();
+            
+            // makes sure not only that the contents are the same but also the order
+            assertListEquals(fullList + "[parsed]", builtin, parsed);
+            assertListEquals(fullList + "[unsupported]", unknown, missing);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnBuiltinValues() {
+        for (NamedFactory<Compression> expected : BuiltinCompressions.VALUES) {
+            String                  name=expected.getName();
+            NamedFactory<Compression>    actual=BuiltinCompressions.resolveFactory(name);
+            Assert.assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testNotAllowedToRegisterBuiltinFactories() {
+        for (CompressionFactory expected : BuiltinCompressions.VALUES) {
+            try {
+                BuiltinCompressions.registerExtension(expected);
+                Assert.fail("Unexpected sucess for " + expected.getName());
+            } catch(IllegalArgumentException e) {
+                // expected - ignored
+            }
+        }
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testNotAllowedToOverrideRegisteredFactories() {
+        CompressionFactory    expected=Mockito.mock(CompressionFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String  name=expected.getName();
+        try {
+            for (int index=1; index <= Byte.SIZE; index++) {
+                BuiltinCompressions.registerExtension(expected);
+                Assert.assertEquals("Unexpected success at attempt #" + index, 1, index);
+            }
+        } finally {
+            BuiltinCompressions.unregisterExtension(name);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnRegisteredExtension() {
+        CompressionFactory    expected=Mockito.mock(CompressionFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String  name=expected.getName();
+        try {
+            Assert.assertNull("Extension already registered", BuiltinCompressions.resolveFactory(name));
+            BuiltinCompressions.registerExtension(expected);
+
+            NamedFactory<Compression>    actual=BuiltinCompressions.resolveFactory(name);
+            Assert.assertSame("Mismatched resolved instance", expected, actual);
+        } finally {
+            NamedFactory<Compression>    actual=BuiltinCompressions.unregisterExtension(name);
+            Assert.assertSame("Mismatched unregistered instance", expected, actual);
+            Assert.assertNull("Extension not un-registered", BuiltinCompressions.resolveFactory(name));
+        }
+    }
+
+    @Test
+    public void testFac2NamedTransformer() {
+        Assert.assertNull("Invalid null transformation", CompressionFactory.FAC2NAMED.transform(null));
+        for (CompressionFactory expected : BuiltinCompressions.VALUES) {
+            NamedFactory<Compression>   actual=CompressionFactory.FAC2NAMED.transform(expected);
+            Assert.assertSame("Mismatched transformed instance for " + expected.getName(), expected, actual);
+        }
+        
+        CompressionFactory   mock=Mockito.mock(CompressionFactory.class);
+        Assert.assertSame("Mismatched transformed mocked instance", mock, CompressionFactory.FAC2NAMED.transform(mock));
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
index e3cfcb3..4c61be3 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
@@ -35,8 +35,11 @@ import org.apache.sshd.common.Mac;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.Transformer;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.compression.BuiltinCompressions;
 import org.apache.sshd.common.compression.Compression;
+import org.apache.sshd.common.compression.CompressionFactory;
 import org.apache.sshd.common.kex.BuiltinDHFactories;
 import org.apache.sshd.common.mac.BuiltinMacs;
 import org.apache.sshd.common.signature.BuiltinSignatures;
@@ -124,7 +127,7 @@ public class SshConfigFileReaderTest extends BaseTest {
                 }
             };
         // must be lenient since we do not cover the full default spectrum
-        AbstractFactoryManager  actual=SshConfigFileReader.configure(expected, props, true);
+        AbstractFactoryManager  actual=SshConfigFileReader.configure(expected, props, true, true);
         Assert.assertSame("Mismatched configured result", expected, actual);
         validateAbstractFactoryManagerConfiguration(expected, props, true);
     }
@@ -139,7 +142,8 @@ public class SshConfigFileReaderTest extends BaseTest {
                     }
                 },
                 getCurrentTestName(),
-                false);
+                false,
+                true);
         Assert.fail("Unexpected success: " + NamedResource.Utils.getNames(manager.getCipherFactories()));
     }
 
@@ -153,7 +157,8 @@ public class SshConfigFileReaderTest extends BaseTest {
                     }
                 },
                 getCurrentTestName(),
-                false);
+                false,
+                true);
         Assert.fail("Unexpected success: " + NamedResource.Utils.getNames(manager.getSignatureFactories()));
     }
 
@@ -167,10 +172,38 @@ public class SshConfigFileReaderTest extends BaseTest {
                     }
                 },
                 getCurrentTestName(),
-                false);
+                false,
+                true);
         Assert.fail("Unexpected success: " + NamedResource.Utils.getNames(manager.getMacFactories()));
     }
-    
+
+    @Test
+    public void testConfigureCompressionFromStringAcceptsCombinedValues() {
+        testConfigureCompressionFromStringAcceptsCombinedValues(CompressionConfigValue.class, Transformer.ENUM_NAME_EXTRACTOR);
+        testConfigureCompressionFromStringAcceptsCombinedValues(BuiltinCompressions.class, NamedResource.NAME_EXTRACTOR);
+    }
+
+    private static <E extends Enum<E> & CompressionFactory> void testConfigureCompressionFromStringAcceptsCombinedValues(
+            Class<E> facs, Transformer<? super E,String> configValueXformer) {
+        for (E expected : facs.getEnumConstants()) {
+            String          value=configValueXformer.transform(expected);
+            String          prefix=facs.getSimpleName() + "[" + expected.name() + "][" + value + "]";
+            FactoryManager  manager=SshConfigFileReader.configureCompression(
+                    new AbstractFactoryManager() {
+                        @Override
+                        protected Closeable getInnerCloseable() {
+                            return null;
+                        }
+                    },
+                    value,
+                    false,
+                    true);
+            List<NamedFactory<Compression>> compressions=manager.getCompressionFactories();
+            Assert.assertEquals(prefix + "(size)", 1, GenericUtils.size(compressions));
+            Assert.assertSame(prefix + "[instance]", expected, compressions.get(0));
+        }
+    }
+
     private static <M extends FactoryManager> M validateAbstractFactoryManagerConfiguration(M manager, Properties props, boolean lenient) {
         validateFactoryManagerCiphers(manager, props);
         validateFactoryManagerSignatures(manager, props);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/test/java/org/apache/sshd/common/kex/BuiltinDHFactoriesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/kex/BuiltinDHFactoriesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/kex/BuiltinDHFactoriesTest.java
index 1dd77ce..04d17a5 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/kex/BuiltinDHFactoriesTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/kex/BuiltinDHFactoriesTest.java
@@ -34,6 +34,7 @@ import org.apache.sshd.common.util.GenericUtils;
 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>
@@ -101,4 +102,60 @@ public class BuiltinDHFactoriesTest extends BaseTest {
             assertListEquals(fullList + "[unsupported]", unknown, missing);
         }
     }
+
+    @Test
+    public void testResolveFactoryOnBuiltinValues() {
+        for (DHFactory expected : BuiltinDHFactories.VALUES) {
+            String              name=expected.getName();
+            DHFactory   actual=BuiltinDHFactories.resolveFactory(name);
+            Assert.assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testNotAllowedToRegisterBuiltinFactories() {
+        for (DHFactory expected : BuiltinDHFactories.VALUES) {
+            try {
+                BuiltinDHFactories.registerExtension(expected);
+                Assert.fail("Unexpected sucess for " + expected.getName());
+            } catch(IllegalArgumentException e) {
+                // expected - ignored
+            }
+        }
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testNotAllowedToOverrideRegisteredFactories() {
+        DHFactory    expected=Mockito.mock(DHFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String  name=expected.getName();
+        try {
+            for (int index=1; index <= Byte.SIZE; index++) {
+                BuiltinDHFactories.registerExtension(expected);
+                Assert.assertEquals("Unexpected success at attempt #" + index, 1, index);
+            }
+        } finally {
+            BuiltinDHFactories.unregisterExtension(name);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnRegisteredExtension() {
+        DHFactory    expected=Mockito.mock(DHFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String  name=expected.getName();
+        try {
+            Assert.assertNull("Extension already registered", BuiltinDHFactories.resolveFactory(name));
+            BuiltinDHFactories.registerExtension(expected);
+
+            DHFactory    actual=BuiltinDHFactories.resolveFactory(name);
+            Assert.assertSame("Mismatched resolved instance", expected, actual);
+        } finally {
+            DHFactory    actual=BuiltinDHFactories.unregisterExtension(name);
+            Assert.assertSame("Mismatched unregistered instance", expected, actual);
+            Assert.assertNull("Extension not un-registered", BuiltinDHFactories.resolveFactory(name));
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
index ead553d..a93f73e 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.Random;
 import java.util.Set;
 
+import org.apache.sshd.common.Mac;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.mac.BuiltinMacs.ParseResult;
@@ -35,6 +36,7 @@ import org.apache.sshd.common.util.GenericUtils;
 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>
@@ -102,4 +104,72 @@ public class BuiltinMacsTest extends BaseTest {
             assertListEquals(fullList + "[unsupported]", unknown, missing);
         }
     }
+
+    @Test
+    public void testResolveFactoryOnBuiltinValues() {
+        for (MacFactory expected : BuiltinMacs.VALUES) {
+            String       name=expected.getName();
+            MacFactory   actual=BuiltinMacs.resolveFactory(name);
+            Assert.assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testNotAllowedToRegisterBuiltinFactories() {
+        for (MacFactory expected : BuiltinMacs.VALUES) {
+            try {
+                BuiltinMacs.registerExtension(expected);
+                Assert.fail("Unexpected sucess for " + expected.getName());
+            } catch(IllegalArgumentException e) {
+                // expected - ignored
+            }
+        }
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testNotAllowedToOverrideRegisteredFactories() {
+        MacFactory    expected=Mockito.mock(MacFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String  name=expected.getName();
+        try {
+            for (int index=1; index <= Byte.SIZE; index++) {
+                BuiltinMacs.registerExtension(expected);
+                Assert.assertEquals("Unexpected success at attempt #" + index, 1, index);
+            }
+        } finally {
+            BuiltinMacs.unregisterExtension(name);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnRegisteredExtension() {
+        MacFactory  expected=Mockito.mock(MacFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String  name=expected.getName();
+        try {
+            Assert.assertNull("Extension already registered", BuiltinMacs.resolveFactory(name));
+            BuiltinMacs.registerExtension(expected);
+
+            MacFactory  actual=BuiltinMacs.resolveFactory(name);
+            Assert.assertSame("Mismatched resolved instance", expected, actual);
+        } finally {
+            MacFactory  actual=BuiltinMacs.unregisterExtension(name);
+            Assert.assertSame("Mismatched unregistered instance", expected, actual);
+            Assert.assertNull("Extension not un-registered", BuiltinMacs.resolveFactory(name));
+        }
+    }
+
+    @Test
+    public void testFac2NamedTransformer() {
+        Assert.assertNull("Invalid null transformation", MacFactory.FAC2NAMED.transform(null));
+        for (MacFactory expected : BuiltinMacs.VALUES) {
+            NamedFactory<Mac>   actual=MacFactory.FAC2NAMED.transform(expected);
+            Assert.assertSame("Mismatched transformed instance for " + expected.getName(), expected, actual);
+        }
+        
+        MacFactory   mock=Mockito.mock(MacFactory.class);
+        Assert.assertSame("Mismatched transformed mocked instance", mock, MacFactory.FAC2NAMED.transform(mock));
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
index f61ad18..155c7c8 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
@@ -27,11 +27,13 @@ import java.util.Random;
 
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.Signature;
 import org.apache.sshd.common.signature.BuiltinSignatures.ParseResult;
 import org.apache.sshd.common.util.GenericUtils;
 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>
@@ -85,4 +87,72 @@ public class BuiltinSignaturesTest extends BaseTest {
             assertListEquals(fullList + "[unsupported]", unknown, missing);
         }
     }
+
+    @Test
+    public void testResolveFactoryOnBuiltinValues() {
+        for (SignatureFactory expected : BuiltinSignatures.VALUES) {
+            String              name=expected.getName();
+            SignatureFactory    actual=BuiltinSignatures.resolveFactory(name);
+            Assert.assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testNotAllowedToRegisterBuiltinFactories() {
+        for (SignatureFactory expected : BuiltinSignatures.VALUES) {
+            try {
+                BuiltinSignatures.registerExtension(expected);
+                Assert.fail("Unexpected sucess for " + expected.getName());
+            } catch(IllegalArgumentException e) {
+                // expected - ignored
+            }
+        }
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testNotAllowedToOverrideRegisteredFactories() {
+        SignatureFactory    expected=Mockito.mock(SignatureFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String  name=expected.getName();
+        try {
+            for (int index=1; index <= Byte.SIZE; index++) {
+                BuiltinSignatures.registerExtension(expected);
+                Assert.assertEquals("Unexpected success at attempt #" + index, 1, index);
+            }
+        } finally {
+            BuiltinSignatures.unregisterExtension(name);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnRegisteredExtension() {
+        SignatureFactory    expected=Mockito.mock(SignatureFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String  name=expected.getName();
+        try {
+            Assert.assertNull("Extension already registered", BuiltinSignatures.resolveFactory(name));
+            BuiltinSignatures.registerExtension(expected);
+
+            SignatureFactory    actual=BuiltinSignatures.resolveFactory(name);
+            Assert.assertSame("Mismatched resolved instance", expected, actual);
+        } finally {
+            SignatureFactory    actual=BuiltinSignatures.unregisterExtension(name);
+            Assert.assertSame("Mismatched unregistered instance", expected, actual);
+            Assert.assertNull("Extension not un-registered", BuiltinSignatures.resolveFactory(name));
+        }
+    }
+
+    @Test
+    public void testFac2NamedTransformer() {
+        Assert.assertNull("Invalid null transformation", SignatureFactory.FAC2NAMED.transform(null));
+        for (SignatureFactory expected : BuiltinSignatures.VALUES) {
+            NamedFactory<Signature>   actual=SignatureFactory.FAC2NAMED.transform(expected);
+            Assert.assertSame("Mismatched transformed instance for " + expected.getName(), expected, actual);
+        }
+        
+        SignatureFactory   mock=Mockito.mock(SignatureFactory.class);
+        Assert.assertSame("Mismatched transformed mocked instance", mock, SignatureFactory.FAC2NAMED.transform(mock));
+    }
 }


[2/2] mina-sshd git commit: [SSHD-455] Allow users to register "extensions" to some of the builtin factories

Posted by lg...@apache.org.
[SSHD-455] Allow users to register "extensions" to some of the builtin factories


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

Branch: refs/heads/master
Commit: 7ccbd066355bde4240cc5ccee0ab06f891921310
Parents: cca7cc7
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Thu May 7 14:58:48 2015 +0300
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Thu May 7 14:58:48 2015 +0300

----------------------------------------------------------------------
 .../main/java/org/apache/sshd/SshBuilder.java   |  84 +++++-----
 .../main/java/org/apache/sshd/SshServer.java    |   1 +
 .../org/apache/sshd/common/NamedFactory.java    |  15 +-
 .../org/apache/sshd/common/NamedResource.java   |  28 ++++
 .../org/apache/sshd/common/Transformer.java     |  58 +++++++
 .../apache/sshd/common/cipher/BaseCipher.java   |   4 +
 .../sshd/common/cipher/BuiltinCiphers.java      |  86 +++++++++-
 .../sshd/common/cipher/CipherFactory.java       |  38 +++++
 .../apache/sshd/common/cipher/CipherNone.java   |   5 +-
 .../org/apache/sshd/common/cipher/ECCurves.java |  12 +-
 .../common/compression/BuiltinCompressions.java | 133 +++++++++++++++-
 .../compression/CompressionDelayedZlib.java     |   1 +
 .../common/compression/CompressionFactory.java  |  37 +++++
 .../common/compression/CompressionZlib.java     |   4 +
 .../common/config/CompressionConfigValue.java   |  12 +-
 .../sshd/common/config/SshConfigFileReader.java | 156 +++++++++++++++----
 .../sshd/common/kex/BuiltinDHFactories.java     |  76 ++++++++-
 .../org/apache/sshd/common/kex/DHFactory.java   |   3 +-
 .../org/apache/sshd/common/mac/BaseMac.java     |   5 +
 .../org/apache/sshd/common/mac/BuiltinMacs.java |  92 +++++++++--
 .../org/apache/sshd/common/mac/MacFactory.java  |  38 +++++
 .../common/signature/AbstractSignature.java     |   2 +
 .../common/signature/BuiltinSignatures.java     | 100 +++++++++---
 .../sshd/common/signature/SignatureDSA.java     |   2 +
 .../sshd/common/signature/SignatureECDSA.java   |   8 +-
 .../sshd/common/signature/SignatureFactory.java |  38 +++++
 .../sshd/common/signature/SignatureRSA.java     |   6 +-
 .../apache/sshd/common/util/GenericUtils.java   |  40 +++++
 .../src/test/java/org/apache/sshd/LoadTest.java |   3 +-
 .../org/apache/sshd/client/kex/KexTest.java     |   2 +-
 .../sshd/common/cipher/BaseCipherTest.java      |   3 +-
 .../sshd/common/cipher/BuiltinCiphersTest.java  |  69 ++++++++
 .../compression/BuiltinCompressionsTest.java    | 114 ++++++++++++++
 .../common/config/SshConfigFileReaderTest.java  |  43 ++++-
 .../sshd/common/kex/BuiltinDHFactoriesTest.java |  57 +++++++
 .../apache/sshd/common/mac/BuiltinMacsTest.java |  70 +++++++++
 .../common/signature/BuiltinSignaturesTest.java |  70 +++++++++
 37 files changed, 1365 insertions(+), 150 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java b/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
index 4daf088..89e9762 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshBuilder.java
@@ -18,7 +18,6 @@
  */
 package org.apache.sshd;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -39,6 +38,7 @@ import org.apache.sshd.common.Random;
 import org.apache.sshd.common.RequestHandler;
 import org.apache.sshd.common.Signature;
 import org.apache.sshd.common.TcpipForwarderFactory;
+import org.apache.sshd.common.Transformer;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.compression.BuiltinCompressions;
 import org.apache.sshd.common.compression.Compression;
@@ -83,17 +83,17 @@ public class SshBuilder {
      * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
      */
     public static class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder<T, S>> implements ObjectBuilder<T> {
-        protected Factory<T> factory = null;
-        protected List<NamedFactory<KeyExchange>> keyExchangeFactories = null;
-        protected List<NamedFactory<Cipher>> cipherFactories = null;
-        protected List<NamedFactory<Compression>> compressionFactories = null;
-        protected List<NamedFactory<Mac>> macFactories = null;
-        protected List<NamedFactory<Signature>> signatureFactories = null;
-        protected Factory<Random> randomFactory = null;
-        protected List<NamedFactory<Channel>> channelFactories = null;
-        protected FileSystemFactory fileSystemFactory = null;
-        protected TcpipForwarderFactory tcpipForwarderFactory = null;
-        protected List<RequestHandler<ConnectionService>> globalRequestHandlers = null;
+        protected Factory<T> factory;
+        protected List<NamedFactory<KeyExchange>> keyExchangeFactories;
+        protected List<NamedFactory<Cipher>> cipherFactories;
+        protected List<NamedFactory<Compression>> compressionFactories;
+        protected List<NamedFactory<Mac>> macFactories;
+        protected List<NamedFactory<Signature>> signatureFactories;
+        protected Factory<Random> randomFactory;
+        protected List<NamedFactory<Channel>> channelFactories;
+        protected FileSystemFactory fileSystemFactory;
+        protected TcpipForwarderFactory tcpipForwarderFactory;
+        protected List<RequestHandler<ConnectionService>> globalRequestHandlers;
 
         protected S fillWithDefaultValues() {
             if (signatureFactories == null) {
@@ -212,6 +212,7 @@ public class SshBuilder {
             return ssh;
         }
 
+        @Override
         public T build() {
             return build(true);
         }
@@ -343,7 +344,19 @@ public class SshBuilder {
      * SshClient builder
      */
     public static class ClientBuilder extends BaseBuilder<SshClient, ClientBuilder> {
-
+        public static final Transformer<DHFactory,NamedFactory<KeyExchange>> DH2KEX =
+                new Transformer<DHFactory, NamedFactory<KeyExchange>>() {
+                    @Override
+                    public NamedFactory<KeyExchange> transform(DHFactory factory) {
+                        if (factory == null) {
+                            return null;
+                        } else if (factory.isGroupExchange()) {
+                            return DHGEXClient.newFactory(factory);
+                        } else {
+                            return DHGClient.newFactory(factory);
+                        }
+                    }
+                };
         protected ServerKeyVerifier serverKeyVerifier;
 
         public ClientBuilder serverKeyVerifier(ServerKeyVerifier serverKeyVerifier) {
@@ -390,21 +403,7 @@ public class SshBuilder {
          * @see BuiltinDHFactories#isSupported()
          */
         public static List<NamedFactory<KeyExchange>> setUpDefaultKeyExchanges(boolean ignoreUnsupported) {
-            List<NamedFactory<KeyExchange>> avail = new ArrayList<>(DEFAULT_KEX_PREFERENCE.size());
-            for (BuiltinDHFactories f : BuiltinDHFactories.VALUES) {
-                if (ignoreUnsupported || f.isSupported()) {
-                    avail.add(getKeyExchangeFactory(f));
-                }
-            }
-            return avail;
-        }
-
-        public static NamedFactory<KeyExchange> getKeyExchangeFactory(DHFactory factory) {
-            if (factory.isGroupExchange()) {
-                return DHGEXClient.newFactory(factory);
-            } else {
-                return DHGClient.newFactory(factory);
-            }
+            return NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, DEFAULT_KEX_PREFERENCE, DH2KEX);
         }
     }
 
@@ -412,6 +411,19 @@ public class SshBuilder {
      * SshServer builder
      */
     public static class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> {
+        public static final Transformer<DHFactory,NamedFactory<KeyExchange>>    DH2KEX = 
+                new Transformer<DHFactory, NamedFactory<KeyExchange>>() {
+                    @Override
+                    public NamedFactory<KeyExchange> transform(DHFactory factory) {
+                        if (factory == null) {
+                            return null;
+                        } else if (factory.isGroupExchange()) {
+                            return DHGEXServer.newFactory(factory);
+                        } else {
+                            return DHGServer.newFactory(factory);
+                        }
+                    }
+                };
 
         @Override
         protected ServerBuilder fillWithDefaultValues() {
@@ -450,21 +462,7 @@ public class SshBuilder {
          * @see BuiltinDHFactories#isSupported()
          */
         public static List<NamedFactory<KeyExchange>> setUpDefaultKeyExchanges(boolean ignoreUnsupported) {
-            List<NamedFactory<KeyExchange>> avail = new ArrayList<>(DEFAULT_KEX_PREFERENCE.size());
-            for (BuiltinDHFactories f : BuiltinDHFactories.VALUES) {
-                if (ignoreUnsupported || f.isSupported()) {
-                    avail.add(getKeyExchangeFactory(f));
-                }
-            }
-            return avail;
-        }
-
-        public static NamedFactory<KeyExchange> getKeyExchangeFactory(DHFactory factory) {
-            if (factory.isGroupExchange()) {
-                return DHGEXServer.newFactory(factory);
-            } else {
-                return DHGServer.newFactory(factory);
-            }
+            return NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, DEFAULT_KEX_PREFERENCE, DH2KEX);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/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 9e45c44..269176a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/SshServer.java
@@ -93,6 +93,7 @@ import org.apache.sshd.server.shell.ProcessShellFactory;
 public class SshServer extends AbstractFactoryManager implements ServerFactoryManager, Closeable {
 
     public static final Factory<SshServer> DEFAULT_SSH_SERVER_FACTORY = new Factory<SshServer>() {
+        @Override
         public SshServer create() {
             return new SshServer();
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
index f12ca99..3337e46 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
@@ -112,7 +112,19 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource {
             }
             return null;
         }
-        
+
+        public static final <S extends OptionalFeature,T,E extends NamedFactory<T>> List<NamedFactory<T>> setUpTransformedFactories(
+                boolean ignoreUnsupported, Collection<? extends S> preferred, Transformer<? super S,? extends E> xform) {
+            List<NamedFactory<T>>   avail=new ArrayList<>(preferred.size());
+            for (S f : preferred) {
+                if (ignoreUnsupported || f.isSupported()) {
+                    avail.add(xform.transform(f));
+                }
+            }
+            
+            return avail;
+        }
+
         public static final <T,E extends NamedFactory<T> & OptionalFeature> List<NamedFactory<T>> setUpBuiltinFactories(
                 boolean ignoreUnsupported, Collection<? extends E> preferred) {
             List<NamedFactory<T>>   avail=new ArrayList<>(preferred.size());
@@ -125,5 +137,4 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource {
             return avail;
         }
     }
-
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
index 494a1a9..d57f433 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
@@ -22,6 +22,7 @@ package org.apache.sshd.common;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 import org.apache.sshd.common.util.GenericUtils;
@@ -34,6 +35,33 @@ public interface NamedResource {
      * @return The resource name
      */
     String getName();
+    
+    /**
+     * Compares 2 {@link NamedResource}s according to their {@link #getName()}
+     * value case <U>insensitive</U>
+     */
+    Comparator<NamedResource> BY_NAME_COMPARATOR=new Comparator<NamedResource>() {
+            @Override
+            public int compare(NamedResource r1, NamedResource r2) {
+                String  n1=r1.getName(), n2=r2.getName();
+                return String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
+            }
+        };
+
+    /**
+     * Returns the value of {@link #getName()} - or {@code null} if argument is {@code null}
+     */
+    Transformer<NamedResource,String> NAME_EXTRACTOR=new Transformer<NamedResource,String>() {
+            @Override
+            public String transform(NamedResource input) {
+                if (input == null) {
+                    return null;
+                } else {
+                    return input.getName();
+                }
+            }
+        };
+
     /**
      * Utility class to help using {@link NamedResource}s
      */

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java b/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java
new file mode 100644
index 0000000..952b2c0
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/Transformer.java
@@ -0,0 +1,58 @@
+/*
+ * 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.common;
+
+import java.util.Objects;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Transformer<I, O> {
+    // TODO in JDK-8 replace this with Function
+    /**
+     * @param input Input value
+     * @return Transformed output value
+     */
+    O transform(I input);
+
+    /**
+     * Invokes {@link Objects#toString(Object)} on the argument
+     */
+    Transformer<Object,String> TOSTRING=new Transformer<Object,String>() {
+            @Override
+            public String transform(Object input) {
+                return Objects.toString(input);
+            }
+        };
+
+    /**
+     * Returns {@link Enum#name()} or {@code null} if argument is {@code null}
+     */
+    Transformer<Enum<?>,String> ENUM_NAME_EXTRACTOR=new Transformer<Enum<?>,String>() {
+            @Override
+            public String transform(Enum<?> input) {
+                if (input == null) {
+                    return null;
+                } else {
+                    return input.name();
+                }
+            }
+        };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
index 66a283c..58accbc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
@@ -45,14 +45,17 @@ public class BaseCipher implements Cipher {
         this.transformation = transformation;
     }
 
+    @Override
     public int getIVSize() {
         return ivsize;
     }
 
+    @Override
     public int getBlockSize() {
         return bsize;
     }
 
+    @Override
     public void init(Mode mode, byte[] key, byte[] iv) throws Exception {
         key = resize(key, bsize);
         iv = resize(iv, ivsize);
@@ -68,6 +71,7 @@ public class BaseCipher implements Cipher {
         }
     }
 
+    @Override
     public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
         cipher.update(input, inputOffset, inputLen, input, inputOffset);
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
index da8550d..68db3cd 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
@@ -25,20 +25,24 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
 
 import org.apache.sshd.common.Cipher;
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.config.NamedFactoriesListParseResult;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * Provides easy access to the currently implemented ciphers
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
+public enum BuiltinCiphers implements CipherFactory {
     none(Constants.NONE, 0, 0, "None", "None") {
         @Override
         public Cipher create() {
@@ -149,6 +153,52 @@ public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
 
     public static final Set<BuiltinCiphers> VALUES =
             Collections.unmodifiableSet(EnumSet.allOf(BuiltinCiphers.class));
+    private static final Map<String,CipherFactory>   extensions =
+            new TreeMap<String,CipherFactory>(String.CASE_INSENSITIVE_ORDER);
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static final void registerExtension(CipherFactory extension) {
+        String  name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized(extensions) {
+            ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+            extensions.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static final SortedSet<CipherFactory> getRegisteredExtensions() {
+        // TODO for JDK-8 return Collections.emptySortedSet()
+        synchronized(extensions) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static final NamedFactory<Cipher> unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+        
+        synchronized(extensions) {
+            return extensions.remove(name);
+        }
+    }
 
     /**
      * @param s The {@link Enum}'s name - ignored if {@code null}/empty
@@ -222,10 +272,10 @@ public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
             return ParseResult.EMPTY;
         }
         
-        List<NamedFactory<Cipher>>  factories=new ArrayList<NamedFactory<Cipher>>(ciphers.size());
-        List<String>                unknown=Collections.<String>emptyList();
+        List<CipherFactory> factories=new ArrayList<CipherFactory>(ciphers.size());
+        List<String>        unknown=Collections.<String>emptyList();
         for (String name : ciphers) {
-            BuiltinCiphers  c=fromFactoryName(name);
+            CipherFactory  c=resolveFactory(name);
             if (c != null) {
                 factories.add(c);
             } else {
@@ -241,13 +291,33 @@ public enum BuiltinCiphers implements NamedFactory<Cipher>, OptionalFeature {
     }
 
     /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension 
+     */
+    public static final CipherFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        CipherFactory  c=fromFactoryName(name);
+        if (c != null) {
+            return c;
+        }
+        
+        synchronized(extensions) {
+            return extensions.get(name);
+        }
+    }
+
+    /**
      * Holds the result of {@link BuiltinCiphers#parseCiphersList(String)}
      * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
      */
-    public static final class ParseResult extends NamedFactoriesListParseResult<Cipher,NamedFactory<Cipher>> {
-        public static final ParseResult EMPTY=new ParseResult(Collections.<NamedFactory<Cipher>>emptyList(), Collections.<String>emptyList());
+    public static final class ParseResult extends NamedFactoriesListParseResult<Cipher,CipherFactory> {
+        public static final ParseResult EMPTY=new ParseResult(Collections.<CipherFactory>emptyList(), Collections.<String>emptyList());
         
-        public ParseResult(List<NamedFactory<Cipher>> parsed, List<String> unsupported) {
+        public ParseResult(List<CipherFactory> parsed, List<String> unsupported) {
             super(parsed, unsupported);
         }
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
new file mode 100644
index 0000000..e536ac0
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.common.cipher;
+
+import org.apache.sshd.common.Cipher;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CipherFactory extends NamedFactory<Cipher>, OptionalFeature {
+    // required because of generics issues
+    Transformer<CipherFactory,NamedFactory<Cipher>> FAC2NAMED=new Transformer<CipherFactory,NamedFactory<Cipher>>() {
+        @Override
+        public NamedFactory<Cipher> transform(CipherFactory input) {
+            return input;
+        }
+    };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
index 3484d93..41f467c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
@@ -19,7 +19,6 @@
 package org.apache.sshd.common.cipher;
 
 import org.apache.sshd.common.Cipher;
-import org.apache.sshd.common.NamedFactory;
 
 /**
  * Represents a no-op cipher.
@@ -31,18 +30,22 @@ import org.apache.sshd.common.NamedFactory;
  */
 public class CipherNone implements Cipher {
 
+    @Override
     public int getIVSize() {
         return 8;
     }
 
+    @Override
     public int getBlockSize() {
         return 16;
     }
 
+    @Override
     public void init(Mode mode, byte[] bytes, byte[] bytes1) throws Exception {
         // ignored - always succeeds
     }
 
+    @Override
     public void update(byte[] input, int inputOffset, int inputLen) throws Exception {
         // ignored - always succeeds
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
index 927b94c..14d979b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
@@ -45,9 +45,9 @@ public class ECCurves {
 
     private static final Map<Integer, String> CURVE_SIZES = new TreeMap<Integer, String>();
     static {
-        CURVE_SIZES.put(256, NISTP256);
-        CURVE_SIZES.put(384, NISTP384);
-        CURVE_SIZES.put(521, NISTP521);
+        CURVE_SIZES.put(Integer.valueOf(256), NISTP256);
+        CURVE_SIZES.put(Integer.valueOf(384), NISTP384);
+        CURVE_SIZES.put(Integer.valueOf(521), NISTP521);
     }
 
     public static String getCurveName(ECParameterSpec params) {
@@ -60,11 +60,7 @@ public class ECCurves {
     }
 
     public static String getCurveName(int fieldSize) {
-        String curveName = CURVE_SIZES.get(fieldSize);
-        if (curveName == null) {
-            return null;
-        }
-        return curveName;
+        return CURVE_SIZES.get(Integer.valueOf(fieldSize));
     }
 
     public static int getCurveSize(ECParameterSpec params) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
index bb0c8f8..95128b8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
@@ -19,18 +19,27 @@
 
 package org.apache.sshd.common.compression;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
 
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFeature {
+public enum BuiltinCompressions implements CompressionFactory {
     none(Constants.NONE) {
             @Override
             public Compression create() {
@@ -62,6 +71,7 @@ public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFe
         return getName();
     }
 
+    @Override
     public final boolean isSupported() {
         return true;
     }
@@ -70,8 +80,55 @@ public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFe
         name = n;
     }
 
-    public static final Set<BuiltinCompressions> VALUES=
+    public static final Set<BuiltinCompressions> VALUES =
             Collections.unmodifiableSet(EnumSet.allOf(BuiltinCompressions.class));
+    private static final Map<String,CompressionFactory>   extensions =
+            new TreeMap<String,CompressionFactory>(String.CASE_INSENSITIVE_ORDER);
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static final void registerExtension(CompressionFactory extension) {
+        String  name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized(extensions) {
+            ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+            extensions.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static final SortedSet<CompressionFactory> getRegisteredExtensions() {
+        // TODO for JDK-8 return Collections.emptySortedSet()
+        synchronized(extensions) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static final CompressionFactory unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+        
+        synchronized(extensions) {
+            return extensions.remove(name);
+        }
+    }
+
     public static final BuiltinCompressions fromFactoryName(String name) {
         if (GenericUtils.isEmpty(name)) {
             return null;
@@ -85,6 +142,76 @@ public enum BuiltinCompressions implements NamedFactory<Compression>, OptionalFe
         
         return null;
     }
+    /**
+     * @param Compressions A comma-separated list of Compressions' names - ignored
+     * if {@code null}/empty
+     * @return A {@link ParseResult} containing the successfully parsed
+     * factories and the unknown ones. <B>Note:</B> it is up to caller to
+     * ensure that the lists do not contain duplicates
+     */
+    public static final ParseResult parseCompressionsList(String Compressions) {
+        return parseCompressionsList(GenericUtils.split(Compressions, ','));
+    }
+
+    public static final ParseResult parseCompressionsList(String ... Compressions) {
+        return parseCompressionsList(GenericUtils.isEmpty((Object[]) Compressions) ? Collections.<String>emptyList() : Arrays.asList(Compressions));
+    }
+
+    public static final ParseResult parseCompressionsList(Collection<String> Compressions) {
+        if (GenericUtils.isEmpty(Compressions)) {
+            return ParseResult.EMPTY;
+        }
+        
+        List<CompressionFactory>    factories=new ArrayList<CompressionFactory>(Compressions.size());
+        List<String>                unknown=Collections.<String>emptyList();
+        for (String name : Compressions) {
+            CompressionFactory  c=resolveFactory(name);
+            if (c != null) {
+                factories.add(c);
+            } else {
+                // replace the (unmodifiable) empty list with a real one
+                if (unknown.isEmpty()) {
+                    unknown = new ArrayList<String>();
+                }
+                unknown.add(name);
+            }
+        }
+        
+        return new ParseResult(factories, unknown);
+    }
+
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension 
+     */
+    public static final CompressionFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        CompressionFactory  c=fromFactoryName(name);
+        if (c != null) {
+            return c;
+        }
+        
+        synchronized(extensions) {
+            return extensions.get(name);
+        }
+    }
+
+    /**
+     * Holds the result of {@link BuiltinCompressions#parseCompressionsList(String)}
+     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+     */
+    public static final class ParseResult extends NamedFactoriesListParseResult<Compression,CompressionFactory> {
+        public static final ParseResult EMPTY=new ParseResult(Collections.<CompressionFactory>emptyList(), Collections.<String>emptyList());
+        
+        public ParseResult(List<CompressionFactory> parsed, List<String> unsupported) {
+            super(parsed, unsupported);
+        }
+    }
+
     public static final class Constants {
         public static final String  NONE="none";
         public static final String  ZLIB="zlib";

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
index 231c214..5c4e1a0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
@@ -34,6 +34,7 @@ public class CompressionDelayedZlib extends CompressionZlib {
         super();
     }
 
+    @Override
     public boolean isDelayed() {
         return true;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
new file mode 100644
index 0000000..3af29d7
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
@@ -0,0 +1,37 @@
+/*
+ * 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.common.compression;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CompressionFactory extends NamedFactory<Compression>, OptionalFeature {
+    // required because of generics issues
+    Transformer<CompressionFactory,NamedFactory<Compression>> FAC2NAMED=new Transformer<CompressionFactory,NamedFactory<Compression>>() {
+        @Override
+        public NamedFactory<Compression> transform(CompressionFactory input) {
+            return input;
+        }
+    };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
index 1e8e921..5e9a56c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
@@ -43,15 +43,18 @@ public class CompressionZlib implements Compression {
     public CompressionZlib() {
     }
 
+    @Override
     public boolean isDelayed() {
         return false;
     }
 
+    @Override
     public void init(Type type, int level) {
         compresser = new Deflater(level);
         decompresser = new Inflater();
     }
 
+    @Override
     public void compress(Buffer buffer) throws IOException {
         compresser.setInput(buffer.array(), buffer.rpos(), buffer.available());
         buffer.wpos(buffer.rpos());
@@ -61,6 +64,7 @@ public class CompressionZlib implements Compression {
         }
     }
 
+    @Override
     public void uncompress(Buffer from, Buffer to) throws IOException {
         decompresser.setInput(from.array(), from.rpos(), from.available());
         int len;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
index bb94f57..fcad285 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
@@ -26,6 +26,7 @@ import java.util.Set;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.compression.BuiltinCompressions;
 import org.apache.sshd.common.compression.Compression;
+import org.apache.sshd.common.compression.CompressionFactory;
 import org.apache.sshd.common.util.GenericUtils;
 
 /**
@@ -33,12 +34,12 @@ import org.apache.sshd.common.util.GenericUtils;
  * actual {@link NamedFactory} for the {@link Compression}.
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum CompressionConfigValue implements NamedFactory<Compression> {
+public enum CompressionConfigValue implements CompressionFactory {
     YES(BuiltinCompressions.zlib),
     NO(BuiltinCompressions.none),
     DELAYED(BuiltinCompressions.delayedZlib);
 
-    private final NamedFactory<Compression> factory;
+    private final CompressionFactory factory;
 
     @Override
     public final String getName() { 
@@ -51,11 +52,16 @@ public enum CompressionConfigValue implements NamedFactory<Compression> {
     }
 
     @Override
+    public boolean isSupported() {
+        return factory.isSupported();
+    }
+
+    @Override
     public final String toString() {
         return getName();
     }
 
-    CompressionConfigValue(NamedFactory<Compression> delegate) {
+    CompressionConfigValue(CompressionFactory delegate) {
         factory = delegate;
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
index 38e0272..e31868c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
@@ -31,21 +31,33 @@ import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.sshd.SshBuilder.ClientBuilder;
+import org.apache.sshd.SshBuilder.ServerBuilder;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.SshServer;
 import org.apache.sshd.common.AbstractFactoryManager;
 import org.apache.sshd.common.Cipher;
+import org.apache.sshd.common.KeyExchange;
 import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.Mac;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.Transformer;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.CipherFactory;
+import org.apache.sshd.common.compression.BuiltinCompressions;
 import org.apache.sshd.common.compression.Compression;
+import org.apache.sshd.common.compression.CompressionFactory;
 import org.apache.sshd.common.kex.BuiltinDHFactories;
 import org.apache.sshd.common.kex.DHFactory;
 import org.apache.sshd.common.mac.BuiltinMacs;
+import org.apache.sshd.common.mac.MacFactory;
 import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.signature.SignatureFactory;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
@@ -408,10 +420,22 @@ public class SshConfigFileReader {
      * @return The matching {@link NamedFactory} for the configured value.
      * {@code null} if no configuration or unknown name specified 
      */
-    public static final NamedFactory<Compression> getCompression(Properties props) {
+    public static final CompressionFactory getCompression(Properties props) {
         return CompressionConfigValue.fromName((props == null) ? null : props.getProperty(COMPRESSION_PROP));
     }
     
+    public static final <S extends SshServer> S configure(S server, Properties props, boolean lenient, boolean ignoreUnsupported) {
+        configure((AbstractFactoryManager) server, props, lenient, ignoreUnsupported);
+        configureKeyExchanges(server, props, lenient, ServerBuilder.DH2KEX, ignoreUnsupported);
+        return server;
+    }
+
+    public static final <C extends SshClient> C configure(C client, Properties props, boolean lenient, boolean ignoreUnsupported) {
+        configure((AbstractFactoryManager) client, props, lenient, ignoreUnsupported);
+        configureKeyExchanges(client, props, lenient, ClientBuilder.DH2KEX, ignoreUnsupported);
+        return client;
+    }
+
     /**
      * <P>Configures an {@link AbstractFactoryManager} with the values read from
      * some configuration. Currently it configures:</P></BR>
@@ -425,78 +449,158 @@ public class SshConfigFileReader {
      * @param props The {@link Properties} to use for configuration - <B>Note:</B>
      * if any known configuration value has a default and does not appear in the
      * properties, the default is used
-     * @param lenient If {@code true} then any unknown/unsupported configuration
-     * values are ignored. Otherwise an {@link IllegalArgumentException} is thrown
+     * @param lenient If {@code true} then any unknown configuration values are ignored.
+     * Otherwise an {@link IllegalArgumentException} is thrown
+     * @param ignoreUnsupported filter out unsupported configuration values (e.g., ciphers,
+     * key exchanges, etc..). <B>Note:</B> if after filtering out all the unknown
+     * or unsupported values there is an empty configuration exception is thrown
      * @return The configured manager
      */
-    public static final <M extends AbstractFactoryManager> M configure(M manager, Properties props, boolean lenient) {
-        configureCiphers(manager, props, lenient);
-        configureSignatures(manager, props, lenient);
-        configureMacs(manager, props, lenient);
-        configureCompression(manager, props, lenient);
+    public static final <M extends AbstractFactoryManager> M configure(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
+        configureCiphers(manager, props, lenient, ignoreUnsupported);
+        configureSignatures(manager, props, lenient, ignoreUnsupported);
+        configureMacs(manager, props, lenient, ignoreUnsupported);
+        configureCompression(manager, props, lenient, ignoreUnsupported);
 
         return manager;
     }
 
-    public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, Properties props, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
-        return configureCiphers(manager, props.getProperty(CIPHERS_CONFIG_PROP, DEFAULT_CIPHERS), lenient);
+        return configureCiphers(manager, props.getProperty(CIPHERS_CONFIG_PROP, DEFAULT_CIPHERS), lenient, ignoreUnsupported);
     }
 
-    public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, String value, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureCiphers(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
 
         BuiltinCiphers.ParseResult  result=BuiltinCiphers.parseCiphersList(value);
         Collection<String>          unsupported=result.getUnsupportedFactories();
         ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported cipher(s) (%s) in %s", unsupported, value);
-        manager.setCipherFactories(ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No known ciphers(s): %s", value));
+
+        List<NamedFactory<Cipher>>  factories =
+                NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), CipherFactory.FAC2NAMED);
+        manager.setCipherFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/unsupported ciphers(s): %s", value));
         return manager;
     }
 
-    public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, Properties props, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
-        return configureSignatures(manager, props.getProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP, DEFAULT_HOST_KEY_ALGORITHMS), lenient);
+        return configureSignatures(manager, props.getProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP, DEFAULT_HOST_KEY_ALGORITHMS), lenient, ignoreUnsupported);
     }
 
-    public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, String value, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureSignatures(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
 
         BuiltinSignatures.ParseResult   result=BuiltinSignatures.parseSignatureList(value);
         Collection<String>              unsupported=result.getUnsupportedFactories();
         ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported signatures (%s) in %s", unsupported, value);
-        manager.setSignatureFactories(ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No known signatures: %s", value));
+        
+        List<NamedFactory<Signature>>   factories =
+                NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), SignatureFactory.FAC2NAMED);
+        manager.setSignatureFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported signatures: %s", value));
         return manager;
     }
     
-    public static final <M extends AbstractFactoryManager> M configureMacs(M manager, Properties props, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureMacs(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
-        return configureMacs(manager, props.getProperty(MACS_CONFIG_PROP, DEFAULT_MACS), lenient);
+        return configureMacs(manager, props.getProperty(MACS_CONFIG_PROP, DEFAULT_MACS), lenient, ignoreUnsupported);
     }
 
-    public static final <M extends AbstractFactoryManager> M configureMacs(M manager, String value, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureMacs(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
 
         BuiltinMacs.ParseResult result=BuiltinMacs.parseMacsList(value);
         Collection<String>      unsupported=result.getUnsupportedFactories();
         ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported MAC(s) (%s) in %s", unsupported, value);
-        manager.setMacFactories(ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No known MAC(s): %s", value));
+        
+        List<NamedFactory<Mac>> factories =
+                NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), MacFactory.FAC2NAMED);
+        manager.setMacFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported MAC(s): %s", value));
         return manager;
     }
 
-    // NOTE: if no compression is resolved it is OK since SSH can function without it
-    public static final <M extends AbstractFactoryManager> M configureCompression(M manager, Properties props, boolean lenient) {
+    /**
+     * @param manager The {@link AbstractFactoryManager} to set up (may not be {@code null})
+     * @param props The (non-{@code null}) {@link Properties} containing the configuration
+     * @param lenient If {@code true} then any unknown/unsupported configuration
+     * values are ignored. Otherwise an {@link IllegalArgumentException} is thrown
+     * @param xformer A {@link Transformer} to convert the configured {@link DHFactory}-ies
+     * to {@link NamedFactory}-ies of {@link KeyExchange}
+     * @param ignoreUnsupported Filter out any un-supported configurations - <B>Note:</B>
+     * if after ignoring the unknown and un-supported values the result is an empty
+     * list of factories and exception is thrown
+     * @return The configured manager
+     * @see #KEX_ALGORITHMS_CONFIG_PROP
+     * @see #DEFAULT_KEX_ALGORITHMS
+     */
+    public static final <M extends AbstractFactoryManager> M configureKeyExchanges(
+            M manager, Properties props, boolean lenient, Transformer<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
-        return configureCompression(manager, props.getProperty(COMPRESSION_PROP, DEFAULT_COMPRESSION), lenient);
+        return configureKeyExchanges(manager, props.getProperty(KEX_ALGORITHMS_CONFIG_PROP, DEFAULT_KEX_ALGORITHMS), lenient, xformer, ignoreUnsupported);
     }
 
-    public static final <M extends AbstractFactoryManager> M configureCompression(M manager, String value, boolean lenient) {
+    public static final <M extends AbstractFactoryManager> M configureKeyExchanges(
+            M manager, String value, boolean lenient, Transformer<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) {
         ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+        ValidateUtils.checkNotNull(xformer, "No DHFactory transformer", GenericUtils.EMPTY_OBJECT_ARRAY);
 
-        NamedFactory<Compression>   factory=CompressionConfigValue.fromName(value);
+        BuiltinDHFactories.ParseResult  result=BuiltinDHFactories.parseDHFactoriesList(value);
+        Collection<String>              unsupported=result.getUnsupportedFactories();
+        ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported KEX(s) (%s) in %s", unsupported, value);
+        
+        List<NamedFactory<KeyExchange>> factories =
+                NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), xformer);
+        manager.setKeyExchangeFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported KEXS(s): %s", value));
+        return manager;
+    }
+
+    /**
+     * Configure the factory manager using one of the known {@link CompressionConfigValue}s.
+     * @param manager The {@link AbstractFactoryManager} to configure
+     * @param props The configuration {@link Properties}
+     * @param lenient If {@code true} and an unknown value is provided then
+     * it is ignored
+     * @param ignoreUnsupported If {@code false} then check if the compression
+     * is currently supported before setting it
+     * @return The configured manager - <B>Note:</B> if the result of filtering due
+     * to lenient mode or ignored unsupported value is empty then no factories are set
+     */
+    public static final <M extends AbstractFactoryManager> M configureCompression(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) {
+        ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+        ValidateUtils.checkNotNull(props, "No properties to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+        
+        String               value=props.getProperty(COMPRESSION_PROP, DEFAULT_COMPRESSION);
+        CompressionFactory   factory=CompressionConfigValue.fromName(value);
         ValidateUtils.checkTrue(lenient || (factory != null), "Unsupported compression value: %s", value);
-        if (factory != null) {
+        if ((factory != null) && factory.isSupported()) {
             manager.setCompressionFactories(Collections.<NamedFactory<Compression>>singletonList(factory));
         }
+        
+        return manager;
+    }
+
+    // accepts BOTH CompressionConfigValue(s) and/or BuiltinCompressions - including extensions
+    public static final <M extends AbstractFactoryManager> M configureCompression(M manager, String value, boolean lenient, boolean ignoreUnsupported) {
+        ValidateUtils.checkNotNull(manager, "No manager to configure", GenericUtils.EMPTY_OBJECT_ARRAY);
+
+        CompressionFactory   factory=CompressionConfigValue.fromName(value);
+        if (factory != null) {
+            // SSH can work without compression
+            if (ignoreUnsupported || factory.isSupported()) {
+                manager.setCompressionFactories(Collections.<NamedFactory<Compression>>singletonList(factory));
+            }
+        } else { 
+            BuiltinCompressions.ParseResult result=BuiltinCompressions.parseCompressionsList(value);
+            Collection<String>              unsupported=result.getUnsupportedFactories();
+            ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported compressions(s) (%s) in %s", unsupported, value);
+        
+            List<NamedFactory<Compression>> factories =
+                NamedFactory.Utils.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), CompressionFactory.FAC2NAMED);
+            // SSH can work without compression
+            if (GenericUtils.size(factories) > 0) {
+                manager.setCompressionFactories(factories);
+            }
+        }
 
         return manager;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
index be29fa2..9503486 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
@@ -26,19 +26,24 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
 
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.config.NamedResourceListParseResult;
 import org.apache.sshd.common.digest.BuiltinDigests;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
+public enum BuiltinDHFactories implements DHFactory {
     dhg1(Constants.DIFFIE_HELLMAN_GROUP1_SHA1) {
         @Override
         public DHG create(Object... params) throws Exception {
@@ -165,6 +170,52 @@ public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
 
     public static final Set<BuiltinDHFactories> VALUES =
             Collections.unmodifiableSet(EnumSet.allOf(BuiltinDHFactories.class));
+    private static final Map<String,DHFactory>   extensions = 
+            new TreeMap<String,DHFactory>(String.CASE_INSENSITIVE_ORDER);
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static final void registerExtension(DHFactory extension) {
+        String  name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized(extensions) {
+            ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+            extensions.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static final SortedSet<DHFactory> getRegisteredExtensions() {
+        // TODO for JDK-8 return Collections.emptySortedSet()
+        synchronized(extensions) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static final DHFactory unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+        
+        synchronized(extensions) {
+            return extensions.remove(name);
+        }
+    }
 
     /**
      * @param name The factory name - ignored if {@code null}/empty
@@ -216,7 +267,7 @@ public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
         List<DHFactory> factories=new ArrayList<DHFactory>(dhList.size());
         List<String>    unknown=Collections.<String>emptyList();
         for (String name : dhList) {
-            DHFactory  f=fromFactoryName(name);
+            DHFactory  f=resolveFactory(name);
             if (f != null) {
                 factories.add(f);
             } else {
@@ -230,6 +281,25 @@ public enum BuiltinDHFactories implements DHFactory, OptionalFeature {
         
         return new ParseResult(factories, unknown);
     }
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension 
+     */
+    public static final DHFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        DHFactory  s=fromFactoryName(name);
+        if (s != null) {
+            return s;
+        }
+        
+        synchronized(extensions) {
+            return extensions.get(name);
+        }
+    }
 
     /**
      * Represents the result of {@link BuiltinDHFactories#parseDHFactoriesList(String)}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
index ad0e295..81ded7e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/DHFactory.java
@@ -19,11 +19,12 @@
 package org.apache.sshd.common.kex;
 
 import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.OptionalFeature;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface DHFactory extends NamedResource {
+public interface DHFactory extends NamedResource, OptionalFeature {
     boolean isGroupExchange();
 
     AbstractDH create(Object... params) throws Exception;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
index 9478a06..77a5006 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
@@ -43,10 +43,12 @@ public class BaseMac implements Mac {
         this.tmp = new byte[defbsize];
     }
 
+    @Override
     public int getBlockSize() {
         return bsize;
     }
 
+    @Override
     public void init(byte[] key) throws Exception {
         if (key.length > defbsize) {
             byte[] tmp = new byte[defbsize];
@@ -59,6 +61,7 @@ public class BaseMac implements Mac {
         mac.init(skey);
     }
 
+    @Override
     public void updateUInt(long i) {
         tmp[0] = (byte) (i >>> 24);
         tmp[1] = (byte) (i >>> 16);
@@ -67,10 +70,12 @@ public class BaseMac implements Mac {
         update(tmp, 0, 4);
     }
 
+    @Override
     public void update(byte foo[], int s, int l) {
         mac.update(foo, s, l);
     }
 
+    @Override
     public void doFinal(byte[] buf, int offset) throws Exception {
         if (bsize != defbsize) {
             mac.doFinal(tmp, 0);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
index 5756dc5..ef920e4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
@@ -25,20 +25,25 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
 
 import org.apache.sshd.common.Digest;
 import org.apache.sshd.common.Mac;
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * Provides easy access to the currently implemented macs
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
+public enum BuiltinMacs implements MacFactory {
     hmacmd5(Constants.HMAC_MD5) {
         @Override
         public Mac create() {
@@ -99,6 +104,52 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
 
     public static final Set<BuiltinMacs> VALUES =
             Collections.unmodifiableSet(EnumSet.allOf(BuiltinMacs.class));
+    private static final Map<String,MacFactory>   extensions =
+            new TreeMap<String,MacFactory>(String.CASE_INSENSITIVE_ORDER);
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static final void registerExtension(MacFactory extension) {
+        String  name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized(extensions) {
+            ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+            extensions.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static final SortedSet<MacFactory> getRegisteredExtensions() {
+        // TODO for JDK-8 return Collections.emptySortedSet()
+        synchronized(extensions) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static final MacFactory unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+        
+        synchronized(extensions) {
+            return extensions.remove(name);
+        }
+    }
 
     /**
      * @param s The {@link Enum}'s name - ignored if {@code null}/empty
@@ -172,10 +223,10 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
             return ParseResult.EMPTY;
         }
         
-        List<NamedFactory<Mac>> factories=new ArrayList<NamedFactory<Mac>>(macs.size());
+        List<MacFactory> factories=new ArrayList<MacFactory>(macs.size());
         List<String>            unknown=Collections.<String>emptyList();
         for (String name : macs) {
-            BuiltinMacs  m=fromFactoryName(name);
+            MacFactory   m=resolveFactory(name);
             if (m != null) {
                 factories.add(m);
             } else {
@@ -190,22 +241,31 @@ public enum BuiltinMacs implements NamedFactory<Mac>, OptionalFeature {
         return new ParseResult(factories, unknown);
     }
 
-    public static final class ParseResult {
-        public static final ParseResult EMPTY=new ParseResult(Collections.<NamedFactory<Mac>>emptyList(), Collections.<String>emptyList());
-        private final List<NamedFactory<Mac>> parsed;
-        private final List<String> unsupported;
-        
-        public ParseResult(List<NamedFactory<Mac>> parsed, List<String> unsupported) {
-            this.parsed = parsed;
-            this.unsupported = unsupported;
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension 
+     */
+    public static final MacFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        MacFactory  m=fromFactoryName(name);
+        if (m != null) {
+            return m;
         }
         
-        public List<NamedFactory<Mac>> getParsedFactories() {
-            return parsed;
+        synchronized(extensions) {
+            return extensions.get(name);
         }
+    }
+
+    public static final class ParseResult extends NamedFactoriesListParseResult<Mac,MacFactory> {
+        public static final ParseResult EMPTY=new ParseResult(Collections.<MacFactory>emptyList(), Collections.<String>emptyList());
         
-        public List<String> getUnsupportedFactories() {
-            return unsupported;
+        public ParseResult(List<MacFactory> parsed, List<String> unsupported) {
+            super(parsed, unsupported);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
new file mode 100644
index 0000000..324405e
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.common.mac;
+
+import org.apache.sshd.common.Mac;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface MacFactory extends NamedFactory<Mac>, OptionalFeature {
+    // required because of generics issues
+    Transformer<MacFactory,NamedFactory<Mac>> FAC2NAMED=new Transformer<MacFactory,NamedFactory<Mac>>() {
+        @Override
+        public NamedFactory<Mac> transform(MacFactory input) {
+            return input;
+        }
+    };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
index ba906b6..f129703 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
@@ -38,6 +38,7 @@ public abstract class AbstractSignature implements Signature {
         this.algorithm = algorithm;
     }
 
+    @Override
     public void init(PublicKey pubkey, PrivateKey prvkey) throws Exception {
         signature = SecurityUtils.getSignature(algorithm);
         if (pubkey != null) {
@@ -48,6 +49,7 @@ public abstract class AbstractSignature implements Signature {
         }
     }
 
+    @Override
     public void update(byte[] foo, int off, int len) throws Exception {
         signature.update(foo, off, len);
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
index 23f72c0..1da1952 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
@@ -26,22 +26,27 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
 
 import org.apache.sshd.common.Digest;
 import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.Signature;
 import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 
 /**
  * Provides easy access to the currently implemented signatures
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeature {
+public enum BuiltinSignatures implements SignatureFactory {
     dsa(KeyPairProvider.SSH_DSS) {
         @Override
         public Signature create() {
@@ -120,8 +125,54 @@ public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeatur
         return true;
     }
 
-    public static final Set<BuiltinSignatures> VALUES =
+    public static final Set<BuiltinSignatures> VALUES = 
             Collections.unmodifiableSet(EnumSet.allOf(BuiltinSignatures.class));
+    private static final Map<String,SignatureFactory>   extensions = 
+            new TreeMap<String,SignatureFactory>(String.CASE_INSENSITIVE_ORDER);
+
+    /**
+     * Registered a {@link NamedFactory} to be available besides the built-in
+     * ones when parsing configuration
+     * @param extension The factory to register
+     * @throws IllegalArgumentException if factory instance is {@code null},
+     * or overrides a built-in one or overrides another registered factory
+     * with the same name (case <U>insensitive</U>).
+     */
+    public static final void registerExtension(SignatureFactory extension) {
+        String  name=ValidateUtils.checkNotNull(extension, "No extension provided", GenericUtils.EMPTY_OBJECT_ARRAY).getName();
+        ValidateUtils.checkTrue(fromFactoryName(name) == null, "Extension overrides built-in: %s", name);
+
+        synchronized(extensions) {
+            ValidateUtils.checkTrue(!extensions.containsKey(name), "Extension overrides existinh: %s", name);
+            extensions.put(name, extension);
+        }
+    }
+
+    /**
+     * @return A {@link SortedSet} of the currently registered extensions, sorted
+     * according to the factory name (case <U>insensitive</U>)
+     */
+    public static final SortedSet<SignatureFactory> getRegisteredExtensions() {
+        // TODO for JDK-8 return Collections.emptySortedSet()
+        synchronized(extensions) {
+            return GenericUtils.asSortedSet(NamedResource.BY_NAME_COMPARATOR, extensions.values());
+        }
+    }
+
+    /**
+     * Unregisters specified extension
+     * @param name The factory name - ignored if {@code null}/empty
+     * @return The registered extension - {@code null} if not found
+     */
+    public static final SignatureFactory unregisterExtension(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+        
+        synchronized(extensions) {
+            return extensions.remove(name);
+        }
+    }
 
     /**
      * @param s The {@link Enum}'s name - ignored if {@code null}/empty
@@ -198,10 +249,10 @@ public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeatur
             return ParseResult.EMPTY;
         }
         
-        List<NamedFactory<Signature>>   factories=new ArrayList<NamedFactory<Signature>>(sigs.size());
-        List<String>                    unknown=Collections.<String>emptyList();
+        List<SignatureFactory>  factories=new ArrayList<SignatureFactory>(sigs.size());
+        List<String>            unknown=Collections.<String>emptyList();
         for (String name : sigs) {
-            BuiltinSignatures  s=fromFactoryName(name);
+            SignatureFactory s=resolveFactory(name);
             if (s != null) {
                 factories.add(s);
             } else {
@@ -216,22 +267,35 @@ public enum BuiltinSignatures implements NamedFactory<Signature>, OptionalFeatur
         return new ParseResult(factories, unknown);
     }
 
-    public static final class ParseResult {
-        public static final ParseResult EMPTY=new ParseResult(Collections.<NamedFactory<Signature>>emptyList(), Collections.<String>emptyList());
-        private final List<NamedFactory<Signature>> parsed;
-        private final List<String> unsupported;
-        
-        public ParseResult(List<NamedFactory<Signature>> parsed, List<String> unsupported) {
-            this.parsed = parsed;
-            this.unsupported = unsupported;
+    /**
+     * @param name The factory name
+     * @return The factory or {@code null} if it is neither a built-in one
+     * or a registered extension 
+     */
+    public static final SignatureFactory resolveFactory(String name) {
+        if (GenericUtils.isEmpty(name)) {
+            return null;
+        }
+
+        SignatureFactory  s=fromFactoryName(name);
+        if (s != null) {
+            return s;
         }
         
-        public List<NamedFactory<Signature>> getParsedFactories() {
-            return parsed;
+        synchronized(extensions) {
+            return extensions.get(name);
         }
+    }
+
+    /**
+     * Holds the result of the {@link BuiltinSignatures#parseSignatureList(String)}
+     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+     */
+    public static final class ParseResult extends NamedFactoriesListParseResult<Signature,SignatureFactory> {
+        public static final ParseResult EMPTY=new ParseResult(Collections.<SignatureFactory>emptyList(), Collections.<String>emptyList());
         
-        public List<String> getUnsupportedFactories() {
-            return unsupported;
+        public ParseResult(List<SignatureFactory> parsed, List<String> unsupported) {
+            super(parsed, unsupported);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
index d682b3a..b6ec18c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
@@ -30,6 +30,7 @@ public class SignatureDSA extends AbstractSignature {
 	    super(algorithm);
     }
 
+    @Override
     public byte[] sign() throws Exception {
         byte[] sig = signature.sign();
 
@@ -63,6 +64,7 @@ public class SignatureDSA extends AbstractSignature {
         return result;
     }
 
+    @Override
     public boolean verify(byte[] sig) throws Exception {
         sig = extractSig(sig);
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
index b1e5a07..5801801 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
@@ -20,12 +20,6 @@ package org.apache.sshd.common.signature;
 
 import java.io.IOException;
 import java.math.BigInteger;
-import java.security.spec.ECParameterSpec;
-
-import org.apache.sshd.common.KeyPairProvider;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Signature;
-import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.util.Buffer;
 
 /**
@@ -39,6 +33,7 @@ public class SignatureECDSA extends AbstractSignature {
         super(algo);
     }
 
+    @Override
     public byte[] sign() throws Exception {
         byte[] sig = signature.sign();
 
@@ -73,6 +68,7 @@ public class SignatureECDSA extends AbstractSignature {
         return rsBuf.getCompactData();
     }
 
+    @Override
     public boolean verify(byte[] sig) throws Exception {
         sig = extractSig(sig);
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
new file mode 100644
index 0000000..99510e6
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.common.signature;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SignatureFactory extends NamedFactory<Signature>, OptionalFeature {
+    // required because of generics issues
+    Transformer<SignatureFactory,NamedFactory<Signature>> FAC2NAMED=new Transformer<SignatureFactory,NamedFactory<Signature>>() {
+        @Override
+        public NamedFactory<Signature> transform(SignatureFactory input) {
+            return input;
+        }
+    };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ccbd066/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
index 5c3558a..1cb117e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
@@ -18,10 +18,6 @@
  */
 package org.apache.sshd.common.signature;
 
-import org.apache.sshd.common.KeyPairProvider;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Signature;
-
 /**
  * RSA <code>Signature</code>
  *
@@ -33,10 +29,12 @@ public class SignatureRSA extends AbstractSignature {
         super("SHA1withRSA");
     }
 
+    @Override
     public byte[] sign() throws Exception {
         return signature.sign();
     }
 
+    @Override
     public boolean verify(byte[] sig) throws Exception {
         sig = extractSig(sig);
         return signature.verify(sig);