You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ja...@apache.org on 2013/11/11 21:57:11 UTC

svn commit: r1540843 - in /felix/trunk/http/jetty/src: main/java/org/apache/felix/http/jetty/internal/ test/ test/java/ test/java/org/ test/java/org/apache/ test/java/org/apache/felix/ test/java/org/apache/felix/http/ test/java/org/apache/felix/http/je...

Author: jawi
Date: Mon Nov 11 20:57:11 2013
New Revision: 1540843

URL: http://svn.apache.org/r1540843
Log:
FELIX-2786 - allow random port to be used:

- Applied patch supplied by Carl Hall, after applying some minor cleanups.


Added:
    felix/trunk/http/jetty/src/test/
    felix/trunk/http/jetty/src/test/java/
    felix/trunk/http/jetty/src/test/java/org/
    felix/trunk/http/jetty/src/test/java/org/apache/
    felix/trunk/http/jetty/src/test/java/org/apache/felix/
    felix/trunk/http/jetty/src/test/java/org/apache/felix/http/
    felix/trunk/http/jetty/src/test/java/org/apache/felix/http/jetty/
    felix/trunk/http/jetty/src/test/java/org/apache/felix/http/jetty/internal/
    felix/trunk/http/jetty/src/test/java/org/apache/felix/http/jetty/internal/JettyConfigTest.java   (with props)
Modified:
    felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java

Modified: felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java?rev=1540843&r1=1540842&r2=1540843&view=diff
==============================================================================
--- felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java (original)
+++ felix/trunk/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java Mon Nov 11 20:57:11 2013
@@ -16,6 +16,8 @@
  */
 package org.apache.felix.http.jetty.internal;
 
+import java.io.IOException;
+import java.net.ServerSocket;
 import java.security.KeyStore;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -24,6 +26,7 @@ import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.Properties;
 
+import org.apache.felix.http.base.internal.logger.SystemLogger;
 import org.osgi.framework.BundleContext;
 
 public final class JettyConfig
@@ -177,12 +180,12 @@ public final class JettyConfig
 
     public int getHttpPort()
     {
-        return getIntProperty(HTTP_PORT, 8080);
+        return determinePort(String.valueOf(getProperty(HTTP_PORT)), 8080);
     }
 
     public int getHttpsPort()
     {
-        return getIntProperty(HTTPS_PORT, 8443);
+        return determinePort(String.valueOf(getProperty(HTTPS_PORT)), 8443);
     }
 
     public int getHttpTimeout()
@@ -197,14 +200,7 @@ public final class JettyConfig
      */
     public int getIntProperty(String name, int defValue)
     {
-        try
-        {
-            return Integer.parseInt(getProperty(name, null));
-        }
-        catch (Exception e)
-        {
-            return defValue;
-        }
+        return parseInt(getProperty(name, null), defValue);
     }
 
     public String getKeyPassword()
@@ -212,14 +208,14 @@ public final class JettyConfig
         return getProperty(FELIX_KEYSTORE_KEY_PASSWORD, this.context.getProperty(OSCAR_KEYSTORE_KEY_PASSWORD));
     }
 
-    public String getKeystoreType()
+    public String getKeystore()
     {
-        return getProperty(FELIX_KEYSTORE_TYPE, KeyStore.getDefaultType());
+        return getProperty(FELIX_KEYSTORE, this.context.getProperty(OSCAR_KEYSTORE));
     }
 
-    public String getKeystore()
+    public String getKeystoreType()
     {
-        return getProperty(FELIX_KEYSTORE, this.context.getProperty(OSCAR_KEYSTORE));
+        return getProperty(FELIX_KEYSTORE_TYPE, KeyStore.getDefaultType());
     }
 
     public String getPassword()
@@ -239,13 +235,7 @@ public final class JettyConfig
      */
     public String getProperty(String name, String defValue)
     {
-        Dictionary conf = this.config;
-        Object value = (conf != null) ? conf.get(name) : null;
-        if (value == null)
-        {
-            value = this.context.getProperty(name);
-        }
-
+        Object value = getProperty(name);
         return value != null ? String.valueOf(value) : defValue;
     }
 
@@ -363,7 +353,126 @@ public final class JettyConfig
         return false;
     }
 
-    private String[] getStringArrayProperty(String name, String[] defValue)
+    private void closeSilently(ServerSocket resource)
+    {
+        if (resource != null)
+        {
+            try
+            {
+                resource.close();
+            }
+            catch (IOException e)
+            {
+                // Ignore...
+            }
+        }
+    }
+
+    /**
+     * Determine the appropriate port to use. <code>portProp</code> is based
+     * "version range" as described in OSGi Core Spec v4.2 3.2.6. It can use the
+     * following forms:
+     * <dl>
+     * <dd>8000 | 8000</dd>
+     * <dd>[8000,9000] | 8000 &lt;= port &lt;= 9000</dd>
+     * <dd>[8000,9000) | 8000 &lt;= port &lt; 9000</dd>
+     * <dd>(8000,9000] | 8000 &lt; port &lt;= 9000</dd>
+     * <dd>(8000,9000) | 8000 &lt; port &lt; 9000</dd>
+     * <dd>[,9000) | 1 &lt; port &lt; 9000</dd>
+     * <dd>[8000,) | 8000 &lt;= port &lt; 65534</dd>
+     * </dl>
+     * 
+     * @param portProp
+     *            The port property value to parse.
+     * @return The port determined to be usable. -1 if failed to find a port.
+     */
+    private int determinePort(String portProp, int dflt)
+    {
+        // Default cases include null/empty range pattern or pattern == *.
+        if (portProp == null || "".equals(portProp.trim()))
+        {
+            return dflt;
+        }
+
+        // asking for random port, so let ServerSocket handle it and return the answer
+        portProp = portProp.trim();
+        if ("*".equals(portProp))
+        {
+            ServerSocket ss = null;
+            try
+            {
+                ss = new ServerSocket(0);
+                return ss.getLocalPort();
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e.getMessage(), e);
+            }
+            finally
+            {
+                closeSilently(ss);
+            }
+        }
+        else
+        {
+            // check that the port property is a version range as described in
+            // OSGi Core Spec v4.2 3.2.6.
+            // deviations from the spec are limited to:
+            // * start, end of interval defaults to 1, 65535, respectively, if missing.
+            char startsWith = portProp.charAt(0);
+            char endsWith = portProp.charAt(portProp.length() - 1);
+            String interval = portProp.substring(1, portProp.length() - 1);
+
+            int minPort = 1;
+            int maxPort = 65535;
+
+            int comma = interval.indexOf(',');
+            if (comma >= 0 && (startsWith == '[' || startsWith == '(') && (endsWith == ']' || endsWith == ')'))
+            {
+                // check if the comma is first (start port in range is missing)
+                int start = (comma == 0) ? minPort : parseInt(interval.substring(0, comma), minPort);
+                // check if the comma is last (end port in range is missing)
+                int end = (comma == interval.length() - 1) ? maxPort : parseInt(interval.substring(comma + 1), maxPort);
+                // check for exclusive notation
+                if (startsWith == '(')
+                {
+                    start++;
+                }
+                if (endsWith == ')')
+                {
+                    end--;
+                }
+                // find a port in the requested range
+                int port = start - 1;
+                for (int i = start; port < start && i <= end; i++)
+                {
+                    ServerSocket ss = null;
+                    try
+                    {
+                        ss = new ServerSocket(i);
+                        port = ss.getLocalPort();
+                    }
+                    catch (IOException e)
+                    {
+                        SystemLogger.debug("Unable to bind to port: " + port + " | " + portProp);
+                    }
+                    finally
+                    {
+                        closeSilently(ss);
+                    }
+                }
+
+                return (port < start) ? dflt : port;
+            }
+            else
+            {
+                // We don't recognize the pattern as special, so try to parse it to an int
+                return parseInt(portProp, dflt);
+            }
+        }
+    }
+
+    private Object getProperty(String name)
     {
         Dictionary conf = this.config;
         Object value = (conf != null) ? conf.get(name) : null;
@@ -371,6 +480,12 @@ public final class JettyConfig
         {
             value = this.context.getProperty(name);
         }
+        return value;
+    }
+
+    private String[] getStringArrayProperty(String name, String[] defValue)
+    {
+        Object value = getProperty(name);
         if (value instanceof String)
         {
             return new String[] { (String) value };
@@ -397,4 +512,16 @@ public final class JettyConfig
             return defValue;
         }
     }
+
+    private int parseInt(String value, int dflt)
+    {
+        try
+        {
+            return Integer.parseInt(value);
+        }
+        catch (NumberFormatException e)
+        {
+            return dflt;
+        }
+    }
 }

Added: felix/trunk/http/jetty/src/test/java/org/apache/felix/http/jetty/internal/JettyConfigTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/jetty/src/test/java/org/apache/felix/http/jetty/internal/JettyConfigTest.java?rev=1540843&view=auto
==============================================================================
--- felix/trunk/http/jetty/src/test/java/org/apache/felix/http/jetty/internal/JettyConfigTest.java (added)
+++ felix/trunk/http/jetty/src/test/java/org/apache/felix/http/jetty/internal/JettyConfigTest.java Mon Nov 11 20:57:11 2013
@@ -0,0 +1,109 @@
+/*
+ * 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.felix.http.jetty.internal;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.replay;
+
+import java.net.ServerSocket;
+import java.util.Hashtable;
+
+import junit.framework.TestCase;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * Unit test for JettyConfig
+ */
+public class JettyConfigTest extends TestCase
+{
+    JettyConfig config;
+    BundleContext context;
+
+    public void testGetDefaultPort()
+    {
+        assertEquals("HTTP port", 8080, this.config.getHttpPort());
+        assertEquals("HTTPS port", 8443, this.config.getHttpsPort());
+    }
+
+    public void testGetPortInRange()
+    {
+        Hashtable<String, Object> props = new Hashtable<String, Object>();
+        props.put("org.osgi.service.http.port", "[8000,9000]");
+        props.put("org.osgi.service.http.port.secure", "[10000,11000)");
+        this.config.update(props);
+
+        assertTrue(this.config.getHttpPort() >= 8000 && this.config.getHttpPort() <= 9000);
+        assertTrue(this.config.getHttpsPort() >= 10000 && this.config.getHttpsPort() < 11000);
+
+        props.put("org.osgi.service.http.port", "(12000,13000]");
+        props.put("org.osgi.service.http.port.secure", "(14000,15000)");
+        this.config.update(props);
+
+        assertTrue(this.config.getHttpPort() > 12000 && this.config.getHttpPort() <= 13000);
+        assertTrue(this.config.getHttpsPort() > 14000 && this.config.getHttpsPort() < 15000);
+
+        props.put("org.osgi.service.http.port", "[,9000]");
+        props.put("org.osgi.service.http.port.secure", "[9000,)");
+        this.config.update(props);
+
+        assertTrue(this.config.getHttpPort() >= 1 && this.config.getHttpPort() <= 9000);
+        assertTrue(this.config.getHttpsPort() >= 9000 && this.config.getHttpsPort() < 65535);
+    }
+
+    public void testGetPortInvalidRange()
+    {
+        Hashtable<String, Object> props = new Hashtable<String, Object>();
+        props.put("org.osgi.service.http.port", "+12000,13000*");
+        props.put("org.osgi.service.http.port.secure", "%14000,15000");
+        this.config.update(props);
+
+        assertEquals(8080, this.config.getHttpPort());
+        assertEquals(8443, this.config.getHttpsPort());
+    }
+
+    public void testGetRandomPort()
+    {
+        Hashtable<String, Object> props = new Hashtable<String, Object>();
+        props.put("org.osgi.service.http.port", "*");
+        props.put("org.osgi.service.http.port.secure", "*");
+        this.config.update(props);
+        assertTrue(this.config.getHttpPort() != 8080);
+        assertTrue(this.config.getHttpsPort() != 433);
+    }
+
+    public void testGetSpecificPort() throws Exception
+    {
+        ServerSocket ss = new ServerSocket(0);
+        int port = ss.getLocalPort();
+        ss.close();
+        Hashtable<String, Object> props = new Hashtable<String, Object>();
+        props.put("org.osgi.service.http.port", port);
+        props.put("org.osgi.service.http.port.secure", port);
+        this.config.update(props);
+        assertTrue(this.config.getHttpPort() == port);
+        assertTrue(this.config.getHttpsPort() == port);
+    }
+
+    @Override
+    protected void setUp()
+    {
+        this.context = createNiceMock(BundleContext.class);
+        replay(this.context);
+        this.config = new JettyConfig(this.context);
+    }
+}
\ No newline at end of file

Propchange: felix/trunk/http/jetty/src/test/java/org/apache/felix/http/jetty/internal/JettyConfigTest.java
------------------------------------------------------------------------------
    svn:eol-style = native