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 2016/02/18 15:31:06 UTC

mina-sshd git commit: [SSHD-647] Use system properties as fallback PropertyResolver

Repository: mina-sshd
Updated Branches:
  refs/heads/master 7e24f2e1b -> e70fb3d11


[SSHD-647] Use system properties as fallback PropertyResolver


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

Branch: refs/heads/master
Commit: e70fb3d11521d5776c6eb6d76e045c8d35228368
Parents: 7e24f2e
Author: Lyor Goldstein <ly...@gmail.com>
Authored: Thu Feb 18 16:31:42 2016 +0200
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Thu Feb 18 16:31:42 2016 +0200

----------------------------------------------------------------------
 .../sshd/common/AbstractFactoryManager.java     |   7 +-
 .../apache/sshd/common/PropertyResolver.java    |  57 +++--
 .../sshd/common/PropertyResolverUtils.java      |   2 +-
 .../apache/sshd/common/SyspropsMapWrapper.java  | 221 +++++++++++++++++++
 .../apache/sshd/common/util/GenericUtils.java   |   4 +-
 .../java/org/apache/sshd/common/util/Pair.java  |  43 +++-
 .../sshd/common/PropertyResolverUtilsTest.java  |  39 ++++
 7 files changed, 352 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e70fb3d1/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java
index ffb3883..58bb9c2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/AbstractFactoryManager.java
@@ -75,6 +75,7 @@ public abstract class AbstractFactoryManager extends AbstractKexFactoryManager i
     protected final ChannelListener channelListenerProxy;
 
     private final Map<String, Object> properties = new ConcurrentHashMap<>();
+    private PropertyResolver parentResolver = SyspropsMapWrapper.SYSPROPS_RESOLVER;
 
     protected AbstractFactoryManager() {
         ClassLoader loader = getClass().getClassLoader();
@@ -116,7 +117,11 @@ public abstract class AbstractFactoryManager extends AbstractKexFactoryManager i
 
     @Override
     public PropertyResolver getParentPropertyResolver() {
-        return null;
+        return parentResolver;
+    }
+
+    public void setParentPropertyResolver(PropertyResolver parent) {
+        parentResolver = parent;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e70fb3d1/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java
index 574a294..c3adff4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java
@@ -19,6 +19,7 @@
 
 package org.apache.sshd.common;
 
+import java.util.Collections;
 import java.util.Map;
 
 /**
@@ -28,36 +29,60 @@ import java.util.Map;
  * to a numeric or {@code boolean} value, or from {@code int} to {@code long},
  * etc.. <B>Note:</B> implementations may decide to use case <U>insensitive</U>
  * property names, therefore it is <U><B>highly discouraged</B></U> to use names
- * that differ from each other only in case sensitivity. Also, implementations may
- * choose to trim whitespaces, thus such are also highly discouraged.
+ * that differ from each other only in case sensitivity. Also, implementations
+ * may choose to trim whitespaces, thus such are also highly discouraged.
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public interface PropertyResolver {
     /**
+     * An &quot;empty&quot; resolver with no properties and no parent
+     */
+    PropertyResolver EMPTY = new PropertyResolver() {
+        @Override
+        public PropertyResolver getParentPropertyResolver() {
+            return null;
+        }
+
+        @Override
+        public Map<String, Object> getProperties() {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public String toString() {
+            return "EMPTY";
+        }
+    };
+
+    /**
      * @return The parent resolver that can be used to query for missing
-     * properties - {@code null} if no parent
+     *         properties - {@code null} if no parent
      */
     PropertyResolver getParentPropertyResolver();
 
     /**
-     * <P>A map of properties that can be used to configure the SSH server
-     * or client.  This map will never be changed by either the server or
-     * client and is not supposed to be changed at runtime (changes are not
-     * bound to have any effect on a running client or server), though it may
-     * affect the creation of sessions later as these values are usually not
-     * cached.</P>
+     * <P>
+     * A map of properties that can be used to configure the SSH server or
+     * client. This map will never be changed by either the server or client and
+     * is not supposed to be changed at runtime (changes are not bound to have
+     * any effect on a running client or server), though it may affect the
+     * creation of sessions later as these values are usually not cached.
+     * </P>
      *
-     * <P><B>Note:</B> the <U>type</U> of the mapped property should match the
+     * <P>
+     * <B>Note:</B> the <U>type</U> of the mapped property should match the
      * expected configuration value type - {@code Long, Integer, Boolean,
      * String}, etc.... If it doesn't, the {@code toString()} result of the
-     * mapped value is used to convert it to the required type. E.g., if
-     * the mapped value is the <U>string</U> &quot;1234&quot; and the expected
-     * value is a {@code long} then it will be parsed into one. Also, if
-     * the mapped value is an {@code Integer} but a {@code long} is expected,
-     * then it will be converted into one.</P>
+     * mapped value is used to convert it to the required type. E.g., if the
+     * mapped value is the <U>string</U> &quot;1234&quot; and the expected value
+     * is a {@code long} then it will be parsed into one. Also, if the mapped
+     * value is an {@code Integer} but a {@code long} is expected, then it will
+     * be converted into one.
+     * </P>
      *
-     * @return a valid <code>Map</code> containing configuration values, never {@code null}
+     * @return a valid <code>Map</code> containing configuration values, never
+     *         {@code null}
      */
     Map<String, Object> getProperties();
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e70fb3d1/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
index 4760a48..903a796 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
@@ -21,6 +21,7 @@ package org.apache.sshd.common;
 
 import java.util.Map;
 import java.util.Objects;
+
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -28,7 +29,6 @@ import org.apache.sshd.common.util.ValidateUtils;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public final class PropertyResolverUtils {
-
     private PropertyResolverUtils() {
         throw new UnsupportedOperationException("No instance allowed");
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e70fb3d1/sshd-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java b/sshd-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
new file mode 100644
index 0000000..698e6e2
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
@@ -0,0 +1,221 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Pair;
+
+/**
+ * A wrapper that exposes a read-only {@link Map} access to the system
+ * properties. Any attempt to modify it will throw {@link UnsupportedOperationException}.
+ * The mapper uses the {@link #SYSPROPS_MAPPED_PREFIX} to filter and access'
+ * only these properties, ignoring all others
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class SyspropsMapWrapper implements Map<String, Object> {
+    /**
+     * Prefix of properties used by the mapper to identify SSHD related settings
+     */
+    public static final String SYSPROPS_MAPPED_PREFIX = "org.apache.sshd.config";
+
+    /**
+     * The one and only wrapper instance
+     */
+    public static final SyspropsMapWrapper INSTANCE = new SyspropsMapWrapper();
+
+    /**
+     * A {@link PropertyResolver} with no parent that exposes the system properties
+     */
+    public static final PropertyResolver SYSPROPS_RESOLVER = new PropertyResolver() {
+        @Override
+        public Map<String, Object> getProperties() {
+            return SyspropsMapWrapper.INSTANCE;
+        }
+
+        @Override
+        public PropertyResolver getParentPropertyResolver() {
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return "SYSPROPS";
+        }
+    };
+
+    private SyspropsMapWrapper() {
+        super();
+    }
+
+    @Override
+    public void clear() {
+        throw new UnsupportedOperationException("sysprops#clear() N/A");
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return get(key) != null;
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        // not the most efficient implementation, but we do not expect it to be called much
+        Properties props = System.getProperties();
+        for (String key : props.stringPropertyNames()) {
+            if (!isMappedSyspropKey(key)) {
+                continue;
+            }
+
+            Object v = props.getProperty(key);
+            if (Objects.equals(v, value)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public Set<Entry<String, Object>> entrySet() {
+        Properties props = System.getProperties();
+        // return a copy in order to avoid concurrent modifications
+        Set<Entry<String, Object>> entries =
+                new TreeSet<Entry<String, Object>>(Pair.<String, Object>byKeyEntryComparator());
+        for (String key : props.stringPropertyNames()) {
+            if (!isMappedSyspropKey(key)) {
+                continue;
+            }
+
+            Object v = props.getProperty(key);
+            if (v != null) {
+                entries.add(new Pair<>(getUnmappedSyspropKey(key), v));
+            }
+        }
+
+        return entries;
+    }
+
+    @Override
+    public Object get(Object key) {
+        return (key instanceof String) ? System.getProperty(getMappedSyspropKey(key)) : null;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return GenericUtils.isEmpty(keySet());
+    }
+
+    @Override
+    public Set<String> keySet() {
+        Properties props = System.getProperties();
+        Set<String> keys = new TreeSet<>();
+        // filter out any non-SSHD properties
+        for (String key : props.stringPropertyNames()) {
+            if (isMappedSyspropKey(key)) {
+                keys.add(getUnmappedSyspropKey(key));
+            }
+        }
+
+        return keys;
+    }
+
+    @Override
+    public Object put(String key, Object value) {
+        throw new UnsupportedOperationException("sysprops#put(" + key + ")[" + value + "] N/A");
+    }
+
+    @Override
+    public void putAll(Map<? extends String, ? extends Object> m) {
+        throw new UnsupportedOperationException("sysprops#putAll(" + m + ") N/A");
+    }
+
+    @Override
+    public Object remove(Object key) {
+        throw new UnsupportedOperationException("sysprops#remove(" + key + ") N/A");
+    }
+
+    @Override
+    public int size() {
+        return GenericUtils.size(keySet());
+    }
+
+    @Override
+    public Collection<Object> values() {
+        Properties props = System.getProperties();
+        // return a copy in order to avoid concurrent modifications
+        List<Object> values = new ArrayList<>(props.size());
+        for (String key : props.stringPropertyNames()) {
+            if (!isMappedSyspropKey(key)) {
+                continue;
+            }
+            Object v = props.getProperty(key);
+            if (v != null) {
+                values.add(v);
+            }
+        }
+
+        return values;
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toString(System.getProperties(), null);
+    }
+
+    /**
+     * @param key Key to be tested
+     * @return {@code true} if key starts with {@link #SYSPROPS_MAPPED_PREFIX}
+     * and continues with a dot followed by some characters
+     */
+    public static boolean isMappedSyspropKey(String key) {
+        return (GenericUtils.length(key) > (SYSPROPS_MAPPED_PREFIX.length() + 1))
+            && key.startsWith(SYSPROPS_MAPPED_PREFIX)
+            && (key.charAt(SYSPROPS_MAPPED_PREFIX.length()) == '.');
+    }
+
+    /**
+     * @param key Key to be transformed
+     * @return The &quot;pure&quot; key name if a mapped one, same as input otherwise
+     * @see #isMappedSyspropKey(String)
+     */
+    public static String getUnmappedSyspropKey(Object key) {
+        String s = Objects.toString(key);
+        return isMappedSyspropKey(s) ? s.substring(SYSPROPS_MAPPED_PREFIX.length() + 1 /* skip dot */) : s;
+    }
+
+    /**
+     * @param key The original key
+     * @return A key prefixed by {@link #SYSPROPS_MAPPED_PREFIX}
+     * @see #isMappedSyspropKey(String)
+     */
+    public static String getMappedSyspropKey(Object key) {
+        return SYSPROPS_MAPPED_PREFIX + "." + key;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e70fb3d1/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 a07ccdc..01743ff 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
@@ -222,7 +222,7 @@ public final class GenericUtils {
     }
 
     public static boolean isEmpty(Collection<?> c) {
-        return size(c) <= 0;
+        return (c == null) || c.isEmpty();
     }
 
     public static int size(Map<?, ?> m) {
@@ -230,7 +230,7 @@ public final class GenericUtils {
     }
 
     public static boolean isEmpty(Map<?, ?> m) {
-        return size(m) <= 0;
+        return (m == null) || m.isEmpty();
     }
 
     @SafeVarargs

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e70fb3d1/sshd-core/src/main/java/org/apache/sshd/common/util/Pair.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/Pair.java b/sshd-core/src/main/java/org/apache/sshd/common/util/Pair.java
index 0d34fd2..8a9bbd8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/Pair.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/Pair.java
@@ -18,6 +18,9 @@
  */
 package org.apache.sshd.common.util;
 
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Objects;
 
 /**
@@ -27,7 +30,19 @@ import java.util.Objects;
  * @param <S> Second value type
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class Pair<F, S> {
+public class Pair<F, S> implements Map.Entry<F, S> {
+    @SuppressWarnings("rawtypes")
+    private static final Comparator<Map.Entry<Comparable, ?>> BY_KEY_COMPARATOR =
+        new Comparator<Map.Entry<Comparable, ?>>() {
+            @SuppressWarnings("unchecked")
+            @Override
+            public int compare(Entry<Comparable, ?> o1, Entry<Comparable, ?> o2) {
+                Comparable k1 = o1.getKey();
+                Comparable k2 = o2.getKey();
+                return k1.compareTo(k2);
+            }
+    };
+
     private final F first;
     private final S second;
 
@@ -36,6 +51,21 @@ public class Pair<F, S> {
         this.second = second;
     }
 
+    @Override
+    public final F getKey() {
+        return getFirst();
+    }
+
+    @Override
+    public S getValue() {
+        return getSecond();
+    }
+
+    @Override
+    public S setValue(S value) {
+        throw new UnsupportedOperationException("setValue(" + value + ") N/A");
+    }
+
     public final F getFirst() {
         return first;
     }
@@ -71,4 +101,15 @@ public class Pair<F, S> {
     public String toString() {
         return Objects.toString(getFirst()) + ", " + Objects.toString(getSecond());
     }
+
+    /**
+     * @param <K> The {@link Comparable} key type
+     * @param <V> The associated entry value
+     * @return A {@link Comparator} for {@link java.util.Map.Entry}-ies that
+     * compares the key values
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <K extends Comparable<K>, V> Comparator<Map.Entry<K, V>> byKeyEntryComparator() {
+        return (Comparator) BY_KEY_COMPARATOR;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e70fb3d1/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
index 9e90173..303bdbc 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import java.util.TreeMap;
 
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.util.test.BaseTestSupport;
 import org.junit.FixMethodOrder;
@@ -54,6 +55,44 @@ public class PropertyResolverUtilsTest extends BaseTestSupport {
     }
 
     @Test
+    public void testSyspropsResolver() {
+        PropertyResolver resolver = SyspropsMapWrapper.SYSPROPS_RESOLVER;
+        Map<String, ?> props = resolver.getProperties();
+        assertTrue("Unexpected initial resolver values: " + props, GenericUtils.isEmpty(props));
+
+        final String NAME = getCurrentTestName();
+        assertNull("Unexpected initial resolved value", PropertyResolverUtils.getObject(resolver, NAME));
+
+        final String PROPKEY = SyspropsMapWrapper.getMappedSyspropKey(NAME);
+        assertNull("Unexpected property value for " + PROPKEY, System.getProperty(PROPKEY));
+
+        try {
+            long expected = System.currentTimeMillis();
+            System.setProperty(PROPKEY, Long.toString(expected));
+            testLongProperty(resolver, NAME, expected);
+        } finally {
+            System.clearProperty(PROPKEY);
+        }
+
+        try {
+            int expected = 3777347;
+            System.setProperty(PROPKEY, Integer.toString(expected));
+            testIntegerProperty(resolver, NAME, expected);
+        } finally {
+            System.clearProperty(PROPKEY);
+        }
+
+        for (final boolean expected : new boolean[]{false, true}) {
+            try {
+                System.setProperty(PROPKEY, Boolean.toString(expected));
+                testBooleanProperty(resolver, NAME, expected);
+            } finally {
+                System.clearProperty(PROPKEY);
+            }
+        }
+    }
+
+    @Test
     public void testLongProperty() {
         final long expected = System.currentTimeMillis();
         final String name = getCurrentTestName();