Merged branch 'jetty-9.1.x' into 'master'.
diff --git a/VERSION.txt b/VERSION.txt
index 6d87bc7..7b8a856 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1,3 +1,5 @@
+jetty-9.2.0-SNAPSHOT
+
 jetty-9.1.4.v20140401 - 01 April 2014
  + 414206 Rewrite rules re-encode requestURI
  + 414885 Don't expose JDT classes by default
diff --git a/aggregates/jetty-all/pom.xml b/aggregates/jetty-all/pom.xml
index 37932e6..de45168 100644
--- a/aggregates/jetty-all/pom.xml
+++ b/aggregates/jetty-all/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
diff --git a/apache-jsp/pom.xml b/apache-jsp/pom.xml
new file mode 100644
index 0000000..27bafe5
--- /dev/null
+++ b/apache-jsp/pom.xml
@@ -0,0 +1,95 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-project</artifactId>
+    <version>9.2.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>apache-jsp</artifactId>
+  <name>Jetty :: Apache JSP</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <packaging>jar</packaging>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.${project.artifactId}</bundle-symbolic-name>
+  </properties>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <executions>
+          <execution>
+            <id>generate-manifest</id>
+            <goals>
+              <goal>manifest</goal>
+            </goals>
+            <configuration>
+              <instructions>
+                <Export-Package>org.eclipse.jetty.apache.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
+                <Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
+                <Provide-Capability>osgi.serviceloader; osgi.serviceloader=javax.servlet.ServletContainerInitializer</Provide-Capability>
+                <_nouses>true</_nouses>
+              </instructions>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+            <configuration>
+              <descriptorRefs>
+                <descriptorRef>config</descriptorRef>
+              </descriptorRefs>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-util</artifactId>
+        <version>${project.version}</version>
+    </dependency>
+    
+    <!-- Schemas -->
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-schemas</artifactId>
+    </dependency>
+
+    <!-- servlet api -->
+    <dependency>
+       <groupId>javax.servlet</groupId>
+       <artifactId>javax.servlet-api</artifactId>
+    </dependency>
+
+    <!-- JSP Api -->
+    <dependency>
+      <groupId>javax.servlet.jsp</groupId>
+      <artifactId>javax.servlet.jsp-api</artifactId>
+    </dependency>
+
+    <!-- JSP Impl -->
+    <dependency>
+      <groupId>org.mortbay.jasper</groupId>
+      <artifactId>apache-jsp</artifactId>
+    </dependency>
+
+    <!-- Eclipse Java Compiler (for JSP Compilation) -->
+    <dependency>
+      <groupId>org.eclipse.jetty.orbit</groupId>
+      <artifactId>org.eclipse.jdt.core</artifactId>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/apache-jsp/src/main/config/modules/jsp-impl/apache-jsp.mod b/apache-jsp/src/main/config/modules/jsp-impl/apache-jsp.mod
new file mode 100644
index 0000000..aed547c
--- /dev/null
+++ b/apache-jsp/src/main/config/modules/jsp-impl/apache-jsp.mod
@@ -0,0 +1,10 @@
+#
+# Apache JSP Module
+#
+
+[name]
+jsp-impl
+
+[lib]
+lib/apache-jsp/*.jar
+
diff --git a/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JettyJasperInitializer.java b/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JettyJasperInitializer.java
new file mode 100644
index 0000000..4cd7993
--- /dev/null
+++ b/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JettyJasperInitializer.java
@@ -0,0 +1,114 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.apache.jsp;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.jasper.servlet.JasperInitializer;
+import org.apache.jasper.servlet.TldPreScanned;
+import org.apache.jasper.servlet.TldScanner;
+import org.xml.sax.SAXException;
+
+/**
+ * JettyJasperInitializer
+ *
+ */
+public class JettyJasperInitializer extends JasperInitializer
+{
+    
+    /**
+     * NullTldScanner
+     *
+     * Does nothing. Used when we can tell that all jsps have been precompiled, in which case
+     * the tlds are not needed.
+     */
+    private final class NullTldScanner extends TldScanner
+    {
+        /**
+         * @param context
+         * @param namespaceAware
+         * @param validation
+         * @param blockExternal
+         */
+        private NullTldScanner(ServletContext context, boolean namespaceAware, boolean validation, boolean blockExternal)
+        {
+            super(context, namespaceAware, validation, blockExternal);
+        }
+
+        /**
+         * @see org.apache.jasper.servlet.TldScanner#scan()
+         */
+        @Override
+        public void scan() throws IOException, SAXException
+        {
+            return; //do nothing
+        }
+
+        /**
+         * @see org.apache.jasper.servlet.TldScanner#getListeners()
+         */
+        @Override
+        public List<String> getListeners()
+        {
+            return Collections.emptyList();
+        }
+
+        /**
+         * @see org.apache.jasper.servlet.TldScanner#scanJars()
+         */
+        @Override
+        public void scanJars()
+        {
+           return; //do nothing
+        }
+    }
+
+    /**
+     * Make a TldScanner, and prefeed it the tlds that have already been discovered in jar files
+     * by the MetaInfConfiguration.
+     * 
+     * @see org.apache.jasper.servlet.JasperInitializer#prepareScanner(javax.servlet.ServletContext, boolean, boolean, boolean)
+     */
+    @Override
+    public TldScanner newTldScanner(ServletContext context, boolean namespaceAware, boolean validate, boolean blockExternal)
+    {  
+        String tmp = context.getInitParameter("org.eclipse.jetty.jsp.precompiled");
+        if (tmp!=null && !tmp.equals("") && Boolean.valueOf(tmp))
+        {
+            return new NullTldScanner(context, namespaceAware, validate, blockExternal);
+        }
+        
+        Collection<URL> tldUrls = (Collection<URL>)context.getAttribute("org.eclipse.jetty.tlds");
+        if (tldUrls != null && !tldUrls.isEmpty())
+        {
+            return new TldPreScanned(context,namespaceAware,validate,blockExternal,tldUrls);
+        }
+        return super.newTldScanner(context, namespaceAware, validate, blockExternal);
+    }
+    
+
+}
diff --git a/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JuliLog.java b/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JuliLog.java
new file mode 100644
index 0000000..e58781d
--- /dev/null
+++ b/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JuliLog.java
@@ -0,0 +1,188 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.apache.jsp;
+
+public class JuliLog implements org.apache.juli.logging.Log 
+{
+    public static org.apache.juli.logging.Log getInstance(String name)
+    {
+        return new JuliLog(name);
+    }
+    
+    private final org.eclipse.jetty.util.log.Logger _logger;
+    private final org.eclipse.jetty.util.log.StdErrLog _stdErrLog;
+
+    public JuliLog()
+    {    
+        _logger=org.eclipse.jetty.util.log.Log.getRootLogger();
+        _stdErrLog=(_logger instanceof org.eclipse.jetty.util.log.StdErrLog) ? (org.eclipse.jetty.util.log.StdErrLog)_logger:null;
+    }
+    
+    public JuliLog(String name)
+    {
+        _logger=org.eclipse.jetty.util.log.Log.getLogger(name);
+        _stdErrLog=(_logger instanceof org.eclipse.jetty.util.log.StdErrLog) ? (org.eclipse.jetty.util.log.StdErrLog)_logger:null;
+    }
+    
+    @Override
+    public boolean isDebugEnabled()
+    {
+        return _logger.isDebugEnabled();
+    }
+
+    @Override
+    public boolean isErrorEnabled()
+    {
+        return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_WARN;
+    }
+
+    @Override
+    public boolean isFatalEnabled()
+    {
+        return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_WARN;
+    }
+
+    @Override
+    public boolean isInfoEnabled()
+    {
+        return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_INFO;
+    }
+
+    @Override
+    public boolean isTraceEnabled()
+    {
+        return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_DEBUG;
+    }
+
+    @Override
+    public boolean isWarnEnabled()
+    {
+        return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_WARN;
+    }
+
+    @Override
+    public void trace(Object message)
+    {
+        if (message instanceof String)
+            _logger.debug((String)message);
+        else
+            _logger.debug("{}",message);
+    }
+
+    @Override
+    public void trace(Object message, Throwable t)
+    {
+        if (message instanceof String)
+            _logger.debug((String)message,t);
+        else
+            _logger.debug("{}",message,t);
+    }
+
+    @Override
+    public void debug(Object message)
+    {
+        if (message instanceof String)
+            _logger.debug((String)message);
+        else
+            _logger.debug("{}",message);
+    }
+
+    @Override
+    public void debug(Object message, Throwable t)
+    {
+        if (message instanceof String)
+            _logger.debug((String)message,t);
+        else
+            _logger.debug("{}",message,t);
+    }
+
+    @Override
+    public void info(Object message)
+    {
+        if (message instanceof String)
+            _logger.info((String)message);
+        else
+            _logger.info("{}",message);
+    }
+
+    @Override
+    public void info(Object message, Throwable t)
+    {
+        if (message instanceof String)
+            _logger.info((String)message,t);
+        else
+            _logger.info("{}",message,t);
+    }
+
+    @Override
+    public void warn(Object message)
+    {
+        if (message instanceof String)
+            _logger.warn((String)message);
+        else
+            _logger.warn("{}",message);
+    }
+
+    @Override
+    public void warn(Object message, Throwable t)
+    {
+        if (message instanceof String)
+            _logger.warn((String)message,t);
+        else
+            _logger.warn("{}",message,t);
+    }
+
+    @Override
+    public void error(Object message)
+    {
+        if (message instanceof String)
+            _logger.warn((String)message);
+        else
+            _logger.warn("{}",message);
+    }
+
+    @Override
+    public void error(Object message, Throwable t)
+    {
+        if (message instanceof String)
+            _logger.warn((String)message,t);
+        else
+            _logger.warn("{}",message,t);
+    }
+
+    @Override
+    public void fatal(Object message)
+    {
+        if (message instanceof String)
+            _logger.warn((String)message);
+        else
+            _logger.warn("{}",message);
+    }
+
+    @Override
+    public void fatal(Object message, Throwable t)
+    {
+        if (message instanceof String)
+            _logger.warn((String)message,t);
+        else
+            _logger.warn("{}",message,t);
+    }
+}
+
+
diff --git a/apache-jsp/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/apache-jsp/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
new file mode 100644
index 0000000..6346705
--- /dev/null
+++ b/apache-jsp/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
@@ -0,0 +1 @@
+org.eclipse.jetty.apache.jsp.JettyJasperInitializer
diff --git a/apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log b/apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log
new file mode 100644
index 0000000..efa397b
--- /dev/null
+++ b/apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log
@@ -0,0 +1 @@
+org.eclipse.jetty.apache.jsp.JuliLog
diff --git a/apache-jstl/pom.xml b/apache-jstl/pom.xml
new file mode 100644
index 0000000..38c8158
--- /dev/null
+++ b/apache-jstl/pom.xml
@@ -0,0 +1,49 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-project</artifactId>
+    <version>9.2.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>apache-jstl</artifactId>
+  <name>Apache :: JSTL module</name>
+  <url>http://tomcat.apache.org/taglibs/standard/</url>
+  <packaging>jar</packaging>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+            <configuration>
+              <descriptorRefs>
+                <descriptorRef>config</descriptorRef>
+              </descriptorRefs>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <!-- JSTL Api -->
+    <dependency>
+       <groupId>org.apache.taglibs</groupId>
+       <artifactId>taglibs-standard-spec</artifactId>
+    </dependency>
+
+    <!-- JSTL Impl -->
+    <dependency>
+       <groupId>org.apache.taglibs</groupId>
+       <artifactId>taglibs-standard-impl</artifactId>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/apache-jstl/src/main/config/modules/jsp-impl/apache-jstl.mod b/apache-jstl/src/main/config/modules/jsp-impl/apache-jstl.mod
new file mode 100644
index 0000000..804b191
--- /dev/null
+++ b/apache-jstl/src/main/config/modules/jsp-impl/apache-jstl.mod
@@ -0,0 +1,8 @@
+#
+# Apache JSTL 
+#
+[name]
+jstl-impl
+
+[lib]
+lib/apache-jstl/*.jar
diff --git a/apache-jstl/src/main/resources/readme.txt b/apache-jstl/src/main/resources/readme.txt
new file mode 100644
index 0000000..a516023
--- /dev/null
+++ b/apache-jstl/src/main/resources/readme.txt
@@ -0,0 +1,4 @@
+This empty jar file is purely to work around a problem with the Maven Dependency plugin.
+Several modules in jetty use the Dependency plugin to copy or unpack the dependencies of  other modules.
+However, the Dependency plugin is not capable of unpacking or copying a dependency of type 'pom', which
+this module is, as it consists purely of external dependencies needed to run jsp.
diff --git a/examples/async-rest/async-rest-jar/pom.xml b/examples/async-rest/async-rest-jar/pom.xml
index a8ff514..9af2078 100644
--- a/examples/async-rest/async-rest-jar/pom.xml
+++ b/examples/async-rest/async-rest-jar/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>example-async-rest</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>  
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.eclipse.jetty.example-async-rest</groupId>
diff --git a/examples/async-rest/async-rest-webapp/pom.xml b/examples/async-rest/async-rest-webapp/pom.xml
index 582d5a0..1779812 100644
--- a/examples/async-rest/async-rest-webapp/pom.xml
+++ b/examples/async-rest/async-rest-webapp/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>example-async-rest</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>  
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.eclipse.jetty.example-async-rest</groupId>
diff --git a/examples/async-rest/pom.xml b/examples/async-rest/pom.xml
index b2b6ca3..84bd9a4 100644
--- a/examples/async-rest/pom.xml
+++ b/examples/async-rest/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.examples</groupId>
     <artifactId>examples-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>  
   <modelVersion>4.0.0</modelVersion>
diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml
index 8c0abae..30019e0 100644
--- a/examples/embedded/pom.xml
+++ b/examples/embedded/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.examples</groupId>
     <artifactId>examples-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
diff --git a/examples/pom.xml b/examples/pom.xml
index 2fadf1a..737b90f 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <groupId>org.eclipse.jetty.examples</groupId>
diff --git a/jetty-alpn/jetty-alpn-client/pom.xml b/jetty-alpn/jetty-alpn-client/pom.xml
new file mode 100644
index 0000000..0c3bb4d
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-client/pom.xml
@@ -0,0 +1,76 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-alpn-parent</artifactId>
+    <version>9.2.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>jetty-alpn-client</artifactId>
+  <name>Jetty :: ALPN Client</name>
+  <description>Jetty ALPN client services</description>
+  <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.alpn.client</bundle-symbolic-name>
+  </properties>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <executions>
+          <execution>
+            <goals>
+              <goal>manifest</goal>
+            </goals>
+            <configuration>
+              <instructions>
+               <Import-Package>org.eclipse.jetty.alpn;resolution:=optional</Import-Package>
+              </instructions>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+          </archive>
+        </configuration>
+      </plugin>
+      <!-- always include the sources to be able to prepare the eclipse-jetty-SDK feature
+      with a snapshot. -->
+      <plugin>
+         <groupId>org.apache.maven.plugins</groupId>
+         <artifactId>maven-source-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>findbugs-maven-plugin</artifactId>
+        <configuration>
+          <onlyAnalyze>org.eclipse.jetty.alpn.*</onlyAnalyze>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-io</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.alpn</groupId>
+      <artifactId>alpn-api</artifactId>
+      <version>${alpn.api.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
new file mode 100644
index 0000000..1cc08ab
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
@@ -0,0 +1,87 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.alpn.client;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import javax.net.ssl.SSLEngine;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.NegotiatingClientConnection;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class ALPNClientConnection extends NegotiatingClientConnection implements ALPN.ClientProvider
+{
+    private static final Logger LOG = Log.getLogger(ALPNClientConnection.class);
+
+    private final String protocol;
+
+    public ALPNClientConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, SSLEngine sslEngine, Map<String, Object> context, String protocol)
+    {
+        super(endPoint, executor, sslEngine, connectionFactory, context);
+        this.protocol = protocol;
+        ALPN.put(sslEngine, this);
+    }
+
+    @Override
+    public boolean supports()
+    {
+        return true;
+    }
+
+    @Override
+    public void unsupported()
+    {
+        ALPN.remove(getSSLEngine());
+        completed();
+    }
+
+    @Override
+    public List<String> protocols()
+    {
+        return Arrays.asList(protocol);
+    }
+
+    @Override
+    public void selected(String protocol)
+    {
+        if (this.protocol.equals(protocol))
+        {
+            ALPN.remove(getSSLEngine());
+            completed();
+        }
+        else
+        {
+            LOG.info("Could not negotiate protocol: server {} - client {}", protocol, this.protocol);
+            close();
+        }
+    }
+
+    @Override
+    public void close()
+    {
+        ALPN.remove(getSSLEngine());
+        super.close();
+    }
+}
diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
new file mode 100644
index 0000000..881c6a5
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
@@ -0,0 +1,51 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.alpn.client;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+import javax.net.ssl.SSLEngine;
+
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
+import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
+
+public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory
+{
+    private final Executor executor;
+    private final String protocol;
+
+    public ALPNClientConnectionFactory(Executor executor, ClientConnectionFactory connectionFactory, String protocol)
+    {
+        super(connectionFactory);
+        this.executor = executor;
+        this.protocol = protocol;
+    }
+
+    @Override
+    public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
+    {
+        return new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(),
+                (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY), context, protocol);
+    }
+}
diff --git a/jetty-alpn/jetty-alpn-server/pom.xml b/jetty-alpn/jetty-alpn-server/pom.xml
new file mode 100644
index 0000000..6634192
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/pom.xml
@@ -0,0 +1,94 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-alpn-parent</artifactId>
+    <version>9.2.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>jetty-alpn-server</artifactId>
+  <name>Jetty :: ALPN Server</name>
+  <description>Jetty ALPN server services</description>
+  <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.alpn.server</bundle-symbolic-name>
+  </properties>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <executions>
+          <execution>
+            <goals>
+              <goal>manifest</goal>
+            </goals>
+            <configuration>
+              <instructions>
+               <Import-Package>org.eclipse.jetty.alpn,*</Import-Package>
+               <_nouses>true</_nouses>
+              </instructions>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+          </archive>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+            <configuration>
+              <descriptorRefs>
+                <descriptorRef>config</descriptorRef>
+              </descriptorRefs>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <!-- always include the sources to be able to prepare the eclipse-jetty-SDK feature
+      with a snapshot. -->
+      <plugin>
+         <groupId>org.apache.maven.plugins</groupId>
+         <artifactId>maven-source-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>findbugs-maven-plugin</artifactId>
+        <configuration>
+          <onlyAnalyze>org.eclipse.jetty.alpn.*</onlyAnalyze>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.alpn</groupId>
+      <artifactId>alpn-api</artifactId>
+      <version>${alpn.api.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/etc/protonego-alpn.xml b/jetty-alpn/jetty-alpn-server/src/main/config/etc/protonego-alpn.xml
new file mode 100644
index 0000000..293de04
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/etc/protonego-alpn.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure id="protonego" class="org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory">
+    <Arg name="protocols">
+	<Array type="String">
+	    <Item>spdy/3</Item>
+	    <Item>spdy/2</Item>
+	    <Item>http/1.1</Item>
+	</Array>
+    </Arg>
+   
+    <Set name="defaultProtocol">http/1.1</Set>
+
+</Configure>
+
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_40.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_40.mod
new file mode 100644
index 0000000..bee4693
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_40.mod
@@ -0,0 +1,8 @@
+[name]
+protonego-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.0.0.v20140317/alpn-boot-7.0.0.v20140317.jar:lib/alpn/alpn-boot-7.0.0.v20140317.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-7.0.0.v20140317.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_45.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_45.mod
new file mode 100644
index 0000000..bee4693
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_45.mod
@@ -0,0 +1,8 @@
+[name]
+protonego-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.0.0.v20140317/alpn-boot-7.0.0.v20140317.jar:lib/alpn/alpn-boot-7.0.0.v20140317.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-7.0.0.v20140317.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_51.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_51.mod
new file mode 100644
index 0000000..bee4693
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_51.mod
@@ -0,0 +1,8 @@
+[name]
+protonego-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.0.0.v20140317/alpn-boot-7.0.0.v20140317.jar:lib/alpn/alpn-boot-7.0.0.v20140317.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-7.0.0.v20140317.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_55.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_55.mod
new file mode 100644
index 0000000..bee4693
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.7.0_55.mod
@@ -0,0 +1,8 @@
+[name]
+protonego-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.0.0.v20140317/alpn-boot-7.0.0.v20140317.jar:lib/alpn/alpn-boot-7.0.0.v20140317.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-7.0.0.v20140317.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0.mod
new file mode 100644
index 0000000..4089153
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0.mod
@@ -0,0 +1,8 @@
+[name]
+protonego-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.0.0.v20140317/alpn-boot-8.0.0.v20140317.jar:lib/alpn/alpn-boot-8.0.0.v20140317.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.0.0.v20140317.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_05.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_05.mod
new file mode 100644
index 0000000..4089153
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn-1.8.0_05.mod
@@ -0,0 +1,8 @@
+[name]
+protonego-boot
+
+[files]
+http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.0.0.v20140317/alpn-boot-8.0.0.v20140317.jar:lib/alpn/alpn-boot-8.0.0.v20140317.jar
+
+[exec]
+-Xbootclasspath/p:lib/alpn/alpn-boot-8.0.0.v20140317.jar
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn.mod
new file mode 100644
index 0000000..0e399f0
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn.mod
@@ -0,0 +1,36 @@
+# ALPN is provided via a -Xbootclasspath that modifies the secure connections
+# in java to support the ALPN layer needed for SPDY (and eventually HTTP/2)
+#
+# This modification has a tight dependency on specific recent updates of
+# Java 1.7 and Java 1.8
+# (Java versions prior to 1.7u40 are not supported)
+#
+# The alpn protonego module will use an appropriate alpn-boot jar for your
+# specific version of Java.
+#
+# IMPORTANT: Versions of Java that exist after this module was created are
+#            not guaranteed to work with existing alpn-boot jars, and might
+#            need a new alpn-boot to be created / tested / deployed by the
+#            Jetty project in order to provide support for these future
+#            Java versions.
+#
+# All versions of alpn-boot can be found at
+# http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/
+
+[name]
+protonego-impl
+
+[depend]
+protonego-impl/alpn-${java.version}
+
+[lib]
+lib/jetty-alpn-client-${jetty.version}.jar
+lib/jetty-alpn-server-${jetty.version}.jar
+
+[xml]
+etc/protonego-alpn.xml
+
+[files]
+lib/
+lib/alpn/
+
diff --git a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java
new file mode 100644
index 0000000..509682e
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java
@@ -0,0 +1,77 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.alpn.server;
+
+import java.util.Collections;
+import java.util.List;
+import javax.net.ssl.SSLEngine;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.NegotiatingServerConnection;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class ALPNServerConnection extends NegotiatingServerConnection implements ALPN.ServerProvider
+{
+    private static final Logger LOG = Log.getLogger(ALPNServerConnection.class);
+
+    public ALPNServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol)
+    {
+        super(connector, endPoint, engine, protocols, defaultProtocol);
+        ALPN.put(engine, this);
+    }
+
+    @Override
+    public void unsupported()
+    {
+        select(Collections.<String>emptyList());
+    }
+
+    @Override
+    public String select(List<String> clientProtocols)
+    {
+        List<String> serverProtocols = getProtocols();
+        String negotiated = null;
+        for (String clientProtocol : clientProtocols)
+        {
+            if (serverProtocols.contains(clientProtocol))
+            {
+                negotiated = clientProtocol;
+                break;
+            }
+        }
+        if (negotiated == null)
+        {
+            negotiated = getDefaultProtocol();
+        }
+        LOG.debug("{} protocol selected {}", this, negotiated);
+        setProtocol(negotiated);
+        ALPN.remove(getSSLEngine());
+        return negotiated;
+    }
+
+    @Override
+    public void close()
+    {
+        ALPN.remove(getSSLEngine());
+        super.close();
+    }
+}
diff --git a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java
new file mode 100644
index 0000000..a6e386b
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java
@@ -0,0 +1,61 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.alpn.server;
+
+import java.util.List;
+import javax.net.ssl.SSLEngine;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
+import org.eclipse.jetty.util.annotation.Name;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFactory
+{
+    private static final Logger LOG = Log.getLogger(ALPNServerConnectionFactory.class);
+
+    public ALPNServerConnectionFactory(@Name("protocols") String... protocols)
+    {
+        super("alpn", protocols);
+        try
+        {
+            ClassLoader alpnClassLoader = ALPN.class.getClassLoader();
+            if (alpnClassLoader != null)
+            {
+                LOG.warn("ALPN must be in the boot classloader, not in: " + alpnClassLoader);
+                throw new IllegalStateException("ALPN must be in the boot classloader");
+            }
+        }
+        catch (Throwable x)
+        {
+            LOG.warn("ALPN not available", x);
+            throw new IllegalStateException("ALPN not available", x);
+        }
+    }
+
+    @Override
+    protected AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol)
+    {
+        return new ALPNServerConnection(connector, endPoint, engine, protocols, defaultProtocol);
+    }
+}
diff --git a/jetty-alpn/pom.xml b/jetty-alpn/pom.xml
new file mode 100644
index 0000000..689b859
--- /dev/null
+++ b/jetty-alpn/pom.xml
@@ -0,0 +1,17 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-project</artifactId>
+    <version>9.2.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>jetty-alpn-parent</artifactId>
+  <packaging>pom</packaging>
+  <name>Jetty :: ALPN :: Parent</name>
+  <description>Jetty ALPN services parent</description>
+  <url>http://www.eclipse.org/jetty</url>
+  <modules>
+    <module>jetty-alpn-server</module>
+    <module>jetty-alpn-client</module>
+  </modules>
+</project>
diff --git a/jetty-annotations/pom.xml b/jetty-annotations/pom.xml
index bb6d838..f07812f 100644
--- a/jetty-annotations/pom.xml
+++ b/jetty-annotations/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-annotations</artifactId>
diff --git a/jetty-ant/pom.xml b/jetty-ant/pom.xml
index cc2012d..3f9ade0 100644
--- a/jetty-ant/pom.xml
+++ b/jetty-ant/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-ant</artifactId>
diff --git a/jetty-client/pom.xml b/jetty-client/pom.xml
index 9eee4c9..ea2eb2d 100644
--- a/jetty-client/pom.xml
+++ b/jetty-client/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java
index f7f17c3..0f0a9f3 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientRedirectTest.java
@@ -45,6 +45,7 @@
 import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 public class HttpClientRedirectTest extends AbstractHttpClientServerTest
@@ -244,8 +245,10 @@
     }
 
     @Test
+    @Ignore
     public void testRedirectFailed() throws Exception
     {
+        // TODO this test is failing with timout after an ISP upgrade??  DNS dependent?
         try
         {
             client.newRequest("localhost", connector.getLocalPort())
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpReceiverTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpReceiverTest.java
deleted file mode 100644
index 86c0b64..0000000
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpReceiverTest.java
+++ /dev/null
@@ -1,220 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.client;
-
-public class HttpReceiverTest
-{
-//    @Rule
-//    public final TestTracker tracker = new TestTracker();
-//
-//    private HttpClient client;
-//    private HttpDestination destination;
-//    private ByteArrayEndPoint endPoint;
-//    private HttpConnection connection;
-//    private HttpConversation conversation;
-//
-//    @Before
-//    public void init() throws Exception
-//    {
-//        client = new HttpClient();
-//        client.start();
-//        destination = new HttpDestination(client, "http", "localhost", 8080);
-//        endPoint = new ByteArrayEndPoint();
-//        connection = new HttpConnection(client, endPoint, destination);
-//        conversation = new HttpConversation(client, 1);
-//    }
-//
-//    @After
-//    public void destroy() throws Exception
-//    {
-//        client.stop();
-//    }
-//
-//    protected HttpExchange newExchange()
-//    {
-//        HttpRequest request = new HttpRequest(client, URI.create("http://localhost"));
-//        FutureResponseListener listener = new FutureResponseListener(request);
-//        HttpExchange exchange = new HttpExchange(conversation, destination, request, Collections.<Response.ResponseListener>singletonList(listener));
-//        conversation.getExchanges().offer(exchange);
-//        connection.associate(exchange);
-//        exchange.requestComplete();
-//        exchange.terminateRequest();
-//        return exchange;
-//    }
-//
-//    @Test
-//    public void test_Receive_NoResponseContent() throws Exception
-//    {
-//        endPoint.setInput("" +
-//                "HTTP/1.1 200 OK\r\n" +
-//                "Content-length: 0\r\n" +
-//                "\r\n");
-//        HttpExchange exchange = newExchange();
-//        FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
-//        connection.receive();
-//
-//        Response response = listener.get(5, TimeUnit.SECONDS);
-//        Assert.assertNotNull(response);
-//        Assert.assertEquals(200, response.getStatus());
-//        Assert.assertEquals("OK", response.getReason());
-//        Assert.assertSame(HttpVersion.HTTP_1_1, response.getVersion());
-//        HttpFields headers = response.getHeaders();
-//        Assert.assertNotNull(headers);
-//        Assert.assertEquals(1, headers.size());
-//        Assert.assertEquals("0", headers.get(HttpHeader.CONTENT_LENGTH));
-//    }
-//
-//    @Test
-//    public void test_Receive_ResponseContent() throws Exception
-//    {
-//        String content = "0123456789ABCDEF";
-//        endPoint.setInput("" +
-//                "HTTP/1.1 200 OK\r\n" +
-//                "Content-length: " + content.length() + "\r\n" +
-//                "\r\n" +
-//                content);
-//        HttpExchange exchange = newExchange();
-//        FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
-//        connection.receive();
-//
-//        Response response = listener.get(5, TimeUnit.SECONDS);
-//        Assert.assertNotNull(response);
-//        Assert.assertEquals(200, response.getStatus());
-//        Assert.assertEquals("OK", response.getReason());
-//        Assert.assertSame(HttpVersion.HTTP_1_1, response.getVersion());
-//        HttpFields headers = response.getHeaders();
-//        Assert.assertNotNull(headers);
-//        Assert.assertEquals(1, headers.size());
-//        Assert.assertEquals(String.valueOf(content.length()), headers.get(HttpHeader.CONTENT_LENGTH));
-//        String received = listener.getContentAsString(StandardCharsets.UTF_8);
-//        Assert.assertEquals(content, received);
-//    }
-//
-//    @Test
-//    public void test_Receive_ResponseContent_EarlyEOF() throws Exception
-//    {
-//        String content1 = "0123456789";
-//        String content2 = "ABCDEF";
-//        endPoint.setInput("" +
-//                "HTTP/1.1 200 OK\r\n" +
-//                "Content-length: " + (content1.length() + content2.length()) + "\r\n" +
-//                "\r\n" +
-//                content1);
-//        HttpExchange exchange = newExchange();
-//        FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
-//        connection.receive();
-//        endPoint.setInputEOF();
-//        connection.receive();
-//
-//        try
-//        {
-//            listener.get(5, TimeUnit.SECONDS);
-//            Assert.fail();
-//        }
-//        catch (ExecutionException e)
-//        {
-//            Assert.assertTrue(e.getCause() instanceof EOFException);
-//        }
-//    }
-//
-//    @Test
-//    public void test_Receive_ResponseContent_IdleTimeout() throws Exception
-//    {
-//        endPoint.setInput("" +
-//                "HTTP/1.1 200 OK\r\n" +
-//                "Content-length: 1\r\n" +
-//                "\r\n");
-//        HttpExchange exchange = newExchange();
-//        FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
-//        connection.receive();
-//        // Simulate an idle timeout
-//        connection.idleTimeout();
-//
-//        try
-//        {
-//            listener.get(5, TimeUnit.SECONDS);
-//            Assert.fail();
-//        }
-//        catch (ExecutionException e)
-//        {
-//            Assert.assertTrue(e.getCause() instanceof TimeoutException);
-//        }
-//    }
-//
-//    @Test
-//    public void test_Receive_BadResponse() throws Exception
-//    {
-//        endPoint.setInput("" +
-//                "HTTP/1.1 200 OK\r\n" +
-//                "Content-length: A\r\n" +
-//                "\r\n");
-//        HttpExchange exchange = newExchange();
-//        FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
-//        connection.receive();
-//
-//        try
-//        {
-//            listener.get(5, TimeUnit.SECONDS);
-//            Assert.fail();
-//        }
-//        catch (ExecutionException e)
-//        {
-//            Assert.assertTrue(e.getCause() instanceof HttpResponseException);
-//        }
-//    }
-//
-//    @Test
-//    public void test_Receive_GZIPResponseContent_Fragmented() throws Exception
-//    {
-//        byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-//        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-//        try (GZIPOutputStream gzipOutput = new GZIPOutputStream(baos))
-//        {
-//            gzipOutput.write(data);
-//        }
-//        byte[] gzip = baos.toByteArray();
-//
-//        endPoint.setInput("" +
-//                "HTTP/1.1 200 OK\r\n" +
-//                "Content-Length: " + gzip.length + "\r\n" +
-//                "Content-Encoding: gzip\r\n" +
-//                "\r\n");
-//        HttpExchange exchange = newExchange();
-//        FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
-//        connection.receive();
-//        endPoint.reset();
-//
-//        ByteBuffer buffer = ByteBuffer.wrap(gzip);
-//        int fragment = buffer.limit() - 1;
-//        buffer.limit(fragment);
-//        endPoint.setInput(buffer);
-//        connection.receive();
-//        endPoint.reset();
-//
-//        buffer.limit(gzip.length);
-//        buffer.position(fragment);
-//        endPoint.setInput(buffer);
-//        connection.receive();
-//
-//        ContentResponse response = listener.get(5, TimeUnit.SECONDS);
-//        Assert.assertNotNull(response);
-//        Assert.assertEquals(200, response.getStatus());
-//        Assert.assertArrayEquals(data, response.getContent());
-//    }
-}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpSenderTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpSenderTest.java
deleted file mode 100644
index 3883ebf..0000000
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpSenderTest.java
+++ /dev/null
@@ -1,280 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.client;
-
-public class HttpSenderTest
-{
-//    @Rule
-//    public final TestTracker tracker = new TestTracker();
-//
-//    private HttpClient client;
-//
-//    @Before
-//    public void init() throws Exception
-//    {
-//        client = new HttpClient();
-//        client.start();
-//    }
-//
-//    @After
-//    public void destroy() throws Exception
-//    {
-//        client.stop();
-//    }
-//
-//    @Test
-//    public void test_Send_NoRequestContent() throws Exception
-//    {
-//        ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
-//        HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080);
-//        HttpConnection connection = new HttpConnection(client, endPoint, destination);
-//        Request request = client.newRequest(URI.create("http://localhost/"));
-//        final CountDownLatch headersLatch = new CountDownLatch(1);
-//        final CountDownLatch successLatch = new CountDownLatch(1);
-//        request.listener(new Request.Listener.Adapter()
-//        {
-//            @Override
-//            public void onHeaders(Request request)
-//            {
-//                headersLatch.countDown();
-//            }
-//
-//            @Override
-//            public void onSuccess(Request request)
-//            {
-//                successLatch.countDown();
-//            }
-//        });
-//        connection.send(request, (Response.CompleteListener)null);
-//
-//        String requestString = endPoint.takeOutputString();
-//        Assert.assertTrue(requestString.startsWith("GET "));
-//        Assert.assertTrue(requestString.endsWith("\r\n\r\n"));
-//        Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
-//        Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
-//    }
-//
-//    @Slow
-//    @Test
-//    public void test_Send_NoRequestContent_IncompleteFlush() throws Exception
-//    {
-//        ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
-//        HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080);
-//        HttpConnection connection = new HttpConnection(client, endPoint, destination);
-//        Request request = client.newRequest(URI.create("http://localhost/"));
-//        connection.send(request, (Response.CompleteListener)null);
-//
-//        // This take will free space in the buffer and allow for the write to complete
-//        StringBuilder builder = new StringBuilder(endPoint.takeOutputString());
-//
-//        // Wait for the write to complete
-//        TimeUnit.SECONDS.sleep(1);
-//
-//        String chunk = endPoint.takeOutputString();
-//        while (chunk.length() > 0)
-//        {
-//            builder.append(chunk);
-//            chunk = endPoint.takeOutputString();
-//        }
-//
-//        String requestString = builder.toString();
-//        Assert.assertTrue(requestString.startsWith("GET "));
-//        Assert.assertTrue(requestString.endsWith("\r\n\r\n"));
-//    }
-//
-//    @Test
-//    public void test_Send_NoRequestContent_Exception() throws Exception
-//    {
-//        ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
-//        // Shutdown output to trigger the exception on write
-//        endPoint.shutdownOutput();
-//        HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080);
-//        HttpConnection connection = new HttpConnection(client, endPoint, destination);
-//        Request request = client.newRequest(URI.create("http://localhost/"));
-//        final CountDownLatch failureLatch = new CountDownLatch(2);
-//        request.listener(new Request.Listener.Adapter()
-//        {
-//            @Override
-//            public void onFailure(Request request, Throwable x)
-//            {
-//                failureLatch.countDown();
-//            }
-//        });
-//        connection.send(request, new Response.Listener.Adapter()
-//        {
-//            @Override
-//            public void onComplete(Result result)
-//            {
-//                Assert.assertTrue(result.isFailed());
-//                failureLatch.countDown();
-//            }
-//        });
-//
-//        Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS));
-//    }
-//
-//    @Test
-//    public void test_Send_NoRequestContent_IncompleteFlush_Exception() throws Exception
-//    {
-//        ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
-//        HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080);
-//        HttpConnection connection = new HttpConnection(client, endPoint, destination);
-//        Request request = client.newRequest(URI.create("http://localhost/"));
-//        final CountDownLatch failureLatch = new CountDownLatch(2);
-//        request.listener(new Request.Listener.Adapter()
-//        {
-//            @Override
-//            public void onFailure(Request request, Throwable x)
-//            {
-//                failureLatch.countDown();
-//            }
-//        });
-//        connection.send(request, new Response.Listener.Adapter()
-//        {
-//            @Override
-//            public void onComplete(Result result)
-//            {
-//                Assert.assertTrue(result.isFailed());
-//                failureLatch.countDown();
-//            }
-//        });
-//
-//        // Shutdown output to trigger the exception on write
-//        endPoint.shutdownOutput();
-//        // This take will free space in the buffer and allow for the write to complete
-//        // although it will fail because we shut down the output
-//        endPoint.takeOutputString();
-//
-//        Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS));
-//    }
-//
-//    @Test
-//    public void test_Send_SmallRequestContent_InOneBuffer() throws Exception
-//    {
-//        ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
-//        HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080);
-//        HttpConnection connection = new HttpConnection(client, endPoint, destination);
-//        Request request = client.newRequest(URI.create("http://localhost/"));
-//        String content = "abcdef";
-//        request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content.getBytes(StandardCharsets.UTF_8))));
-//        final CountDownLatch headersLatch = new CountDownLatch(1);
-//        final CountDownLatch successLatch = new CountDownLatch(1);
-//        request.listener(new Request.Listener.Adapter()
-//        {
-//            @Override
-//            public void onHeaders(Request request)
-//            {
-//                headersLatch.countDown();
-//            }
-//
-//            @Override
-//            public void onSuccess(Request request)
-//            {
-//                successLatch.countDown();
-//            }
-//        });
-//        connection.send(request, (Response.CompleteListener)null);
-//
-//        String requestString = endPoint.takeOutputString();
-//        Assert.assertTrue(requestString.startsWith("GET "));
-//        Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content));
-//        Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
-//        Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
-//    }
-//
-//    @Test
-//    public void test_Send_SmallRequestContent_InTwoBuffers() throws Exception
-//    {
-//        ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
-//        HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080);
-//        HttpConnection connection = new HttpConnection(client, endPoint, destination);
-//        Request request = client.newRequest(URI.create("http://localhost/"));
-//        String content1 = "0123456789";
-//        String content2 = "abcdef";
-//        request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8))));
-//        final CountDownLatch headersLatch = new CountDownLatch(1);
-//        final CountDownLatch successLatch = new CountDownLatch(1);
-//        request.listener(new Request.Listener.Adapter()
-//        {
-//            @Override
-//            public void onHeaders(Request request)
-//            {
-//                headersLatch.countDown();
-//            }
-//
-//            @Override
-//            public void onSuccess(Request request)
-//            {
-//                successLatch.countDown();
-//            }
-//        });
-//        connection.send(request, (Response.CompleteListener)null);
-//
-//        String requestString = endPoint.takeOutputString();
-//        Assert.assertTrue(requestString.startsWith("GET "));
-//        Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content1 + content2));
-//        Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
-//        Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
-//    }
-//
-//    @Test
-//    public void test_Send_SmallRequestContent_Chunked_InTwoChunks() throws Exception
-//    {
-//        ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
-//        HttpDestination destination = new HttpDestination(client, "http", "localhost", 8080);
-//        HttpConnection connection = new HttpConnection(client, endPoint, destination);
-//        Request request = client.newRequest(URI.create("http://localhost/"));
-//        String content1 = "0123456789";
-//        String content2 = "ABCDEF";
-//        request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8)))
-//        {
-//            @Override
-//            public long getLength()
-//            {
-//                return -1;
-//            }
-//        });
-//        final CountDownLatch headersLatch = new CountDownLatch(1);
-//        final CountDownLatch successLatch = new CountDownLatch(1);
-//        request.listener(new Request.Listener.Adapter()
-//        {
-//            @Override
-//            public void onHeaders(Request request)
-//            {
-//                headersLatch.countDown();
-//            }
-//
-//            @Override
-//            public void onSuccess(Request request)
-//            {
-//                successLatch.countDown();
-//            }
-//        });
-//        connection.send(request, (Response.CompleteListener)null);
-//
-//        String requestString = endPoint.takeOutputString();
-//        Assert.assertTrue(requestString.startsWith("GET "));
-//        String content = Integer.toHexString(content1.length()).toUpperCase(Locale.ENGLISH) + "\r\n" + content1 + "\r\n";
-//        content += Integer.toHexString(content2.length()).toUpperCase(Locale.ENGLISH) + "\r\n" + content2 + "\r\n";
-//        content += "0\r\n\r\n";
-//        Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content));
-//        Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
-//        Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
-//    }
-}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java
new file mode 100644
index 0000000..0be2b91
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java
@@ -0,0 +1,262 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client.http;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.zip.GZIPOutputStream;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.HttpRequest;
+import org.eclipse.jetty.client.HttpResponseException;
+import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.util.FutureResponseListener;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.io.ByteArrayEndPoint;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class HttpReceiverOverHTTPTest
+{
+    @Rule
+    public final TestTracker tracker = new TestTracker();
+
+    private HttpClient client;
+    private HttpDestinationOverHTTP destination;
+    private ByteArrayEndPoint endPoint;
+    private HttpConnectionOverHTTP connection;
+
+    @Before
+    public void init() throws Exception
+    {
+        client = new HttpClient();
+        client.start();
+        destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+        endPoint = new ByteArrayEndPoint();
+        connection = new HttpConnectionOverHTTP(endPoint, destination);
+    }
+
+    @After
+    public void destroy() throws Exception
+    {
+        client.stop();
+    }
+
+    protected HttpExchange newExchange()
+    {
+        HttpRequest request = (HttpRequest)client.newRequest("http://localhost");
+        FutureResponseListener listener = new FutureResponseListener(request);
+        HttpExchange exchange = new HttpExchange(destination, request, Collections.<Response.ResponseListener>singletonList(listener));
+        connection.getHttpChannel().associate(exchange);
+        exchange.requestComplete();
+        exchange.terminateRequest(null);
+        return exchange;
+    }
+
+    @Test
+    public void test_Receive_NoResponseContent() throws Exception
+    {
+        endPoint.setInput("" +
+                "HTTP/1.1 200 OK\r\n" +
+                "Content-length: 0\r\n" +
+                "\r\n");
+        HttpExchange exchange = newExchange();
+        FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
+        connection.getHttpChannel().receive();
+
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("OK", response.getReason());
+        Assert.assertSame(HttpVersion.HTTP_1_1, response.getVersion());
+        HttpFields headers = response.getHeaders();
+        Assert.assertNotNull(headers);
+        Assert.assertEquals(1, headers.size());
+        Assert.assertEquals("0", headers.get(HttpHeader.CONTENT_LENGTH));
+    }
+
+    @Test
+    public void test_Receive_ResponseContent() throws Exception
+    {
+        String content = "0123456789ABCDEF";
+        endPoint.setInput("" +
+                "HTTP/1.1 200 OK\r\n" +
+                "Content-length: " + content.length() + "\r\n" +
+                "\r\n" +
+                content);
+        HttpExchange exchange = newExchange();
+        FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
+        connection.getHttpChannel().receive();
+
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("OK", response.getReason());
+        Assert.assertSame(HttpVersion.HTTP_1_1, response.getVersion());
+        HttpFields headers = response.getHeaders();
+        Assert.assertNotNull(headers);
+        Assert.assertEquals(1, headers.size());
+        Assert.assertEquals(String.valueOf(content.length()), headers.get(HttpHeader.CONTENT_LENGTH));
+        String received = listener.getContentAsString(StandardCharsets.UTF_8);
+        Assert.assertEquals(content, received);
+    }
+
+    @Test
+    public void test_Receive_ResponseContent_EarlyEOF() throws Exception
+    {
+        String content1 = "0123456789";
+        String content2 = "ABCDEF";
+        endPoint.setInput("" +
+                "HTTP/1.1 200 OK\r\n" +
+                "Content-length: " + (content1.length() + content2.length()) + "\r\n" +
+                "\r\n" +
+                content1);
+        HttpExchange exchange = newExchange();
+        FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
+        connection.getHttpChannel().receive();
+        endPoint.setInputEOF();
+        connection.getHttpChannel().receive();
+
+        try
+        {
+            listener.get(5, TimeUnit.SECONDS);
+            Assert.fail();
+        }
+        catch (ExecutionException e)
+        {
+            Assert.assertTrue(e.getCause() instanceof EOFException);
+        }
+    }
+
+    @Test
+    public void test_Receive_ResponseContent_IdleTimeout() throws Exception
+    {
+        endPoint.setInput("" +
+                "HTTP/1.1 200 OK\r\n" +
+                "Content-length: 1\r\n" +
+                "\r\n");
+        HttpExchange exchange = newExchange();
+        FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
+        connection.getHttpChannel().receive();
+        // Simulate an idle timeout
+        connection.onReadTimeout();
+
+        try
+        {
+            listener.get(5, TimeUnit.SECONDS);
+            Assert.fail();
+        }
+        catch (ExecutionException e)
+        {
+            Assert.assertTrue(e.getCause() instanceof TimeoutException);
+        }
+    }
+
+    @Test
+    public void test_Receive_BadResponse() throws Exception
+    {
+        endPoint.setInput("" +
+                "HTTP/1.1 200 OK\r\n" +
+                "Content-length: A\r\n" +
+                "\r\n");
+        HttpExchange exchange = newExchange();
+        FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0);
+        connection.getHttpChannel().receive();
+
+        try
+        {
+            listener.get(5, TimeUnit.SECONDS);
+            Assert.fail();
+        }
+        catch (ExecutionException e)
+        {
+            Assert.assertTrue(e.getCause() instanceof HttpResponseException);
+        }
+    }
+
+    @Test
+    public void test_Receive_GZIPResponseContent_Fragmented() throws Exception
+    {
+        byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (GZIPOutputStream gzipOutput = new GZIPOutputStream(baos))
+        {
+            gzipOutput.write(data);
+        }
+        byte[] gzip = baos.toByteArray();
+
+        endPoint.setInput("" +
+                "HTTP/1.1 200 OK\r\n" +
+                "Content-Length: " + gzip.length + "\r\n" +
+                "Content-Encoding: gzip\r\n" +
+                "\r\n");
+
+        HttpRequest request = (HttpRequest)client.newRequest("http://localhost");
+        final CountDownLatch latch = new CountDownLatch(1);
+        FutureResponseListener listener = new FutureResponseListener(request)
+        {
+            @Override
+            public void onContent(Response response, ByteBuffer content)
+            {
+                super.onContent(response, content);
+                latch.countDown();
+            }
+        };
+        HttpExchange exchange = new HttpExchange(destination, request, Collections.<Response.ResponseListener>singletonList(listener));
+        connection.getHttpChannel().associate(exchange);
+        exchange.requestComplete();
+        exchange.terminateRequest(null);
+        connection.getHttpChannel().receive();
+        endPoint.reset();
+
+        ByteBuffer buffer = ByteBuffer.wrap(gzip);
+        int fragment = buffer.limit() - 1;
+        buffer.limit(fragment);
+        endPoint.setInput(buffer);
+        connection.getHttpChannel().receive();
+        endPoint.reset();
+
+        buffer.limit(gzip.length);
+        buffer.position(fragment);
+        endPoint.setInput(buffer);
+        connection.getHttpChannel().receive();
+
+        ContentResponse response = listener.get(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        Assert.assertArrayEquals(data, response.getContent());
+    }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java
new file mode 100644
index 0000000..afe1ec7
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpSenderOverHTTPTest.java
@@ -0,0 +1,302 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.client.http;
+
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.util.ByteBufferContentProvider;
+import org.eclipse.jetty.io.ByteArrayEndPoint;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.toolchain.test.annotation.Slow;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class HttpSenderOverHTTPTest
+{
+    @Rule
+    public final TestTracker tracker = new TestTracker();
+
+    private HttpClient client;
+
+    @Before
+    public void init() throws Exception
+    {
+        client = new HttpClient();
+        client.start();
+    }
+
+    @After
+    public void destroy() throws Exception
+    {
+        client.stop();
+    }
+
+    @Test
+    public void test_Send_NoRequestContent() throws Exception
+    {
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
+        HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+        HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination);
+        Request request = client.newRequest(URI.create("http://localhost/"));
+        final CountDownLatch headersLatch = new CountDownLatch(1);
+        final CountDownLatch successLatch = new CountDownLatch(1);
+        request.listener(new Request.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Request request)
+            {
+                headersLatch.countDown();
+            }
+
+            @Override
+            public void onSuccess(Request request)
+            {
+                successLatch.countDown();
+            }
+        });
+        connection.send(request, null);
+
+        String requestString = endPoint.takeOutputString();
+        Assert.assertTrue(requestString.startsWith("GET "));
+        Assert.assertTrue(requestString.endsWith("\r\n\r\n"));
+        Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Slow
+    @Test
+    public void test_Send_NoRequestContent_IncompleteFlush() throws Exception
+    {
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
+        HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+        HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination);
+        Request request = client.newRequest(URI.create("http://localhost/"));
+        connection.send(request, null);
+
+        // This take will free space in the buffer and allow for the write to complete
+        StringBuilder builder = new StringBuilder(endPoint.takeOutputString());
+
+        // Wait for the write to complete
+        TimeUnit.SECONDS.sleep(1);
+
+        String chunk = endPoint.takeOutputString();
+        while (chunk.length() > 0)
+        {
+            builder.append(chunk);
+            chunk = endPoint.takeOutputString();
+        }
+
+        String requestString = builder.toString();
+        Assert.assertTrue(requestString.startsWith("GET "));
+        Assert.assertTrue(requestString.endsWith("\r\n\r\n"));
+    }
+
+    @Test
+    public void test_Send_NoRequestContent_Exception() throws Exception
+    {
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
+        // Shutdown output to trigger the exception on write
+        endPoint.shutdownOutput();
+        HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+        HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination);
+        Request request = client.newRequest(URI.create("http://localhost/"));
+        final CountDownLatch failureLatch = new CountDownLatch(2);
+        request.listener(new Request.Listener.Adapter()
+        {
+            @Override
+            public void onFailure(Request request, Throwable x)
+            {
+                failureLatch.countDown();
+            }
+        });
+        connection.send(request, new Response.Listener.Adapter()
+        {
+            @Override
+            public void onComplete(Result result)
+            {
+                Assert.assertTrue(result.isFailed());
+                failureLatch.countDown();
+            }
+        });
+
+        Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void test_Send_NoRequestContent_IncompleteFlush_Exception() throws Exception
+    {
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
+        HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+        HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination);
+        Request request = client.newRequest(URI.create("http://localhost/"));
+        final CountDownLatch failureLatch = new CountDownLatch(2);
+        request.listener(new Request.Listener.Adapter()
+        {
+            @Override
+            public void onFailure(Request request, Throwable x)
+            {
+                failureLatch.countDown();
+            }
+        });
+        connection.send(request, new Response.Listener.Adapter()
+        {
+            @Override
+            public void onComplete(Result result)
+            {
+                Assert.assertTrue(result.isFailed());
+                failureLatch.countDown();
+            }
+        });
+
+        // Shutdown output to trigger the exception on write
+        endPoint.shutdownOutput();
+        // This take will free space in the buffer and allow for the write to complete
+        // although it will fail because we shut down the output
+        endPoint.takeOutputString();
+
+        Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void test_Send_SmallRequestContent_InOneBuffer() throws Exception
+    {
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
+        HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+        HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination);
+        Request request = client.newRequest(URI.create("http://localhost/"));
+        String content = "abcdef";
+        request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content.getBytes(StandardCharsets.UTF_8))));
+        final CountDownLatch headersLatch = new CountDownLatch(1);
+        final CountDownLatch successLatch = new CountDownLatch(1);
+        request.listener(new Request.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Request request)
+            {
+                headersLatch.countDown();
+            }
+
+            @Override
+            public void onSuccess(Request request)
+            {
+                successLatch.countDown();
+            }
+        });
+        connection.send(request, null);
+
+        String requestString = endPoint.takeOutputString();
+        Assert.assertTrue(requestString.startsWith("GET "));
+        Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content));
+        Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void test_Send_SmallRequestContent_InTwoBuffers() throws Exception
+    {
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
+        HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+        HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination);
+        Request request = client.newRequest(URI.create("http://localhost/"));
+        String content1 = "0123456789";
+        String content2 = "abcdef";
+        request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8))));
+        final CountDownLatch headersLatch = new CountDownLatch(1);
+        final CountDownLatch successLatch = new CountDownLatch(1);
+        request.listener(new Request.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Request request)
+            {
+                headersLatch.countDown();
+            }
+
+            @Override
+            public void onSuccess(Request request)
+            {
+                successLatch.countDown();
+            }
+        });
+        connection.send(request, null);
+
+        String requestString = endPoint.takeOutputString();
+        Assert.assertTrue(requestString.startsWith("GET "));
+        Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content1 + content2));
+        Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void test_Send_SmallRequestContent_Chunked_InTwoChunks() throws Exception
+    {
+        ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
+        HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080));
+        HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination);
+        Request request = client.newRequest(URI.create("http://localhost/"));
+        String content1 = "0123456789";
+        String content2 = "ABCDEF";
+        request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8)))
+        {
+            @Override
+            public long getLength()
+            {
+                return -1;
+            }
+        });
+        final CountDownLatch headersLatch = new CountDownLatch(1);
+        final CountDownLatch successLatch = new CountDownLatch(1);
+        request.listener(new Request.Listener.Adapter()
+        {
+            @Override
+            public void onHeaders(Request request)
+            {
+                headersLatch.countDown();
+            }
+
+            @Override
+            public void onSuccess(Request request)
+            {
+                successLatch.countDown();
+            }
+        });
+        connection.send(request, null);
+
+        String requestString = endPoint.takeOutputString();
+        Assert.assertTrue(requestString.startsWith("GET "));
+        String content = Integer.toHexString(content1.length()).toUpperCase(Locale.ENGLISH) + "\r\n" + content1 + "\r\n";
+        content += Integer.toHexString(content2.length()).toUpperCase(Locale.ENGLISH) + "\r\n" + content2 + "\r\n";
+        content += "0\r\n\r\n";
+        Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content));
+        Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
+    }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java
index c360f70..bdef5f2 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesServerTest.java
@@ -77,6 +77,7 @@
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 public class SslBytesServerTest extends SslBytesTest
@@ -237,7 +238,7 @@
             threadPool.shutdownNow();
     }
 
-    @Test
+    @Test(timeout=10000)
     public void testHandshake() throws Exception
     {
         final SSLSocket client = newClient();
@@ -1390,7 +1391,9 @@
         closeClient(client);
     }
 
-    @Test
+    // TODO work out why this test frequently fails
+    @Ignore
+    @Test(timeout=10000)
     public void testRequestWithContentWithRenegotiationInMiddleOfContentWhenRenegotiationIsForbidden() throws Exception
     {
         assumeJavaVersionSupportsTLSRenegotiations();
@@ -1616,7 +1619,7 @@
         closeClient(client);
     }
 
-    @Test
+    @Test(timeout=10000)
     public void testRequestWithBigContentWithRenegotiationInMiddleOfContentWithSplitBoundary() throws Exception
     {
         assumeJavaVersionSupportsTLSRenegotiations();
diff --git a/jetty-continuation/pom.xml b/jetty-continuation/pom.xml
index e7219dc..a926837 100644
--- a/jetty-continuation/pom.xml
+++ b/jetty-continuation/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-continuation</artifactId>
diff --git a/jetty-deploy/pom.xml b/jetty-deploy/pom.xml
index 979acb0..0325c56 100644
--- a/jetty-deploy/pom.xml
+++ b/jetty-deploy/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-deploy</artifactId>
diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml
index 5e43fa7..5003e02 100644
--- a/jetty-distribution/pom.xml
+++ b/jetty-distribution/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <artifactId>jetty-distribution</artifactId>
   <name>Jetty :: Distribution Assemblies</name>
@@ -302,7 +302,7 @@
             <configuration>
               <includeGroupIds>org.eclipse.jetty</includeGroupIds>
               <excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs</excludeGroupIds>
-              <excludeArtifactIds>jetty-all,jetty-jsp,jetty-start,jetty-monitor</excludeArtifactIds>
+              <excludeArtifactIds>jetty-all,jetty-jsp,apache-jsp,jetty-start,jetty-monitor</excludeArtifactIds>
               <includeTypes>jar</includeTypes>
               <outputDirectory>${assembly-directory}/lib</outputDirectory>
             </configuration>
@@ -458,6 +458,20 @@
             </configuration>
           </execution>
           <execution>
+            <id>copy-apache-jsp-deps</id>
+            <phase>generate-resources</phase>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.toolchain,javax.servlet.jsp,org.mortbay.jasper,org.mortbay.jasper,org.eclipse.jetty.orbit</includeGroupIds>
+              <includeArtifactIds>apache-jsp,javax.servlet.jsp-api,apache-el,org.eclipse.jdt.core</includeArtifactIds>
+              <includeTypes>jar</includeTypes>
+              <prependGroupId>true</prependGroupId>
+              <outputDirectory>${assembly-directory}/lib/apache-jsp</outputDirectory>
+            </configuration>
+          </execution>
+          <execution>
            <id>copy-jstl-api</id>
            <phase>generate-resources</phase>
            <goals>
@@ -485,6 +499,20 @@
            </configuration>
           </execution>
           <execution>
+           <id>copy-apache-jstl-deps</id>
+           <phase>generate-resources</phase>
+           <goals>
+             <goal>copy-dependencies</goal>
+           </goals>
+           <configuration>
+          <excludeGroupIds>org.glassfish.web</excludeGroupIds>
+          <includeArtifactIds>taglibs-standard-spec,taglibs-standard-impl</includeArtifactIds>
+              <prependGroupId>true</prependGroupId>
+              <includeTypes>jar</includeTypes>
+              <outputDirectory>${assembly-directory}/lib/apache-jstl</outputDirectory>
+           </configuration>
+          </execution>
+          <execution>
             <id>copy-jaspi-deps</id>
             <phase>generate-resources</phase>
             <goals>
@@ -526,7 +554,7 @@
                 <argument>jetty.home=${assembly-directory}</argument>
                 <argument>jetty.base=${assembly-directory}</argument>
                 <argument>--add-to-start=server,deploy,websocket,ext,resources</argument>
-                <argument>--add-to-startd=jsp,http</argument>
+                <argument>--add-to-startd=jsp,jstl,http</argument>
               </arguments>
             </configuration>
             <goals>
@@ -541,8 +569,8 @@
               <arguments>
                 <argument>jetty.home=${assembly-directory}</argument>
                 <argument>jetty.base=${assembly-directory}/demo-base</argument>
-                <argument>--add-to-start=server,continuation,deploy,ext,resources,client,annotations,jndi,servlets</argument>
-                <argument>--add-to-startd-ini=jsp,http,https</argument>
+                <argument>--add-to-start=server,continuation,deploy,websocket,ext,resources,client,annotations,jndi,servlets</argument>
+                <argument>--add-to-startd-ini=jsp,jstl,http,https</argument>
               </arguments>
             </configuration>
             <goals>
@@ -669,6 +697,11 @@
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-quickstart</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-start</artifactId>
       <version>${project.version}</version>
     </dependency>
@@ -699,6 +732,16 @@
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
+      <artifactId>apache-jsp</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>apache-jstl</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-plus</artifactId>
       <version>${project.version}</version>
     </dependency>
@@ -766,6 +809,11 @@
       <type>war</type>
     </dependency>
     <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-alpn-server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.eclipse.jetty.example-async-rest</groupId>
       <artifactId>example-async-rest-webapp</artifactId>
       <version>${project.version}</version>
diff --git a/jetty-distribution/src/main/resources/modules/jsp.mod b/jetty-distribution/src/main/resources/modules/jsp.mod
new file mode 100644
index 0000000..4924ed9
--- /dev/null
+++ b/jetty-distribution/src/main/resources/modules/jsp.mod
@@ -0,0 +1,20 @@
+#
+# Jetty JSP Module
+#
+
+[depend]
+servlet
+jsp-impl/${jsp-impl}-jsp
+
+[ini-template]
+# JSP Configuration
+
+# Select JSP implementation, choices are
+#   glassfish : The reference implementation 
+#               default in jetty <= 9.1
+#   apache    : The apache version 
+#               default jetty >= 9.2
+jsp-impl=apache
+
+# To use an non-jdk compiler for JSP compilation uncomment next line
+# -Dorg.apache.jasper.compiler.disablejsr199=true
diff --git a/jetty-distribution/src/main/resources/modules/jstl.mod b/jetty-distribution/src/main/resources/modules/jstl.mod
new file mode 100644
index 0000000..cb06244
--- /dev/null
+++ b/jetty-distribution/src/main/resources/modules/jstl.mod
@@ -0,0 +1,14 @@
+#
+# Jetty JSP Module
+#
+
+[depend]
+jsp
+jsp-impl/${jsp-impl}-jstl
+
+[ini-template]
+# JSTL Configuration
+# The glassfish jsp-impl includes JSTL by default and this module
+# is not required to activate it.
+# The apache jsp-impl does not include JSTL by default and this module
+# is required to put JSTL on the container classpath
diff --git a/jetty-distribution/src/main/resources/modules/protonego.mod b/jetty-distribution/src/main/resources/modules/protonego.mod
new file mode 100644
index 0000000..d7bba9f
--- /dev/null
+++ b/jetty-distribution/src/main/resources/modules/protonego.mod
@@ -0,0 +1,15 @@
+#
+# Protocol Negotiatin Selection Module
+#
+
+[depend]
+protonego-impl/${protonego}
+
+[ini-template]
+# Protocol Negotiation Implementation Selection
+#  choices are:
+#    'npn'  : original implementation for SPDY (now deprecated)
+#    'alpn' : replacement for NPN, in use by current SPDY implementations
+#             and the future HTTP/2 spec
+#  Note: java 1.8+ are ALPN only.
+protonego=alpn
diff --git a/jetty-fcgi/fcgi-client/pom.xml b/jetty-fcgi/fcgi-client/pom.xml
index aeb2d6d..fc7542e 100644
--- a/jetty-fcgi/fcgi-client/pom.xml
+++ b/jetty-fcgi/fcgi-client/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.fcgi</groupId>
         <artifactId>fcgi-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-fcgi/fcgi-distribution/pom.xml b/jetty-fcgi/fcgi-distribution/pom.xml
index c265796..81d03d9 100644
--- a/jetty-fcgi/fcgi-distribution/pom.xml
+++ b/jetty-fcgi/fcgi-distribution/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <artifactId>fcgi-parent</artifactId>
         <groupId>org.eclipse.jetty.fcgi</groupId>
-        <version>9.1.4-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-fcgi/fcgi-http-client-transport/pom.xml b/jetty-fcgi/fcgi-http-client-transport/pom.xml
index 61fe9fe..d911c12 100644
--- a/jetty-fcgi/fcgi-http-client-transport/pom.xml
+++ b/jetty-fcgi/fcgi-http-client-transport/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.eclipse.jetty.fcgi</groupId>
         <artifactId>fcgi-parent</artifactId>
-        <version>9.1.4-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-fcgi/fcgi-proxy/pom.xml b/jetty-fcgi/fcgi-proxy/pom.xml
index d313dc7..149d189 100644
--- a/jetty-fcgi/fcgi-proxy/pom.xml
+++ b/jetty-fcgi/fcgi-proxy/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <artifactId>fcgi-parent</artifactId>
         <groupId>org.eclipse.jetty.fcgi</groupId>
-        <version>9.1.4-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-fcgi/fcgi-server/pom.xml b/jetty-fcgi/fcgi-server/pom.xml
index 9335f29..4b343cb 100644
--- a/jetty-fcgi/fcgi-server/pom.xml
+++ b/jetty-fcgi/fcgi-server/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.fcgi</groupId>
         <artifactId>fcgi-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-fcgi/pom.xml b/jetty-fcgi/pom.xml
index 1812d57..d309fec 100644
--- a/jetty-fcgi/pom.xml
+++ b/jetty-fcgi/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty</groupId>
         <artifactId>jetty-project</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-http-spi/pom.xml b/jetty-http-spi/pom.xml
index 3697bbd..a21e2e3 100644
--- a/jetty-http-spi/pom.xml
+++ b/jetty-http-spi/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-http-spi</artifactId>
diff --git a/jetty-http/pom.xml b/jetty-http/pom.xml
index 0681b33..67d85ac 100644
--- a/jetty-http/pom.xml
+++ b/jetty-http/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <artifactId>jetty-project</artifactId>
     <groupId>org.eclipse.jetty</groupId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-http</artifactId>
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java
index ef13232..8a26268 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java
@@ -38,7 +38,8 @@
     DELETE,
     TRACE,
     CONNECT,
-    MOVE;
+    MOVE,
+    PROXY;
 
     /* ------------------------------------------------------------ */
     /**
@@ -48,7 +49,7 @@
      * @param limit The first non valid index
      * @return A HttpMethod if a match or null if no easy match.
      */
-    public static HttpMethod lookAheadGet(byte[] bytes, int position, int limit)
+    public static HttpMethod lookAheadGet(byte[] bytes, final int position, int limit)
     {
         int length=limit-position;
         if (length<4)
@@ -62,6 +63,8 @@
             case 'P':
                 if (bytes[position+1]=='O' && bytes[position+2]=='S' && bytes[position+3]=='T' && length>=5 && bytes[position+4]==' ')
                     return POST;
+                if (bytes[position+1]=='R' && bytes[position+2]=='O' && bytes[position+3]=='X' && length>=6 && bytes[position+4]=='Y' && bytes[position+5]==' ')
+                    return PROXY;
                 if (bytes[position+1]=='U' && bytes[position+2]=='T' && bytes[position+3]==' ')
                     return PUT;
                 break;
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
index 8443f67..ee40a74 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
@@ -104,6 +104,7 @@
         SPACE2,
         REQUEST_VERSION,
         REASON,
+        PROXY,
         HEADER,
         HEADER_IN_NAME,
         HEADER_VALUE,
@@ -403,7 +404,7 @@
      * otherwise skip white space until something else to parse.
      */
     private boolean quickStart(ByteBuffer buffer)
-    {
+    {    	
         if (_requestHandler!=null)
         {
             _method = HttpMethod.lookAheadGet(buffer);
@@ -411,6 +412,7 @@
             {
                 _methodString = _method.asString();
                 buffer.position(buffer.position()+_methodString.length()+1);
+                
                 setState(State.SPACE1);
                 return false;
             }
@@ -655,7 +657,29 @@
                                 version=HttpVersion.lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.arrayOffset()+buffer.limit());
                             else
                                 version=HttpVersion.CACHE.getBest(buffer,0,buffer.remaining());
-                            if (version!=null) 
+                            if (version==null)
+                            {
+                                if (_method==HttpMethod.PROXY)
+                                {
+                                    if (!(_requestHandler instanceof ProxyHandler))
+                                        throw new BadMessage();
+                                    
+                                    _uri.flip();
+                                    String protocol=BufferUtil.toString(_uri);
+                                    // This is the proxy protocol, so we can assume entire first line is in buffer else 400
+                                    buffer.position(buffer.position()-1);
+                                    String sAddr = getProxyField(buffer);
+                                    String dAddr = getProxyField(buffer);
+                                    int sPort = BufferUtil.takeInt(buffer);
+                                    next(buffer);
+                                    int dPort = BufferUtil.takeInt(buffer);
+                                    next(buffer);
+                                    _state=State.START;
+                                    ((ProxyHandler)_requestHandler).proxied(protocol,sAddr,dAddr,sPort,dPort);
+                                    return false;
+                                }
+                            }
+                            else
                             {
                                 int pos = buffer.position()+version.asString().length()-1;
                                 if (pos<buffer.limit())
@@ -715,8 +739,7 @@
                         if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
                         {
                             int header_cache = _handler.getHeaderCacheSize();
-                            if (header_cache>0)
-                                _connectionFields=new ArrayTernaryTrie<>(header_cache);
+                            _connectionFields=new ArrayTernaryTrie<>(header_cache);                            
                         }
 
                         setState(State.HEADER);
@@ -1334,24 +1357,36 @@
 
     protected boolean parseContent(ByteBuffer buffer)
     {
+        int remaining=buffer.remaining();
+        if (remaining==0 && _state==State.CONTENT)
+        {
+            long content=_contentLength - _contentPosition;
+            if (content == 0)
+            {
+                setState(State.END);
+                if (_handler.messageComplete())
+                    return true;
+            }
+        }
+        
         // Handle _content
         byte ch;
-        while (_state.ordinal() < State.END.ordinal() && buffer.hasRemaining())
+        while (_state.ordinal() < State.END.ordinal() && remaining>0)
         {
             switch (_state)
             {
                 case EOF_CONTENT:
                     _contentChunk=buffer.asReadOnlyBuffer();
-                    _contentPosition += _contentChunk.remaining();
-                    buffer.position(buffer.position()+_contentChunk.remaining());
+                    _contentPosition += remaining;
+                    buffer.position(buffer.position()+remaining);
                     if (_handler.content(_contentChunk))
                         return true;
                     break;
 
                 case CONTENT:
                 {
-                    long remaining=_contentLength - _contentPosition;
-                    if (remaining == 0)
+                    long content=_contentLength - _contentPosition;
+                    if (content == 0)
                     {
                         setState(State.END);
                         if (_handler.messageComplete())
@@ -1362,25 +1397,25 @@
                         _contentChunk=buffer.asReadOnlyBuffer();
 
                         // limit content by expected size
-                        if (_contentChunk.remaining() > remaining)
+                        if (remaining > content)
                         {
                             // We can cast remaining to an int as we know that it is smaller than
                             // or equal to length which is already an int.
-                            _contentChunk.limit(_contentChunk.position()+(int)remaining);
+                            _contentChunk.limit(_contentChunk.position()+(int)content);
                         }
 
                         _contentPosition += _contentChunk.remaining();
                         buffer.position(buffer.position()+_contentChunk.remaining());
 
-                        boolean handle=_handler.content(_contentChunk);
+                        if (_handler.content(_contentChunk))
+                            return true;
+
                         if(_contentPosition == _contentLength)
                         {
                             setState(State.END);
                             if (_handler.messageComplete())
                                 return true;
                         }
-                        if (handle)
-                            return true;
                     }
                     break;
                 }
@@ -1440,8 +1475,8 @@
 
                 case CHUNK:
                 {
-                    int remaining=_chunkLength - _chunkPosition;
-                    if (remaining == 0)
+                    int chunk=_chunkLength - _chunkPosition;
+                    if (chunk == 0)
                     {
                         setState(State.CHUNKED_CONTENT);
                     }
@@ -1449,13 +1484,13 @@
                     {
                         _contentChunk=buffer.asReadOnlyBuffer();
 
-                        if (_contentChunk.remaining() > remaining)
-                            _contentChunk.limit(_contentChunk.position()+remaining);
-                        remaining=_contentChunk.remaining();
+                        if (remaining > chunk)
+                            _contentChunk.limit(_contentChunk.position()+chunk);
+                        chunk=_contentChunk.remaining();
 
-                        _contentPosition += remaining;
-                        _chunkPosition += remaining;
-                        buffer.position(buffer.position()+remaining);
+                        _contentPosition += chunk;
+                        _chunkPosition += chunk;
+                        buffer.position(buffer.position()+chunk);
                         if (_handler.content(_contentChunk))
                             return true;
                     }
@@ -1470,7 +1505,10 @@
 
                 default: 
                     break;
+                    
             }
+            
+            remaining=buffer.remaining();
         }
         return false;
     }
@@ -1586,6 +1624,11 @@
         public int getHeaderCacheSize();
     }
 
+    public interface ProxyHandler 
+    {
+        void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort);
+    }
+    
     public interface RequestHandler<T> extends HttpHandler<T>
     {
         /**
@@ -1618,4 +1661,20 @@
     {
         return _connectionFields;
     }
+
+    private String getProxyField(ByteBuffer buffer)
+    {
+        _string.setLength(0);
+        _length=0;
+        
+        while (buffer.hasRemaining())
+        {
+            // process each character
+            byte ch=next(buffer);
+            if (ch<=' ')
+                return _string.toString();
+            _string.append((char)ch);    
+        }
+        throw new BadMessage();
+    }
 }
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
index 38c77af..6bc6ae2 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
@@ -1398,6 +1398,63 @@
         
     }
 
+    @Test
+    public void testProxyProtocol() throws Exception
+    {
+        ByteBuffer buffer=BufferUtil
+            .toBuffer("PROXY TCP4 107.47.45.254 10.0.1.116 27689 80\015\012"
+                +"GET / HTTP/1.1\015\012"
+                +"Host: localhost \015\012"
+                +"Connection: close\015\012"+"\015\012"+"\015\012");
+
+        Handler handler=new Handler();
+        HttpParser parser=new HttpParser((HttpParser.RequestHandler)handler);
+        parseAll(parser, buffer);
+
+        assertTrue(_headerCompleted);
+        assertTrue(_messageCompleted);
+        assertEquals("GET", _methodOrVersion);
+        assertEquals("/", _uriOrStatus);
+        assertEquals("HTTP/1.1", _versionOrReason);
+        assertEquals("PROXY TCP4 107.47.45.254 10.0.1.116 27689 80", handler._proxy);
+        assertEquals("Host", _hdr[0]);
+        assertEquals("localhost", _val[0]);
+        assertEquals("Connection", _hdr[1]);
+        assertEquals("close", _val[1]);
+        assertEquals(1, _headers);
+    }
+
+    @Test
+    public void testSplitProxyHeaderParseTest() throws Exception
+    {
+        Handler handler=new Handler();
+        HttpParser parser=new HttpParser((HttpParser.RequestHandler)handler);
+
+        ByteBuffer buffer=BufferUtil.toBuffer("PROXY TCP4 207.47.45.254 10.0.1.116 27689 80\015\012");
+        parser.parseNext(buffer);
+
+        buffer=BufferUtil.toBuffer(
+            "GET / HTTP/1.1\015\012"
+                +"Host: localhost \015\012"
+                +"Connection: close\015\012"
+                +"\015\012"
+                +"\015\012");
+
+        parser.parseNext(buffer);
+        assertTrue(_headerCompleted);
+        assertTrue(_messageCompleted);
+        assertEquals("GET", _methodOrVersion);
+        assertEquals("/", _uriOrStatus);
+        assertEquals("HTTP/1.1", _versionOrReason);
+        assertEquals("PROXY TCP4 207.47.45.254 10.0.1.116 27689 80", handler._proxy);
+        assertEquals("Host", _hdr[0]);
+        assertEquals("localhost", _val[0]);
+        assertEquals("Connection", _hdr[1]);
+        assertEquals("close", _val[1]);
+        assertEquals(1, _headers);
+    }
+    
+
     @Before
     public void init()
     {
@@ -1429,9 +1486,10 @@
     private boolean _headerCompleted;
     private boolean _messageCompleted;
 
-    private class Handler implements HttpParser.RequestHandler<ByteBuffer>, HttpParser.ResponseHandler<ByteBuffer>
+    private class Handler implements HttpParser.RequestHandler<ByteBuffer>, HttpParser.ResponseHandler<ByteBuffer>, HttpParser.ProxyHandler
     {
         private HttpFields fields;
+        String _proxy;
 
         @Override
         public boolean content(ByteBuffer ref)
@@ -1539,5 +1597,11 @@
         {
             return 512;
         }
+
+        @Override
+        public void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort)
+        {
+            _proxy="PROXY "+protocol+" "+sAddr+" "+dAddr+" "+sPort+" "+dPort;
+        }
     }
 }
diff --git a/jetty-io/pom.xml b/jetty-io/pom.xml
index d146f2e..c587972 100644
--- a/jetty-io/pom.xml
+++ b/jetty-io/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <artifactId>jetty-project</artifactId>
     <groupId>org.eclipse.jetty</groupId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-io</artifactId>
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java
similarity index 92%
rename from jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnection.java
rename to jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java
index a2fc851..cd05630 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java
@@ -16,18 +16,13 @@
 //  ========================================================================
 //
 
-package org.eclipse.jetty.spdy.client;
+package org.eclipse.jetty.io;
 
 import java.io.IOException;
 import java.util.Map;
 import java.util.concurrent.Executor;
 import javax.net.ssl.SSLEngine;
 
-import org.eclipse.jetty.io.AbstractConnection;
-import org.eclipse.jetty.io.ClientConnectionFactory;
-import org.eclipse.jetty.io.Connection;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.RuntimeIOException;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnectionFactory.java
similarity index 92%
rename from jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
rename to jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnectionFactory.java
index 50a1e5b..ff38600 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnectionFactory.java
@@ -16,9 +16,8 @@
 //  ========================================================================
 //
 
-package org.eclipse.jetty.spdy.client;
+package org.eclipse.jetty.io;
 
-import org.eclipse.jetty.io.ClientConnectionFactory;
 
 public abstract class NegotiatingClientConnectionFactory implements ClientConnectionFactory
 {
diff --git a/jetty-jaas/pom.xml b/jetty-jaas/pom.xml
index f8f57e6..ddc80f9 100644
--- a/jetty-jaas/pom.xml
+++ b/jetty-jaas/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-jaas</artifactId>
diff --git a/jetty-jaspi/pom.xml b/jetty-jaspi/pom.xml
index e45825c..9f8fc89 100644
--- a/jetty-jaspi/pom.xml
+++ b/jetty-jaspi/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-jaspi</artifactId>
diff --git a/jetty-jmx/pom.xml b/jetty-jmx/pom.xml
index 803d084..56a5cae 100644
--- a/jetty-jmx/pom.xml
+++ b/jetty-jmx/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-jmx</artifactId>
diff --git a/jetty-jmx/src/main/config/modules/jmx.mod b/jetty-jmx/src/main/config/modules/jmx.mod
index f8cadb5..ee091c7 100644
--- a/jetty-jmx/src/main/config/modules/jmx.mod
+++ b/jetty-jmx/src/main/config/modules/jmx.mod
@@ -2,6 +2,9 @@
 # JMX Module
 #
 
+[depend]
+server
+
 [lib]
 lib/jetty-jmx-${jetty.version}.jar
 
diff --git a/jetty-jndi/pom.xml b/jetty-jndi/pom.xml
index 66ffd58..c40a600 100644
--- a/jetty-jndi/pom.xml
+++ b/jetty-jndi/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-jndi</artifactId>
diff --git a/jetty-jsp/pom.xml b/jetty-jsp/pom.xml
index 21c1dca..c9002c2 100644
--- a/jetty-jsp/pom.xml
+++ b/jetty-jsp/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-jsp</artifactId>
diff --git a/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jsp.mod b/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jsp.mod
new file mode 100644
index 0000000..130d2b3
--- /dev/null
+++ b/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jsp.mod
@@ -0,0 +1,8 @@
+#
+# Glassfish JSP Module
+#
+[name]
+jsp-impl
+
+[lib]
+lib/jsp/*.jar
diff --git a/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jstl.mod b/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jstl.mod
new file mode 100644
index 0000000..4b8e6f3
--- /dev/null
+++ b/jetty-jsp/src/main/config/modules/jsp-impl/glassfish-jstl.mod
@@ -0,0 +1,6 @@
+#
+# Glassfish JSTL
+[name]
+jstl-impl
+
+# This file is empty as glassfish jstl is provided by glassfish jsp
diff --git a/jetty-jsp/src/main/config/modules/jsp.mod b/jetty-jsp/src/main/config/modules/jsp.mod
deleted file mode 100644
index b67dfe2..0000000
--- a/jetty-jsp/src/main/config/modules/jsp.mod
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# Jetty JSP Module
-#
-
-[depend]
-servlet
-
-[lib]
-lib/jsp/*.jar
-
-[ini-template]
-# JSP Configuration
-# To use an non-jdk compiler for JSP compilation uncomment next line
-# -Dorg.apache.jasper.compiler.disablejsr199=true
diff --git a/jetty-jspc-maven-plugin/pom.xml b/jetty-jspc-maven-plugin/pom.xml
index ad5472a..2f709b2 100644
--- a/jetty-jspc-maven-plugin/pom.xml
+++ b/jetty-jspc-maven-plugin/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-jspc-maven-plugin</artifactId>
@@ -73,8 +73,18 @@
       </exclusions>
     </dependency>
     <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>apache-jsp</artifactId>
+      <version>${project.version}</version>
+     </dependency>
+    <dependency>
+      <groupId>org.apache.ant</groupId>
+      <artifactId>ant</artifactId>
+      <version>1.8.4</version>
+    </dependency>
+    <dependency>
         <groupId>org.eclipse.jetty</groupId>
-        <artifactId>jetty-jsp</artifactId>
+        <artifactId>apache-jstl</artifactId>
         <version>${project.version}</version>
       </dependency>
     </dependencies>
diff --git a/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java b/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java
index 1e6d103..ac3e45f 100644
--- a/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java
+++ b/jetty-jspc-maven-plugin/src/main/java/org/eclipse/jetty/jspc/plugin/JspcMojo.java
@@ -79,6 +79,7 @@
 public class JspcMojo extends AbstractMojo
 {
     public static final String END_OF_WEBAPP = "</web-app>";
+    public static final String PRECOMPILED_FLAG = "org.eclipse.jetty.jsp.precompiled";
 
 
     /**
@@ -207,7 +208,7 @@
     /**
      * Patterns of jars on the system path that contain tlds. Use | to separate each pattern.
      * 
-     * @parameter default-value=".*taglibs[^/]*\.jar|.*jstl-impl[^/]*\.jar$
+     * @parameter default-value=".*taglibs[^/]*\.jar|.*jstl[^/]*\.jar$
      */
     private String tldJarNamePatterns;
     
@@ -294,9 +295,9 @@
         jspc.setWebXmlFragment(webXmlFragment);
         jspc.setUriroot(webAppSourceDirectory);     
         jspc.setOutputDir(generatedClasses);
-        jspc.setClassPath(webAppClassPath.toString());
+        jspc.setClassPath(sysClassPath+System.getProperty("path.separator")+webAppClassPath.toString());
         jspc.setCompile(true);
-        jspc.setSystemClassPath(sysClassPath);
+        //jspc.setSystemClassPath(sysClassPath);
                
 
         // JspC#setExtensions() does not exist, so 
@@ -419,6 +420,10 @@
                         mergedWebXmlWriter.println(line);
                     }
                 }
+                
+                //put in a context init-param to flag that the contents have been precompiled
+                mergedWebXmlWriter.println("<context-param><param-name>"+PRECOMPILED_FLAG+"</param-name><param-value>true</param-value></context-param>");
+                
 
                 // put in the generated fragment
                 try (BufferedReader fragmentWebXmlReader = new BufferedReader(
@@ -541,13 +546,16 @@
      */
     private List<URL> getSystemJarsWithTlds() throws Exception
     {
+        getLog().debug("tld pattern=" + tldJarNamePatterns);   
         final List<URL> list = new ArrayList<URL>();
         List<URI> artifactUris = new ArrayList<URI>();
         Pattern pattern = Pattern.compile(tldJarNamePatterns);
         for (Iterator<Artifact> iter = pluginArtifacts.iterator(); iter.hasNext(); )
         {
             Artifact pluginArtifact = iter.next();
-            artifactUris.add(Resource.newResource(pluginArtifact.getFile()).getURI());
+            Resource res = Resource.newResource(pluginArtifact.getFile());
+            getLog().debug("scan jar: "+res.getURI());
+            artifactUris.add(res.getURI());
         }
         
         PatternMatcher matcher = new PatternMatcher()
diff --git a/jetty-maven-plugin/pom.xml b/jetty-maven-plugin/pom.xml
index b92a91f..eefa280 100644
--- a/jetty-maven-plugin/pom.xml
+++ b/jetty-maven-plugin/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-maven-plugin</artifactId>
@@ -122,19 +122,19 @@
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-jsp</artifactId>
+      <artifactId>apache-jsp</artifactId>
       <version>${project.version}</version>
     </dependency>
-      <!-- dependency>
-        <groupId>org.eclipse.jetty.orbit</groupId>
-        <artifactId>javax.activation</artifactId>
-        <scope>compile</scope>
-      </dependency -->
-      <dependency>
-        <groupId>javax.transaction</groupId>
-        <artifactId>javax.transaction-api</artifactId>
-        <scope>compile</scope>
-      </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>apache-jstl</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+     <dependency>
+       <groupId>javax.transaction</groupId>
+       <artifactId>javax.transaction-api</artifactId>
+       <scope>compile</scope>
+     </dependency>
   </dependencies>
   <reporting>
   <plugins>
diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java
index 818073e..e4d8348 100644
--- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java
+++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java
@@ -62,7 +62,7 @@
 {
     private static final Logger LOG = Log.getLogger(JettyWebAppContext.class);
 
-    private static final String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN = ".*/javax.servlet-[^/]*\\.jar$|.*/servlet-api-[^/]*\\.jar$";
+    private static final String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN = ".*/javax.servlet-[^/]*\\.jar$|.*/servlet-api-[^/]*\\.jar$|.*javax.servlet.jsp.jstl-[^/]*\\.jar";
     private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes";
     private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib";
 
diff --git a/jetty-monitor/pom.xml b/jetty-monitor/pom.xml
index a6fdceb..2b5611a 100644
--- a/jetty-monitor/pom.xml
+++ b/jetty-monitor/pom.xml
@@ -19,7 +19,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-monitor</artifactId>
diff --git a/jetty-nosql/pom.xml b/jetty-nosql/pom.xml
index d65a221..7c4b1c8 100644
--- a/jetty-nosql/pom.xml
+++ b/jetty-nosql/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-nosql</artifactId>
diff --git a/jetty-osgi/jetty-osgi-alpn/pom.xml b/jetty-osgi/jetty-osgi-alpn/pom.xml
new file mode 100644
index 0000000..3698b98
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-alpn/pom.xml
@@ -0,0 +1,47 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty.osgi</groupId>
+    <artifactId>jetty-osgi-project</artifactId>
+    <version>9.2.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>jetty-osgi-alpn</artifactId>
+  <name>Jetty :: OSGi ALPN Fragment</name>
+  <packaging>jar</packaging>
+  <properties>
+    <bundle-symbolic-name>org.eclipse.jetty.osgi.alpn.fragment</bundle-symbolic-name>
+  </properties>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>1.7</version>
+        <executions>
+          <execution>
+            <id>parse-version</id>
+            <goals>
+              <goal>parse-version</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestEntries>
+              <Bundle-ManifestVersion>2</Bundle-ManifestVersion>
+              <Bundle-SymbolicName>${bundle-symbolic-name};singleton:=true</Bundle-SymbolicName>
+              <Bundle-Name>Jetty OSGi ALPN Fragment</Bundle-Name>
+              <Bundle-Version>${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}</Bundle-Version>
+              <Export-Package>org.eclipse.jetty.alpn;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
+              <Fragment-Host>system.bundle;extension:=framework</Fragment-Host>
+            </manifestEntries>
+          </archive>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
index 0efecc3..8dfd29d 100644
--- a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-osgi-boot-jsp</artifactId>
diff --git a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
index 1bc7177..a785f58 100644
--- a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml
index a52ebfa..5a02af5 100644
--- a/jetty-osgi/jetty-osgi-boot/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-osgi-boot</artifactId>
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
index 79e350c..199bde3 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java
@@ -37,37 +37,81 @@
 public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
 {
     private static final Logger LOG = Log.getLogger(BundleClassLoaderHelper.class);
+    private static enum OSGiContainerType {EquinoxOld, EquinoxLuna, FelixOld, Felix403};
+    private static OSGiContainerType osgiContainer;
+    private static Class Equinox_BundleHost_Class;
+    private static Class Equinox_EquinoxBundle_Class;
+    private static Class Felix_BundleImpl_Class;
+    private static Class Felix_BundleWiring_Class;
+    //old equinox
+    private static Method Equinox_BundleHost_getBundleLoader_method;
+    private static Method Equinox_BundleLoader_createClassLoader_method;
+    //new equinox
+    private static Method Equinox_EquinoxBundle_getModuleClassLoader_Method;
+  
+    //new felix
+    private static Method Felix_BundleImpl_Adapt_Method;
+    //old felix
+    private static Field Felix_BundleImpl_m_Modules_Field;
+    private static Field Felix_ModuleImpl_m_ClassLoader_Field;
+    private static Method Felix_BundleWiring_getClassLoader_Method;
     
-    private static boolean identifiedOsgiImpl = false;
-
-    private static boolean isEquinox = false;
-
-    private static boolean isFelix = false;
-
-    private static void init(Bundle bundle)
+    
+    private static void checkContainerType (Bundle bundle)
     {
-        identifiedOsgiImpl = true;
+        if (osgiContainer != null)
+            return;
+        
         try
         {
-            isEquinox = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost") != null;
+            Equinox_BundleHost_Class = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost");
+            osgiContainer = OSGiContainerType.EquinoxOld;
+            return;
         }
-        catch (Throwable t)
+        catch (ClassNotFoundException e)
         {
-            isEquinox = false;
+            LOG.ignore(e);
         }
-        if (!isEquinox)
+
+        try
         {
+            Equinox_EquinoxBundle_Class = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.internal.framework.EquinoxBundle");
+            osgiContainer = OSGiContainerType.EquinoxLuna;
+            return;
+        }
+        catch (ClassNotFoundException e)
+        {
+            LOG.ignore(e);
+        }
+        
+        try
+        {       
+            //old felix or new felix?
+            Felix_BundleImpl_Class = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl");  
             try
             {
-                isFelix = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl") != null;  
+                Felix_BundleImpl_Adapt_Method = Felix_BundleImpl_Class.getDeclaredMethod("adapt", new Class[] {Class.class});
+                osgiContainer = OSGiContainerType.Felix403;
+                return;
             }
-            catch (Throwable t2)
+            catch (NoSuchMethodException e)
             {
-                isFelix = false;
+                osgiContainer = OSGiContainerType.FelixOld;
+                return;
             }
         }
+        catch (ClassNotFoundException e)
+        {
+            LOG.warn("Unknown OSGi container type");
+            return;
+        }
+        
     }
 
+  
+    
+    
+
     /**
      * Assuming the bundle is started.
      * 
@@ -77,7 +121,7 @@
     public ClassLoader getBundleClassLoader(Bundle bundle)
     {
         String bundleActivator = (String) bundle.getHeaders().get("Bundle-Activator");
-       
+   
         if (bundleActivator == null)
         {
             bundleActivator = (String) bundle.getHeaders().get("Jetty-ClassInBundle");
@@ -93,80 +137,135 @@
                 LOG.warn(e);
             }
         }
-        // resort to introspection
-        if (!identifiedOsgiImpl)
+        
+        // resort to introspection     
+        return getBundleClassLoaderForContainer(bundle);
+    }
+    
+    /**
+     * @param bundle
+     * @return
+     */
+    private ClassLoader getBundleClassLoaderForContainer (Bundle bundle)
+    {
+        checkContainerType (bundle);
+        if (osgiContainer == null)
         {
-            init(bundle);
-        }
-        if (isEquinox)
-        {
-            return internalGetEquinoxBundleClassLoader(bundle);
-        }
-        else if (isFelix) 
-        { 
-            return internalGetFelixBundleClassLoader(bundle); 
+            LOG.warn("No classloader for unknown OSGi container type");
+            return null;
         }
         
-        LOG.warn("No classloader found for bundle "+bundle.getSymbolicName());
-        return null;
+        switch (osgiContainer)
+        {
+            case EquinoxOld:
+            case EquinoxLuna:
+            {
+                return internalGetEquinoxBundleClassLoader(bundle);
+            }
+
+            case FelixOld:
+            case Felix403:
+            {
+                return internalGetFelixBundleClassLoader(bundle); 
+            }
+            default:
+            {
+                LOG.warn("No classloader found for bundle "+bundle.getSymbolicName());
+                return null;
+
+            }
+        }
     }
+    
+    
 
-    private static Method Equinox_BundleHost_getBundleLoader_method;
-
-    private static Method Equinox_BundleLoader_createClassLoader_method;
-
+    /**
+     * @param bundle
+     * @return
+     */
     private static ClassLoader internalGetEquinoxBundleClassLoader(Bundle bundle)
     {
-        // assume equinox:
-        try
+        if (osgiContainer == OSGiContainerType.EquinoxOld)
         {
-            if (Equinox_BundleHost_getBundleLoader_method == null)
+            try
             {
-                Equinox_BundleHost_getBundleLoader_method = 
-                    bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost").getDeclaredMethod("getBundleLoader", new Class[] {});
-                Equinox_BundleHost_getBundleLoader_method.setAccessible(true);
+                if (Equinox_BundleHost_getBundleLoader_method == null)
+                {
+                    Equinox_BundleHost_getBundleLoader_method = 
+                            Equinox_BundleHost_Class.getDeclaredMethod("getBundleLoader", new Class[] {});
+                    Equinox_BundleHost_getBundleLoader_method.setAccessible(true);
+                }
+                Object bundleLoader = Equinox_BundleHost_getBundleLoader_method.invoke(bundle, new Object[] {});
+                if (Equinox_BundleLoader_createClassLoader_method == null && bundleLoader != null)
+                {
+                    Equinox_BundleLoader_createClassLoader_method = 
+                            bundleLoader.getClass().getClassLoader().loadClass("org.eclipse.osgi.internal.loader.BundleLoader").getDeclaredMethod("createClassLoader", new Class[] {});
+                    Equinox_BundleLoader_createClassLoader_method.setAccessible(true);
+                }
+                return (ClassLoader) Equinox_BundleLoader_createClassLoader_method.invoke(bundleLoader, new Object[] {});
             }
-            Object bundleLoader = Equinox_BundleHost_getBundleLoader_method.invoke(bundle, new Object[] {});
-            if (Equinox_BundleLoader_createClassLoader_method == null && bundleLoader != null)
+            catch (ClassNotFoundException t)
             {
-                Equinox_BundleLoader_createClassLoader_method = 
-                    bundleLoader.getClass().getClassLoader().loadClass("org.eclipse.osgi.internal.loader.BundleLoader").getDeclaredMethod("createClassLoader", new Class[] {});
-                Equinox_BundleLoader_createClassLoader_method.setAccessible(true);
+                LOG.warn(t);
+                return null;
             }
-            return (ClassLoader) Equinox_BundleLoader_createClassLoader_method.invoke(bundleLoader, new Object[] {});
+            catch (Throwable t)
+            {
+                LOG.warn(t);
+                return null;
+            }
         }
-        catch (Throwable t)
+        
+        if (osgiContainer == OSGiContainerType.EquinoxLuna)
         {
-            LOG.warn(t);
+            try
+            {
+                if (Equinox_EquinoxBundle_getModuleClassLoader_Method == null)
+                    Equinox_EquinoxBundle_getModuleClassLoader_Method = Equinox_EquinoxBundle_Class.getDeclaredMethod("getModuleClassLoader", new Class[] {Boolean.TYPE});
+
+                Equinox_EquinoxBundle_getModuleClassLoader_Method.setAccessible(true);
+                return (ClassLoader)Equinox_EquinoxBundle_getModuleClassLoader_Method.invoke(bundle, new Object[] {Boolean.FALSE});
+            }
+            catch (Exception e)
+            {
+                LOG.warn(e);
+                return null;
+            }
         }
+        
         LOG.warn("No classloader for equinox platform for bundle "+bundle.getSymbolicName());
         return null;
     }
 
-    private static Field Felix_BundleImpl_m_modules_field;
+   
 
-    private static Field Felix_ModuleImpl_m_classLoader_field;
-    
-    private static Method Felix_adapt_method;
-    
-    private static Method Felix_bundle_wiring_getClassLoader_method;
-    
-    private static Class Felix_bundleWiringClazz;
 
-    private static Boolean isFelix403 = null;
-
+    /**
+     * @param bundle
+     * @return
+     */
     private static ClassLoader internalGetFelixBundleClassLoader(Bundle bundle)
     {
-        //firstly, try to find classes matching a newer version of felix
-        initFelix403(bundle);
-      
-        if (isFelix403.booleanValue())
+        
+        if (osgiContainer == OSGiContainerType.Felix403)
         {
             try
             {
-                Object wiring = Felix_adapt_method.invoke(bundle, new Object[] {Felix_bundleWiringClazz});
-                ClassLoader cl = (ClassLoader)Felix_bundle_wiring_getClassLoader_method.invoke(wiring);
-                return cl;
+                if (Felix_BundleWiring_Class == null)
+                    Felix_BundleWiring_Class = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring");
+
+
+                Felix_BundleImpl_Adapt_Method.setAccessible(true);
+
+                if (Felix_BundleWiring_getClassLoader_Method == null)
+                {
+                    Felix_BundleWiring_getClassLoader_Method = Felix_BundleWiring_Class.getDeclaredMethod("getClassLoader");
+                    Felix_BundleWiring_getClassLoader_Method.setAccessible(true);
+                }
+
+
+                Object wiring = Felix_BundleImpl_Adapt_Method.invoke(bundle, new Object[] {Felix_BundleWiring_Class});
+                return (ClassLoader)Felix_BundleWiring_getClassLoader_Method.invoke(wiring);
             }
             catch (Exception e)
             {
@@ -176,123 +275,92 @@
         }
 
 
-        // Fallback to trying earlier versions of felix.     
-        if (Felix_BundleImpl_m_modules_field == null)
-        {
+        if (osgiContainer == OSGiContainerType.FelixOld)
+        {     
             try
             {
-                Class bundleImplClazz = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl");  
-                Felix_BundleImpl_m_modules_field = bundleImplClazz.getDeclaredField("m_modules");
-                Felix_BundleImpl_m_modules_field.setAccessible(true);
-            }
-            catch (ClassNotFoundException e)
-            {
-                LOG.warn(e);
-            }
-            catch (NoSuchFieldException e)
-            {
-                LOG.warn(e);
-            }
-        }
+                if (Felix_BundleImpl_m_Modules_Field == null)
+                {
+                    Felix_BundleImpl_m_Modules_Field = Felix_BundleImpl_Class.getDeclaredField("m_modules");
+                    Felix_BundleImpl_m_Modules_Field.setAccessible(true);
+                }
 
-        // Figure out which version of the modules is exported
-        Object currentModuleImpl;
-        try
-        {
-            Object[] moduleArray = (Object[]) Felix_BundleImpl_m_modules_field.get(bundle);
-            currentModuleImpl = moduleArray[moduleArray.length - 1];
-        }
-        catch (Throwable t2)
-        {
-            try
-            {
-                List<Object> moduleArray = (List<Object>) Felix_BundleImpl_m_modules_field.get(bundle);
-                currentModuleImpl = moduleArray.get(moduleArray.size() - 1);
-            }
+                // Figure out which version of the modules is exported
+                Object currentModuleImpl;
+
+                try
+                {
+                    Object[] moduleArray = (Object[]) Felix_BundleImpl_m_Modules_Field.get(bundle);
+                    currentModuleImpl = moduleArray[moduleArray.length - 1];
+                }
+                catch (Throwable t2)
+                {
+                    try
+                    {
+                        List<Object> moduleArray = (List<Object>) Felix_BundleImpl_m_Modules_Field.get(bundle);
+                        currentModuleImpl = moduleArray.get(moduleArray.size() - 1);
+                    }
+                    catch (Exception e)
+                    {
+                        LOG.warn(e);
+                        return null;
+                    }
+                }
+
+                if (Felix_ModuleImpl_m_ClassLoader_Field == null && currentModuleImpl != null)
+                {
+                    try
+                    {
+                        Felix_ModuleImpl_m_ClassLoader_Field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.ModuleImpl").getDeclaredField("m_classLoader");
+                        Felix_ModuleImpl_m_ClassLoader_Field.setAccessible(true);
+                    }
+                    catch (Exception e)
+                    {
+                        LOG.warn(e);
+                        return null;
+                    }
+                }
+
+                // first make sure that the classloader is ready:
+                // the m_classLoader field must be initialized by the
+                // ModuleImpl.getClassLoader() private method.
+                ClassLoader cl = null;
+                try
+                {
+                    cl = (ClassLoader) Felix_ModuleImpl_m_ClassLoader_Field.get(currentModuleImpl);
+                    if (cl != null)
+                        return cl;
+                }
+                catch (Exception e)
+                {
+                    LOG.warn(e);
+                    return null;
+                }
+
+                // looks like it was not ready:
+                // the m_classLoader field must be initialized by the
+                // ModuleImpl.getClassLoader() private method.
+                // this call will do that.
+                try
+                {
+                    bundle.loadClass("java.lang.Object");
+                    cl = (ClassLoader) Felix_ModuleImpl_m_ClassLoader_Field.get(currentModuleImpl);
+                    return cl;
+                }
+                catch (Exception e)
+                {
+                    LOG.warn(e);
+                    return null;
+                }
+            }  
             catch (Exception e)
             {
                 LOG.warn(e);
                 return null;
             }
         }
-
-        if (Felix_ModuleImpl_m_classLoader_field == null && currentModuleImpl != null)
-        {
-            try
-            {
-                Felix_ModuleImpl_m_classLoader_field = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.ModuleImpl").getDeclaredField("m_classLoader");
-                Felix_ModuleImpl_m_classLoader_field.setAccessible(true);
-            }
-            catch (ClassNotFoundException e)
-            {
-                LOG.warn(e);
-                return null;
-            }   
-            catch (NoSuchFieldException e)
-            {
-                LOG.warn(e);
-                return null;
-            }
-        }
-        // first make sure that the classloader is ready:
-        // the m_classLoader field must be initialized by the
-        // ModuleImpl.getClassLoader() private method.
-        ClassLoader cl = null;
-        try
-        {
-            cl = (ClassLoader) Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl);
-            if (cl != null)
-                return cl;
-        }
-        catch (Exception e)
-        {
-            LOG.warn(e);
-            return null;
-        }
         
-        // looks like it was not ready:
-        // the m_classLoader field must be initialized by the
-        // ModuleImpl.getClassLoader() private method.
-        // this call will do that.
-        try
-        {
-            bundle.loadClass("java.lang.Object");
-            cl = (ClassLoader) Felix_ModuleImpl_m_classLoader_field.get(currentModuleImpl);
-            return cl;
-        }
-        catch (Exception e)
-        {
-            LOG.warn(e);
-            return null;
-        }
-    }
-
-
-    private static void initFelix403 (Bundle bundle)
-    {
-        //see if the version of Felix is a new one
-        if (isFelix403 == null)
-        {
-            try
-            {
-                Class bundleImplClazz = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl");
-                Felix_bundleWiringClazz = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring");
-                Felix_adapt_method = bundleImplClazz.getDeclaredMethod("adapt", new Class[] {Class.class});
-                Felix_adapt_method.setAccessible(true);
-                Felix_bundle_wiring_getClassLoader_method = Felix_bundleWiringClazz.getDeclaredMethod("getClassLoader");
-                Felix_bundle_wiring_getClassLoader_method.setAccessible(true);
-                isFelix403 = Boolean.TRUE;
-            }
-            catch (ClassNotFoundException e)
-            {
-                LOG.warn("Felix 4.x classes not found in environment");
-                isFelix403 = Boolean.FALSE;
-            }
-            catch (NoSuchMethodException e)
-            { 
-                LOG.warn("Felix 4.x classes not found in environment");
-                isFelix403 = Boolean.FALSE;
-            }           
-        }
+        LOG.warn("No classloader for felix platform for bundle "+bundle.getSymbolicName());
+        return null;
     }
 }
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
index c1d3bca..fed6f8b 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/DefaultFileLocatorHelper.java
@@ -63,7 +63,25 @@
     // DirZipBundleEntry
 
     private static Field ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = null;// ZipFile
+    
+    private static final String[] FILE_BUNDLE_ENTRY_CLASSES = {"org.eclipse.osgi.baseadaptor.bundlefile.FileBundleEntry","org.eclipse.osgi.storage.bundlefile.FileBundleEntry"};
+    private static final String[] ZIP_BUNDLE_ENTRY_CLASSES = {"org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleEntry","org.eclipse.osgi.storage.bundlefile.ZipBundleEntry"};
+    private static final String[] DIR_ZIP_BUNDLE_ENTRY_CLASSES = {"org.eclipse.osgi.baseadaptor.bundlefile.DirZipBundleEntry","org.eclipse.osgi.storage.bundlefile.DirZipBundleEntry"};
+    private static final String[] BUNDLE_URL_CONNECTION_CLASSES = {"org.eclipse.osgi.framework.internal.core.BundleURLConnection", "org.eclipse.osgi.storage.url.BundleURLConnection"};
 
+
+    public static boolean match (String name, String... names)
+    {
+        if (name == null || names == null)
+            return false;
+        boolean matched = false;
+        for (int i=0; i< names.length && !matched; i++)
+            if (name.equals(names[i]))
+                matched = true;
+        return matched;
+    }
+    
+    
     /**
      * Works with equinox, felix, nuxeo and probably more. Not exactly in the
      * spirit of OSGi but quite necessary to support self-contained webapps and
@@ -107,7 +125,8 @@
                 BUNDLE_ENTRY_FIELD.setAccessible(true);
             }
             Object bundleEntry = BUNDLE_ENTRY_FIELD.get(con);
-            if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.FileBundleEntry"))
+           
+            if (match(bundleEntry.getClass().getName(), FILE_BUNDLE_ENTRY_CLASSES))
             {
                 if (FILE_FIELD == null)
                 {
@@ -117,7 +136,7 @@
                 File f = (File) FILE_FIELD.get(bundleEntry);
                 return f.getParentFile().getParentFile();
             }
-            else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleEntry"))
+            else if (match(bundleEntry.getClass().getName(), ZIP_BUNDLE_ENTRY_CLASSES))
             {
                 url = bundle.getEntry("/");
 
@@ -144,7 +163,7 @@
                 ZipFile zipFile = (ZipFile) ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.get(zipBundleFile);
                 return new File(zipFile.getName());
             }
-            else if (bundleEntry.getClass().getName().equals("org.eclipse.osgi.baseadaptor.bundlefile.DirZipBundleEntry"))
+            else if (match (bundleEntry.getClass().getName(), DIR_ZIP_BUNDLE_ENTRY_CLASSES))
             {
                 // that will not happen as we did ask for the manifest not a
                 // directory.
@@ -309,7 +328,7 @@
 
             URLConnection conn = url.openConnection();
             conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
-            if (BUNDLE_URL_CONNECTION_getLocalURL == null && conn.getClass().getName().equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
+            if (BUNDLE_URL_CONNECTION_getLocalURL == null && match(conn.getClass().getName(), BUNDLE_URL_CONNECTION_CLASSES))
             {
                 BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL", null);
                 BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true);
@@ -340,7 +359,9 @@
 
             URLConnection conn = url.openConnection();
             conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
-            if (BUNDLE_URL_CONNECTION_getFileURL == null && conn.getClass().getName().equals("org.eclipse.osgi.framework.internal.core.BundleURLConnection"))
+            if (BUNDLE_URL_CONNECTION_getFileURL == null 
+                && 
+                match (conn.getClass().getName(), BUNDLE_URL_CONNECTION_CLASSES))
             {
                 BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL", null);
                 BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true);
diff --git a/jetty-osgi/jetty-osgi-httpservice/pom.xml b/jetty-osgi/jetty-osgi-httpservice/pom.xml
index c061b28..91087d7 100644
--- a/jetty-osgi/jetty-osgi-httpservice/pom.xml
+++ b/jetty-osgi/jetty-osgi-httpservice/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-httpservice</artifactId>
@@ -28,6 +28,7 @@
     <dependency>
       <groupId>org.eclipse.osgi</groupId>
       <artifactId>org.eclipse.osgi</artifactId>
+      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>javax.servlet</groupId>
diff --git a/jetty-osgi/jetty-osgi-npn/pom.xml b/jetty-osgi/jetty-osgi-npn/pom.xml
index 63a46e6..db524d9 100644
--- a/jetty-osgi/jetty-osgi-npn/pom.xml
+++ b/jetty-osgi/jetty-osgi-npn/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-osgi-npn</artifactId>
diff --git a/jetty-osgi/pom.xml b/jetty-osgi/pom.xml
index a89a1c6..6dbd99d 100644
--- a/jetty-osgi/pom.xml
+++ b/jetty-osgi/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <groupId>org.eclipse.jetty.osgi</groupId>
   <artifactId>jetty-osgi-project</artifactId>
@@ -22,12 +22,23 @@
     <module>jetty-osgi-boot</module>
     <module>jetty-osgi-boot-jsp</module>
     <module>jetty-osgi-boot-warurl</module>
-    <module>jetty-osgi-npn</module>
     <module>jetty-osgi-httpservice</module>
     <module>test-jetty-osgi-webapp</module>
     <module>test-jetty-osgi-context</module>
     <module>test-jetty-osgi</module>
+    <module>jetty-osgi-alpn</module>
   </modules>
+  <profiles>
+    <profile>
+      <id>npn</id>
+      <activation>
+        <jdk>1.7</jdk>
+      </activation>
+      <modules>
+        <module>jetty-osgi-npn</module>
+      </modules>
+    </profile>
+  </profiles>
   <build>
     <resources>
       <resource>
diff --git a/jetty-osgi/test-jetty-osgi-context/pom.xml b/jetty-osgi/test-jetty-osgi-context/pom.xml
index 46ad381..941b13b 100644
--- a/jetty-osgi/test-jetty-osgi-context/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-context/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>test-jetty-osgi-context</artifactId>
@@ -21,10 +21,12 @@
     <dependency>
     	<groupId>org.eclipse.osgi</groupId>
     	<artifactId>org.eclipse.osgi</artifactId>
+        <scope>provided</scope>
     </dependency>
     <dependency>
         <groupId>org.eclipse.osgi</groupId>
         <artifactId>org.eclipse.osgi.services</artifactId>
+        <scope>provided</scope>
     </dependency>
     <dependency>
         <groupId>org.eclipse.jetty.toolchain</groupId>
diff --git a/jetty-osgi/test-jetty-osgi-webapp/pom.xml b/jetty-osgi/test-jetty-osgi-webapp/pom.xml
index 23a67d6..1a0f37b 100644
--- a/jetty-osgi/test-jetty-osgi-webapp/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-webapp/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
@@ -21,10 +21,12 @@
     <dependency>
     	<groupId>org.eclipse.osgi</groupId>
     	<artifactId>org.eclipse.osgi</artifactId>
+        <scope>provided</scope>
     </dependency>
     <dependency>
         <groupId>org.eclipse.osgi</groupId>
         <artifactId>org.eclipse.osgi.services</artifactId>
+        <scope>provided</scope>
     </dependency>
   </dependencies>
 
diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml
index d417372..cf6810a 100644
--- a/jetty-osgi/test-jetty-osgi/pom.xml
+++ b/jetty-osgi/test-jetty-osgi/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty.osgi</groupId>
     <artifactId>jetty-osgi-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
@@ -14,40 +14,14 @@
     <bundle-symbolic-name>${project.groupId}.boot.test.spdy</bundle-symbolic-name>
     <jetty-orbit-url>http://download.eclipse.org/jetty/orbit/</jetty-orbit-url>
     <assembly-directory>target/distribution</assembly-directory>
-    <exam.version>2.6.0</exam.version>
-    <url.version>1.4.0</url.version>
-    <paxswissbox.version>1.5.1</paxswissbox.version>
-    <felixversion>4.0.3</felixversion>
+    <exam.version>3.4.0</exam.version>
+    <url.version>1.5.2</url.version>
     <injection.bundle.version>1.0</injection.bundle.version>
-    <runner.version>1.7.6</runner.version>
+    <runner.version>1.8.5</runner.version>
   </properties>
   <dependencies>
     <!-- Pax Exam Dependencies -->
-    <!-- OPS4J Swissbox Dependencies -->
-    <dependency>
-      <groupId>org.ops4j.pax.swissbox</groupId>
-      <artifactId>pax-swissbox-core</artifactId>
-      <version>${paxswissbox.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.ops4j.pax.swissbox</groupId>
-      <artifactId>pax-swissbox-extender</artifactId>
-      <version>${paxswissbox.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.ops4j.pax.swissbox</groupId>
-      <artifactId>pax-swissbox-lifecycle</artifactId>
-      <version>${paxswissbox.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.ops4j.pax.swissbox</groupId>
-      <artifactId>pax-swissbox-framework</artifactId>
-      <version>${paxswissbox.version}</version>
-      <scope>test</scope>
-    </dependency>
+
     <dependency>
       <groupId>org.ops4j.pax.exam</groupId>
       <artifactId>pax-exam</artifactId>
@@ -55,61 +29,21 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.apache.geronimo.specs</groupId>
-      <artifactId>geronimo-atinject_1.0_spec</artifactId>
-      <version>${injection.bundle.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>org.ops4j.pax.exam</groupId>
       <artifactId>pax-exam-inject</artifactId>
       <version>${exam.version}</version>
       <scope>test</scope>
     </dependency>
-    <dependency>
-     <groupId>org.apache.aries.spifly</groupId>
-      <artifactId>org.apache.aries.spifly.dynamic.bundle</artifactId>
-      <version>1.0.0</version>
-     <scope>test</scope>
-    </dependency>
     
 
-    <!-- Don't use the native container for now. Observed limitations:
-    - single test with a single configuration
-    - does not read the versions of the dependencies from the pom.xml
-    and hence hardcode the bundles versions in the source code instead
-    - no support for most configuration options for the OSGi container. -->
-    <!--dependency>
-      <groupId>org.ops4j.pax.exam</groupId>
-      <artifactId>pax-exam-container-native</artifactId>
-      <version>${exam.version}</version>
-      <scope>test</scope>
-    </dependency-->
-
-    <!-- container is not bad but not enough config parameters yet
-    can't pass the VMOption for npn-boot
+    <!-- use the forked container so we can pass it system properties eg for npn/alpn -->
     <dependency>
         <groupId>org.ops4j.pax.exam</groupId>
         <artifactId>pax-exam-container-forked</artifactId>
         <version>${exam.version}</version>
         <scope>test</scope>
     </dependency>
-    -->
-
-    <dependency>
-        <groupId>org.ops4j.pax.exam</groupId>
-        <artifactId>pax-exam-container-paxrunner</artifactId>
-        <version>${exam.version}</version>
-        <scope>test</scope>
-    </dependency>
-
-    <dependency>
-        <groupId>org.ops4j.pax.runner</groupId>
-        <artifactId>pax-runner-no-jcl</artifactId>
-        <version>${runner.version}</version>
-        <scope>test</scope>
-    </dependency>
-
+ 
     <dependency>
       <groupId>org.ops4j.pax.exam</groupId>
       <artifactId>pax-exam-junit4</artifactId>
@@ -134,29 +68,82 @@
       <version>${url.version}</version>
       <scope>test</scope>
     </dependency>
+
     <!-- OSGi R4 frameworks -->
+<!--
     <dependency>
       <groupId>org.apache.felix</groupId>
       <artifactId>org.apache.felix.framework</artifactId>
-      <version>${felixversion}</version>
+      <version>4.4.0</version>
       <scope>test</scope>
     </dependency>
     <dependency>
-        <groupId>org.ops4j.pax.exam</groupId>
-        <artifactId>pax-exam-testforge</artifactId>
-        <version>${exam.version}</version>
-        <scope>test</scope>
-    </dependency>
-    <!-- For sane logging -->
-<!--
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-log4j12</artifactId>
-      <version>1.6.1</version>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.enterprise</artifactId>
+      <version>5.0.0</version>
       <scope>test</scope>
     </dependency>
 -->
     <dependency>
+      <groupId>org.eclipse</groupId>
+      <artifactId>osgi</artifactId>
+      <version>3.9.1-v20140110-1610</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.eclipse.osgi</groupId>
+        <artifactId>org.eclipse.osgi.services</artifactId>
+        <scope>test</scope>
+    </dependency>
+
+
+    <!-- Jetty OSGi Deps -->
+    <dependency>
+      <groupId>org.eclipse.jetty.osgi</groupId>
+      <artifactId>jetty-osgi-boot</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>org.eclipse.osgi</groupId>
+          <artifactId>org.eclipse.osgi</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.eclipse.osgi</groupId>
+          <artifactId>org.eclipse.osgi.services</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.osgi</groupId>
+      <artifactId>jetty-osgi-boot-jsp</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>org.eclipse.osgi</groupId>
+          <artifactId>org.eclipse.osgi</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.eclipse.osgi</groupId>
+          <artifactId>org.eclipse.osgi.services</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-jsp-fragment</artifactId>
+      <version>2.3.3</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.osgi</groupId>
+      <artifactId>jetty-httpservice</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>javax.servlet-api</artifactId>
       <scope>test</scope>
@@ -167,36 +154,55 @@
       <version>1.1.1</version>
       <scope>test</scope>
     </dependency>
-    <!-- OSGi Deps -->
+
     <dependency>
-      <groupId>org.eclipse.jetty.osgi</groupId>
-      <artifactId>jetty-osgi-boot</artifactId>
-      <version>${project.version}</version>
-      <scope>provided</scope>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-atinject_1.0_spec</artifactId>
+      <version>${injection.bundle.version}</version>
+      <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.eclipse.jetty.osgi</groupId>
-      <artifactId>jetty-osgi-boot-jsp</artifactId>
-      <version>${project.version}</version>
-      <scope>provided</scope>
+     <groupId>org.apache.aries.spifly</groupId>
+      <artifactId>org.apache.aries.spifly.dynamic.bundle</artifactId>
+      <version>1.0.0</version>
+     <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.eclipse.jetty.toolchain</groupId>
-      <artifactId>jetty-jsp-fragment</artifactId>
-      <version>2.3.3</version>
-      <scope>provided</scope>
+        <groupId>org.ow2.asm</groupId>
+        <artifactId>asm</artifactId>
+        <version>4.1</version>
     </dependency>
     <dependency>
-      <groupId>org.eclipse.jetty.osgi</groupId>
-      <artifactId>jetty-httpservice</artifactId>
-      <version>${project.version}</version>
-      <scope>provided</scope>
+        <groupId>org.ow2.asm</groupId>
+        <artifactId>asm-commons</artifactId>
+        <version>4.1</version>
     </dependency>
+    <dependency>
+        <groupId>org.ow2.asm</groupId>
+        <artifactId>asm-tree</artifactId>
+        <version>4.1</version>
+    </dependency>
+
+
     <!-- Jetty Deps -->
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-annotations</artifactId>
       <scope>runtime</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>org.ow2.asm</groupId>
+          <artifactId>asm</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.ow2.asm</groupId>
+          <artifactId>asm-commons</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.ow2.asm</groupId>
+          <artifactId>asm-tree</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
@@ -324,14 +330,20 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.mortbay.jetty.npn</groupId>
-      <artifactId>npn-boot</artifactId>
-      <version>${npn.version}</version>
+      <groupId>org.mortbay.jetty.alpn</groupId>
+      <artifactId>alpn-boot</artifactId>
+      <version>${alpn.version}</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty.osgi</groupId>
-      <artifactId>jetty-osgi-npn</artifactId>
+      <artifactId>jetty-osgi-alpn</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-alpn-server</artifactId>
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
@@ -348,7 +360,9 @@
       <version>${project.version}</version>
       <scope>runtime</scope>
     </dependency>
+
     <!-- Eclipse OSGi Deps -->
+<!--
     <dependency>
       <groupId>org.eclipse.osgi</groupId>
       <artifactId>org.eclipse.osgi</artifactId>
@@ -360,7 +374,6 @@
       <scope>runtime</scope>
       <exclusions>
         <exclusion>
-          <!-- we use the servlet jar from orbit -->
           <groupId>javax.servlet</groupId>
           <artifactId>servlet-api</artifactId>
         </exclusion>
@@ -371,6 +384,9 @@
       <artifactId>servlet</artifactId>
       <scope>runtime</scope>
     </dependency>
+
+-->
+
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>test-jetty-webapp</artifactId>
@@ -402,9 +418,8 @@
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
           <!-- No point defining -Xbootclasspath as the actual OSGi VM is run as a forked process by pax-exam -->
-          <!--argLine>-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar</argLine-->
-          <!-- But we do pass the sys property of the npn-boot jar -->
-          <argLine>-Dmortbay-npn-boot=${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar</argLine>
+          <!-- But we do pass the sys property of the alpn-boot jar so that it can be configued inside tests -->
+          <argLine>-Dmortbay-alpn-boot=${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn.version}/alpn-boot-${alpn.version}.jar</argLine>
         </configuration>
       </plugin>
       <plugin>
diff --git a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-spdy.xml b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-spdy.xml
index 8c06a2c..2bc1fe3 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-spdy.xml
+++ b/jetty-osgi/test-jetty-osgi/src/test/config/etc/jetty-spdy.xml
@@ -4,7 +4,7 @@
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
     <!-- =========================================================== -->
-    <!-- HttpChannel Configuration                                   -->
+    <!-- Add HTTP Customizer for Secure request                      -->
     <!-- =========================================================== -->
     <New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
         <Set name="secureScheme">https</Set>
@@ -16,7 +16,7 @@
         <Set name="responseHeaderSize">8192</Set>
         <Call name="addCustomizer">
             <Arg>
-                <New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/>
+                <New class="org.eclipse.jetty.server.SecureRequestCustomizer"/>
             </Arg>
         </Call>
     </New>
@@ -36,53 +36,6 @@
     </New>
 
     <!-- =========================================================== -->
-    <!-- Add HTTP Customizer for Secure request                      -->
-    <!-- =========================================================== -->
-    <Ref refid="httpConfig">
-        <Call name="addCustomizer">
-            <Arg>
-                <New class="org.eclipse.jetty.server.SecureRequestCustomizer"/>
-            </Arg>
-        </Call>
-    </Ref>
-
-    <!-- =========================================================== -->
-    <!-- Create a push strategy which can be used by reference by    -->
-    <!-- individual connection factories below.                      -->
-    <!--                                                             -->
-    <!-- Consult the javadoc of o.e.j.spdy.server.http.ReferrerPushStrategy -->
-    <!-- for all configuration that may be set here.                 -->
-    <!-- =========================================================== -->
-    <New id="pushStrategy" class="org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy">
-        <!-- Uncomment to blacklist browsers for this push strategy. If one of the blacklisted Strings occurs in the
-             user-agent header sent by the client, push will be disabled for this browser. This is case insensitive" -->
-        <!--
-        <Set name="UserAgentBlacklist">
-            <Array type="String">
-                <Item>.*(?i)firefox/14.*</Item>
-                <Item>.*(?i)firefox/15.*</Item>
-                <Item>.*(?i)firefox/16.*</Item>
-            </Array>
-        </Set>
-        -->
-
-        <!-- Uncomment to override default file extensions to push -->
-        <!--
-        <Set name="PushRegexps">
-            <Array type="String">
-                <Item>.*\.css</Item>
-                <Item>.*\.js</Item>
-                <Item>.*\.png</Item>
-                <Item>.*\.jpg</Item>
-                <Item>.*\.gif</Item>
-            </Array>
-        </Set>
-        -->
-        <Set name="referrerPushPeriod">5000</Set>
-        <Set name="maxAssociatedResources">32</Set>
-    </New>
-
-    <!-- =========================================================== -->
     <!-- Set connectors                                              -->
     <!-- =========================================================== -->
     <Call id="sslConnector" name="addConnector">
@@ -95,7 +48,7 @@
                     <Array type="org.eclipse.jetty.server.ConnectionFactory">
                         <Item>
                             <New class="org.eclipse.jetty.server.SslConnectionFactory">
-                                <Arg name="next">npn</Arg>
+                                <Arg name="next">alpn</Arg>
                                 <Arg name="sslContextFactory">
                                     <Ref refid="sslContextFactory"/>
                                 </Arg>
@@ -103,7 +56,7 @@
                         </Item>
 
                         <Item>
-                            <New class="org.eclipse.jetty.spdy.server.NPNServerConnectionFactory">
+                            <New class="org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory">
                                 <Arg name="protocols">
                                     <Array type="String">
                                         <Item>spdy/3</Item>
@@ -124,8 +77,6 @@
                                 <!-- Set the initial window size for this SPDY connector. -->
                                 <!-- See: http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-2.6.8-WINDOW_UPDATE -->
                                 <Set name="initialWindowSize">65536</Set>
-                                <!-- Uncomment to enable the push strategy with id "pushStrategy" -->
-                                <!-- <Arg name="pushStrategy"><Ref refid="pushStrategy"/></Arg> -->
                             </New>
                         </Item>
 
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java
index 94ecd39..0dfc1bb 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootContextAsService.java
@@ -37,12 +37,13 @@
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
 import org.eclipse.jetty.server.handler.ContextHandler;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.CoreOptions;
 import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.junit.Configuration;
-import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.junit.PaxExam;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
@@ -55,12 +56,11 @@
  * Tests the ServiceContextProvider.
  * 
  */
-@RunWith(JUnit4TestRunner.class)
+@RunWith(PaxExam.class)
 public class TestJettyOSGiBootContextAsService
 {
-    private static final boolean LOGGING_ENABLED = false;
+    private static final String LOG_LEVEL = "WARN";
 
-    private static final boolean REMOTE_DEBUGGING = false;
 
     @Inject
     BundleContext bundleContext = null;
@@ -69,7 +69,6 @@
     public static Option[] configure()
     {
         ArrayList<Option> options = new ArrayList<Option>();
-        TestOSGiUtil.addMoreOSGiContainers(options);
         options.add(CoreOptions.junitBundles());
         options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
         options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*"));
@@ -79,22 +78,10 @@
         // to pick up and deploy
         options.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("test-jetty-osgi-context").versionAsInProject().start());
 
-        String logLevel = "WARN";
-        // Enable Logging
-        if (LOGGING_ENABLED)
-            logLevel = "INFO";
+        options.addAll(Arrays.asList(options(systemProperty("pax.exam.logging").value("none"))));
+        options.addAll(Arrays.asList(options(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL))));
+        options.addAll(Arrays.asList(options(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL))));
         
-
-        options.addAll(Arrays.asList(options(
-                                             // install log service using pax runners profile abstraction (there
-                                             // are more profiles, like DS)
-                                             // logProfile(),
-                                             // this is how you set the default log level when using pax logging
-                                             // (logProfile)
-                                             systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(logLevel),
-                                             systemProperty("org.eclipse.jetty.LEVEL").value(logLevel))));
-
-
         return options.toArray(new Option[options.size()]);
     }
 
@@ -117,6 +104,7 @@
         return options;
     }
 
+    @Ignore
     @Test
     public void assertAllBundlesActiveOrResolved()
     {
@@ -148,14 +136,6 @@
         ServiceReference[] refs = bundleContext.getServiceReferences(ContextHandler.class.getName(), null);
         assertNotNull(refs);
         assertEquals(1, refs.length);
-        //uncomment for debugging
-       /*  
-       String[] keys = refs[0].getPropertyKeys();
-        if (keys != null)
-        {
-            for (String k : keys)
-                System.err.println("service property: " + k + ", " + refs[0].getProperty(k));
-        }*/
         ContextHandler ch = (ContextHandler) bundleContext.getService(refs[0]);
         assertEquals("/acme", ch.getContextPath());
 
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootCore.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootCore.java
index 2c23318..d31760d 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootCore.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootCore.java
@@ -19,19 +19,23 @@
 package org.eclipse.jetty.osgi.test;
  
 import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import javax.inject.Inject;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.CoreOptions;
 import org.ops4j.pax.exam.MavenUtils;
 import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.junit.Configuration;
-import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.junit.PaxExam;
 import org.ops4j.pax.exam.options.MavenUrlReference.VersionResolver;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -40,27 +44,30 @@
 /**
  * Default OSGi setup integration test
  */
-@RunWith( JUnit4TestRunner.class )
+@RunWith( PaxExam.class )
 public class TestJettyOSGiBootCore
 {
- 
+    private static final String LOG_LEVEL = "WARN";
     public static int DEFAULT_JETTY_HTTP_PORT = 9876;
-     
+
     @Inject
     private BundleContext bundleContext;
- 
+
     @Configuration
     public Option[] config()
     {
         VersionResolver resolver = MavenUtils.asInProject();
         ArrayList<Option> options = new ArrayList<Option>();
-        TestOSGiUtil.addMoreOSGiContainers(options);
         options.addAll(provisionCoreJetty());
         options.add(CoreOptions.junitBundles());
         options.addAll(httpServiceJetty());
+        options.addAll(Arrays.asList(options(systemProperty("pax.exam.logging").value("none"))));
+        options.addAll(Arrays.asList(options(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL))));
+        options.addAll(Arrays.asList(options(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL))));
+
         return options.toArray(new Option[options.size()]);
     }
-     
+
     public static List<Option> provisionCoreJetty()
     { 
         List<Option> res = new ArrayList<Option>();
@@ -75,12 +82,21 @@
     public static List<Option> coreJettyDependencies()
     {
         List<Option> res = new ArrayList<Option>();
-        res.add(mavenBundle().groupId( "org.ow2.asm" ).artifactId( "asm" ).versionAsInProject().start());
-        res.add(mavenBundle().groupId( "org.ow2.asm" ).artifactId( "asm-commons" ).versionAsInProject().start());
-        res.add(mavenBundle().groupId( "org.ow2.asm" ).artifactId( "asm-tree" ).versionAsInProject().start());
-        res.add(mavenBundle().groupId( "org.apache.aries" ).artifactId( "org.apache.aries.util" ).version("1.0.0").start());
-        res.add(mavenBundle().groupId( "org.apache.aries.spifly" ).artifactId( "org.apache.aries.spifly.dynamic.bundle" ).version("1.0.0").start());
         
+        String jdk = System.getProperty("java.version");
+        int firstdot = jdk.indexOf(".");
+        jdk = jdk.substring(0,firstdot+2);
+        double version = Double.parseDouble(jdk);
+
+        if (version < 1.8)
+        {
+            res.add(mavenBundle().groupId( "org.ow2.asm" ).artifactId( "asm" ).versionAsInProject().start());
+            res.add(mavenBundle().groupId( "org.ow2.asm" ).artifactId( "asm-commons" ).versionAsInProject().start());
+            res.add(mavenBundle().groupId( "org.ow2.asm" ).artifactId( "asm-tree" ).versionAsInProject().start());
+            res.add(mavenBundle().groupId( "org.apache.aries" ).artifactId( "org.apache.aries.util" ).version("1.0.0").start());
+            res.add(mavenBundle().groupId( "org.apache.aries.spifly" ).artifactId( "org.apache.aries.spifly.dynamic.bundle" ).version("1.0.0").start());
+        }
+
         res.add(mavenBundle().groupId( "javax.servlet" ).artifactId( "javax.servlet-api" ).versionAsInProject().noStart());
         res.add(mavenBundle().groupId( "javax.annotation" ).artifactId( "javax.annotation-api" ).versionAsInProject().noStart());
         res.add(mavenBundle().groupId( "org.apache.geronimo.specs" ).artifactId( "geronimo-jta_1.1_spec" ).version("1.1.1").noStart());
@@ -103,7 +119,10 @@
         res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-client" ).versionAsInProject().noStart());  
         res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-jndi" ).versionAsInProject().noStart());
         res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-plus" ).versionAsInProject().noStart());
-        res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-annotations" ).versionAsInProject().start());
+        if (version < 1.8)
+        {
+            res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-annotations" ).versionAsInProject().start());
+        }
         res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-api" ).versionAsInProject().noStart());
         res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-common" ).versionAsInProject().noStart());
         res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-servlet" ).versionAsInProject().noStart());
@@ -124,9 +143,12 @@
         return res;
     }
      
+    @Ignore
     @Test
     public void assertAllBundlesActiveOrResolved() throws Exception
     {
+        
+        TestOSGiUtil.debugBundles(bundleContext);
         TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
     }
      
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootSpdy.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootSpdy.java
index 9558861..0cb66a5 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootSpdy.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootSpdy.java
@@ -31,21 +31,23 @@
 import javax.inject.Inject;
 
 import org.junit.Assert;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.CoreOptions;
 import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.junit.Configuration;
-import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 
 /**
  * SPDY setup.
  */
-@RunWith(JUnit4TestRunner.class)
+@RunWith(PaxExam.class)
 public class TestJettyOSGiBootSpdy
 {
-    private static final boolean LOGGING_ENABLED = false;
+    private static final String LOG_LEVEL = "WARN";
     
     private static final String JETTY_SPDY_PORT = "jetty.spdy.port";
 
@@ -58,31 +60,14 @@
     public Option[] config()
     {
         ArrayList<Option> options = new ArrayList<Option>();
-
-        TestOSGiUtil.addMoreOSGiContainers(options);
-
-      
         options.addAll(TestJettyOSGiBootWithJsp.configureJettyHomeAndPort("jetty-spdy.xml"));
         options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
+        options.addAll(spdyJettyDependencies());
         options.add(CoreOptions.junitBundles());
         options.addAll(TestJettyOSGiBootCore.httpServiceJetty());
-        options.addAll(spdyJettyDependencies());
-        
-        String logLevel = "WARN";
-        
-        // Enable Logging
-        if (LOGGING_ENABLED)
-            logLevel = "INFO";
-        
-
-        options.addAll(Arrays.asList(options(
-                                             // install log service using pax runners profile abstraction (there
-                                             // are more profiles, like DS)
-                                             // logProfile(),
-                                             // this is how you set the default log level when using pax logging
-                                             // (logProfile)
-                                             systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(logLevel),
-                                             systemProperty("org.eclipse.jetty.LEVEL").value(logLevel))));
+        options.addAll(Arrays.asList(options(systemProperty("pax.exam.logging").value("none"))));
+        options.addAll(Arrays.asList(options(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL))));
+        options.addAll(Arrays.asList(options(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL))));
         return options.toArray(new Option[options.size()]);
     }
 
@@ -90,37 +75,39 @@
     {
         List<Option> res = new ArrayList<Option>();
         res.add(CoreOptions.systemProperty(JETTY_SPDY_PORT).value(String.valueOf(DEFAULT_JETTY_SPDY_PORT)));
-        // java
-        // -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn-version}/npn-boot-${npn-version}.jar
-        // res.add(CoreOptions.vmOptions("-Xbootclasspath/p:"+System.getenv("HOME")+"/.m2/repository/org/mortbay/jetty/npn/npn-boot/"+npnBootVersion+"/npn-boot-"+npnBootVersion+".jar"));
-        String npnBoot = System.getProperty("mortbay-npn-boot");
-        if (npnBoot == null) { throw new IllegalStateException("Define path to npn boot jar as system property -Dmortbay-npn-boot"); }
-        File checkNpnBoot = new File(npnBoot);
-        if (!checkNpnBoot.exists()) { throw new IllegalStateException("Unable to find the npn boot jar here: " + npnBoot); }
 
-        res.add(CoreOptions.vmOptions("-Xbootclasspath/p:" + npnBoot));
-       // res.add(CoreOptions.bootDelegationPackages("org.eclipse.jetty.npn"));
+        String alpnBoot = System.getProperty("mortbay-alpn-boot");
+        if (alpnBoot == null) { throw new IllegalStateException("Define path to alpn boot jar as system property -Dmortbay-alpn-boot"); }
+        File checkALPNBoot = new File(alpnBoot);
+        if (!checkALPNBoot.exists()) { throw new IllegalStateException("Unable to find the alpn boot jar here: " + alpnBoot); }
 
-        res.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("jetty-osgi-npn").versionAsInProject().noStart());
+
+        res.add(CoreOptions.vmOptions("-Xbootclasspath/p:" + alpnBoot));
+
+        res.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("jetty-osgi-alpn").versionAsInProject().noStart());
+        res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-alpn-server").versionAsInProject().start());
+
+        res.add(mavenBundle().groupId("org.eclipse.jetty.spdy").artifactId("spdy-client").versionAsInProject().noStart());
         res.add(mavenBundle().groupId("org.eclipse.jetty.spdy").artifactId("spdy-core").versionAsInProject().noStart());
         res.add(mavenBundle().groupId("org.eclipse.jetty.spdy").artifactId("spdy-server").versionAsInProject().noStart());
         res.add(mavenBundle().groupId("org.eclipse.jetty.spdy").artifactId("spdy-http-common").versionAsInProject().noStart());
         res.add(mavenBundle().groupId("org.eclipse.jetty.spdy").artifactId("spdy-http-server").versionAsInProject().noStart());
-        res.add(mavenBundle().groupId("org.eclipse.jetty.spdy").artifactId("spdy-client").versionAsInProject().noStart());
         return res;
     }
 
     @Test
-    public void checkNpnBootOnBootstrapClasspath() throws Exception
+    public void checkALPNBootOnBootstrapClasspath() throws Exception
     {
-        Class<?> npn = Thread.currentThread().getContextClassLoader().loadClass("org.eclipse.jetty.npn.NextProtoNego");
-        Assert.assertNotNull(npn);
-        Assert.assertNull(npn.getClassLoader());
+        Class<?> alpn = Thread.currentThread().getContextClassLoader().loadClass("org.eclipse.jetty.alpn.ALPN");
+        Assert.assertNotNull(alpn);
+        Assert.assertNull(alpn.getClassLoader());
     }
 
+    @Ignore
     @Test
     public void assertAllBundlesActiveOrResolved()
     {
+        TestOSGiUtil.debugBundles(bundleContext);
         TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext);
     }
 
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java
index b43279c..cf2e30f 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java
@@ -38,12 +38,13 @@
 import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.webapp.WebAppContext;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.CoreOptions;
 import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.junit.Configuration;
-import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.junit.PaxExam;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 
@@ -58,12 +59,10 @@
  * httpservice web-bundle. Then make sure we can deploy an OSGi service on the
  * top of this.
  */
-@RunWith(JUnit4TestRunner.class)
+@RunWith(PaxExam.class)
 public class TestJettyOSGiBootWebAppAsService
 {
-    private static final boolean LOGGING_ENABLED = false;
-
-    private static final boolean REMOTE_DEBUGGING = false;
+    private static final String LOG_LEVEL = "WARN";
 
     @Inject
     BundleContext bundleContext = null;
@@ -72,8 +71,6 @@
     public static Option[] configure()
     {
         ArrayList<Option> options = new ArrayList<Option>();
-        TestOSGiUtil.addMoreOSGiContainers(options);
-
         options.add(CoreOptions.junitBundles());
         options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
         options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*"));
@@ -82,19 +79,9 @@
                                                "com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects"));
      
         options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
-        
-        String logLevel = "WARN";
-        if (LOGGING_ENABLED)
-            logLevel = "INFO";
-        
-        options.addAll(Arrays.asList(options(
-                                             // install log service using pax runners profile abstraction (there
-                                             // are more profiles, like DS)
-                                             // logProfile(),
-                                             // this is how you set the default log level when using pax logging
-                                             // (logProfile)
-                                             systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(logLevel),
-                                             systemProperty("org.eclipse.jetty.LEVEL").value(logLevel))));
+        options.addAll(Arrays.asList(options(systemProperty("pax.exam.logging").value("none"))));
+        options.addAll(Arrays.asList(options(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL))));
+        options.addAll(Arrays.asList(options(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL))));
 
         options.addAll(jspDependencies());
         return options.toArray(new Option[options.size()]);
@@ -139,6 +126,7 @@
         return res;
     }
 
+    @Ignore
     @Test
     public void assertAllBundlesActiveOrResolved()
     {
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java
index 10b21f5..b2a9be5 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java
@@ -38,25 +38,21 @@
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
 import org.ops4j.pax.exam.CoreOptions;
 import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.junit.Configuration;
-import org.ops4j.pax.exam.junit.JUnit4TestRunner;
-import org.osgi.framework.Bundle;
+import org.ops4j.pax.exam.junit.PaxExam;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
 
 /**
  * Pax-Exam to make sure the jetty-osgi-boot can be started along with the
  * httpservice web-bundle. Then make sure we can deploy an OSGi service on the
  * top of this.
  */
-@RunWith(JUnit4TestRunner.class)
+@RunWith(PaxExam.class)
 public class TestJettyOSGiBootWithJsp
 {
-    private static final boolean LOGGING_ENABLED = false;
-
-    private static final boolean REMOTE_DEBUGGING = false;
+    private static final String LOG_LEVEL = "WARN";
 
     @Inject
     BundleContext bundleContext = null;
@@ -66,9 +62,6 @@
     {
 
         ArrayList<Option> options = new ArrayList<Option>();
-
-        TestOSGiUtil.addMoreOSGiContainers(options);
-
         options.add(CoreOptions.junitBundles());
         options.addAll(configureJettyHomeAndPort("jetty-selector.xml"));
         options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*", "javax.activation.*"));
@@ -77,39 +70,11 @@
                                                "com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects"));
      
         options.addAll(TestJettyOSGiBootCore.coreJettyDependencies());
+        options.addAll(Arrays.asList(options(systemProperty("pax.exam.logging").value("none"))));
+        options.addAll(Arrays.asList(options(systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(LOG_LEVEL))));
+        options.addAll(Arrays.asList(options(systemProperty("org.eclipse.jetty.LEVEL").value(LOG_LEVEL))));
 
-        String logLevel = "WARN";
-        
-        // Enable Logging
-        if (LOGGING_ENABLED)
-            logLevel = "INFO";
- 
-            options.addAll(Arrays.asList(options(
-                                                 // install log service using pax runners profile abstraction (there
-                                                 // are more profiles, like DS)
-                                                 // logProfile(),
-                                                 // this is how you set the default log level when using pax logging
-                                                 // (logProfile)
-                                                 systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value(logLevel),
-                                                 systemProperty("org.eclipse.jetty.annotations.LEVEL").value(logLevel))));
-     
         options.addAll(jspDependencies());
-
-        // Remote JDWP Debugging, this won't work with the forked container.
-        // if(REMOTE_DEBUGGING) {
-        // options.addAll(Arrays.asList(options(
-        // // this just adds all what you write here to java vm argumenents of
-        // the (new) osgi process.
-        // PaxRunnerOptions.vmOption(
-        // "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5006" )
-        // )));
-        // }
-
-        // bug at the moment: this would make the httpservice catch all
-        // requests and prevent the webapp at the root context to catch any of
-        // them.
-        // options.addAll(TestJettyOSGiBootCore.httpServiceJetty());
-
         return options.toArray(new Option[options.size()]);
     }
 
@@ -157,7 +122,7 @@
         return res;
     }
 
-
+    @Ignore
     @Test
     public void assertAllBundlesActiveOrResolved()
     {
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java
index d9d63ed..a3f486d 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java
@@ -33,7 +33,6 @@
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.junit.Assert;
-import org.ops4j.pax.exam.CoreOptions;
 import org.ops4j.pax.exam.Option;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -46,22 +45,6 @@
 public class TestOSGiUtil
 {
 
-    /**
-     * Note: this will run many more tests. TODO: find a better way to control
-     * this and use non-deprecated methods.
-     * 
-     * @param opti
-     */
-    protected static void addMoreOSGiContainers(List<Option> options)
-    {
-        //Uncomment to run more containers - these have been commented out
-        //to improve speed of builds.
-        //options.add(CoreOptions.equinox().version("3.6.1"));
-        //options.add(CoreOptions.equinox().version("3.7.0"));
-       // options.add(CoreOptions.felix().version("3.2.2"));
-        options.add(CoreOptions.felix().version("4.0.2")); 
-    }
-
     protected static Bundle getBundle(BundleContext bundleContext, String symbolicName)
     {
             Map<String,Bundle> _bundles = new HashMap<String, Bundle>();
@@ -146,7 +129,7 @@
         for (Bundle b : bundleContext.getBundles())
         {
             bundlesIndexedBySymbolicName.put(b.getSymbolicName(), b);
-            System.err.println("    " + b.getSymbolicName() + " " + b.getState());
+            System.err.println("    " + b.getSymbolicName() + " " + b.getLocation() + " " + b.getVersion()+ " " + b.getState());
         }
     }
    
diff --git a/jetty-plus/pom.xml b/jetty-plus/pom.xml
index 377ea4f..d5485a1 100644
--- a/jetty-plus/pom.xml
+++ b/jetty-plus/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-plus</artifactId>
diff --git a/jetty-proxy/pom.xml b/jetty-proxy/pom.xml
index 8ae47be..b17d0a1 100644
--- a/jetty-proxy/pom.xml
+++ b/jetty-proxy/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-proxy</artifactId>
diff --git a/jetty-quickstart/README.txt b/jetty-quickstart/README.txt
new file mode 100644
index 0000000..43ad7f3
--- /dev/null
+++ b/jetty-quickstart/README.txt
@@ -0,0 +1,6 @@
+mvn exec:java -Dexec.classpathScope=test -Dexec.mainClass="org.eclipse.jetty.quickstart.StartBenchmarkWar"
+
+OR
+
+mvn exec:java -Dexec.classpathScope=test -Dexec.mainClass="org.eclipse.jetty.quickstart.PreconfigureBenchmarkWar"
+mvn exec:java -Dexec.classpathScope=test -Dexec.mainClass="org.eclipse.jetty.quickstart.QuickStartBenchmarkWar"
diff --git a/jetty-quickstart/pom.xml b/jetty-quickstart/pom.xml
new file mode 100644
index 0000000..3a34ba7
--- /dev/null
+++ b/jetty-quickstart/pom.xml
@@ -0,0 +1,170 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.eclipse.jetty</groupId>
+    <artifactId>jetty-project</artifactId>
+    <version>9.2.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.eclipse.jetty</groupId>
+  <artifactId>jetty-quickstart</artifactId>
+  <name>Example :: Jetty Quick Start</name>
+  <description>Jetty Quick Start</description>
+  <url>http://www.eclipse.org/jetty</url>
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-webapp</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-jmx</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-plus</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-annotations</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>javax.transaction</groupId>
+      <artifactId>javax.transaction-api</artifactId>
+      <version>1.2</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.tests</groupId>
+      <artifactId>test-mock-resources</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.orbit</groupId>
+      <artifactId>javax.mail.glassfish</artifactId>
+      <version>1.4.1.v201005082020</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlets</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>javax-websocket-server-impl</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>websocket-server</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>apache-jsp</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>apache-jstl</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+
+    <plugin>
+      <groupId>org.codehaus.mojo</groupId>
+      <artifactId>appassembler-maven-plugin</artifactId>
+      <version>1.7</version>
+      <configuration>
+        <platforms>
+          <platform>unix</platform>
+        </platforms>
+        <programs>
+          <program>
+            <id>preconfigure</id>
+            <mainClass>org.eclipse.jetty.quickstart.PreconfigureQuickStartWar</mainClass>
+          </program>
+          <program>
+            <mainClass>org.eclipse.jetty.quickstart.QuickStartWar</mainClass>
+            <id>quickstart</id>
+          </program>
+        </programs>
+      </configuration>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>copy</id>
+            <phase>generate-resources</phase>
+            <goals>
+              <goal>copy</goal>
+            </goals>
+            <configuration>
+              <artifactItems>
+                <artifactItem>
+                  <groupId>org.eclipse.jetty.tests</groupId>
+                  <artifactId>test-jndi-webapp</artifactId>
+                  <version>${project.version}</version>
+                  <type>war</type>
+                  <overWrite>true</overWrite>
+                  <includes>**</includes>
+                  <outputDirectory>${basedir}/target</outputDirectory>
+                  <destFileName>test-jndi.war</destFileName>
+                </artifactItem>
+                <artifactItem>
+                  <groupId>org.eclipse.jetty.tests</groupId>
+                  <artifactId>test-spec-webapp</artifactId>
+                  <version>${project.version}</version>
+                  <type>war</type>
+                  <overWrite>true</overWrite>
+                  <includes>**</includes>
+                  <outputDirectory>${basedir}/target</outputDirectory>
+                  <destFileName>test-spec.war</destFileName>
+                </artifactItem>
+                <artifactItem>
+                  <groupId>org.eclipse.jetty</groupId>
+                  <artifactId>test-jetty-webapp</artifactId>
+                  <version>${project.version}</version>
+                  <type>war</type>
+                  <overWrite>true</overWrite>
+                  <includes>**</includes>
+                  <outputDirectory>${basedir}/target</outputDirectory>
+                  <destFileName>test-standard.war</destFileName>
+                </artifactItem>
+              </artifactItems>
+            </configuration>
+          </execution>
+	</executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+            <configuration>
+              <descriptorRefs>
+                <descriptorRef>config</descriptorRef>
+              </descriptorRefs>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/jetty-quickstart/src/main/config/etc/example-quickstart.xml b/jetty-quickstart/src/main/config/etc/example-quickstart.xml
new file mode 100644
index 0000000..9a012c7
--- /dev/null
+++ b/jetty-quickstart/src/main/config/etc/example-quickstart.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"  encoding="ISO-8859-1"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<!-- An example context XML for a quickstart webapp
+A quick started webapp has all the jar scanning and fragment resolution done in a 
+Preconfigure phase, with all the discovered configuration encoded into a
+WEB-INF/quickstart-web.xml file.
+
+This allows very rapid and precise starting of a webapp, without the risk of accidental
+deployment of other discovered resources.  This is above and beyond what is available 
+with web.xml meta-data-complete, as it also prevents scanning for ServletContainer 
+initialisers and any annotations/classes they require.
+
+If autoPreconfigure is set to true, then the webapp will be preconfigured the first
+time it is run.
+ -->
+<Configure class="org.eclipse.jetty.quickstart.QuickStartWebApp">
+  <Set name="autoPreconfigure">true</Set>
+  <Set name="contextPath">/</Set>
+  <Set name="war"><Property name="jetty.webapps" default="."/>/application.war</Set>
+</Configure>
diff --git a/jetty-quickstart/src/main/config/modules/quickstart.mod b/jetty-quickstart/src/main/config/modules/quickstart.mod
new file mode 100644
index 0000000..89db9fd
--- /dev/null
+++ b/jetty-quickstart/src/main/config/modules/quickstart.mod
@@ -0,0 +1,12 @@
+#
+# Jetty Quickstart module
+#
+
+[depend]
+server
+plus
+annotations
+
+
+[lib]
+lib/jetty-quickstart-${jetty.version}.jar
diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureDescriptorProcessor.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureDescriptorProcessor.java
new file mode 100644
index 0000000..627277e
--- /dev/null
+++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureDescriptorProcessor.java
@@ -0,0 +1,88 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.Descriptor;
+import org.eclipse.jetty.webapp.IterativeDescriptorProcessor;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.xml.XmlParser;
+
+/**
+ * Preconfigure DescriptorProcessor
+ *
+ * Saves literal XML snippets
+ *
+ */
+
+public class PreconfigureDescriptorProcessor extends IterativeDescriptorProcessor
+{
+    private static final Logger LOG = Log.getLogger(PreconfigureDescriptorProcessor.class);
+    
+    private final StringBuilder _buffer = new StringBuilder();
+    private final boolean _showOrigin;
+    private String _origin;
+
+    public PreconfigureDescriptorProcessor ()
+    {
+        _showOrigin=LOG.isDebugEnabled();
+        try
+        {
+            registerVisitor("env-entry", getClass().getDeclaredMethod("saveSnippet", __signature));
+            registerVisitor("resource-ref", getClass().getDeclaredMethod("saveSnippet", __signature));
+            registerVisitor("resource-env-ref", getClass().getDeclaredMethod("saveSnippet", __signature));
+            registerVisitor("message-destination-ref", getClass().getDeclaredMethod("saveSnippet", __signature));
+            registerVisitor("data-source", getClass().getDeclaredMethod("saveSnippet", __signature));
+        }
+        catch (Exception e)
+        {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    @Override
+    public void start(WebAppContext context, Descriptor descriptor)
+    {
+        LOG.debug("process {}",descriptor);
+        _origin=("  <!-- "+descriptor+" -->\n");
+    }
+
+
+    @Override
+    public void end(WebAppContext context,Descriptor descriptor)
+    {
+    }
+
+
+    public void saveSnippet (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
+    throws Exception
+    {
+        LOG.debug("save {}",node.getTag());
+        if (_showOrigin)
+            _buffer.append(_origin);
+        _buffer.append("  ").append(node.toString()).append("\n");
+    }
+    
+    public String getXML()
+    {
+        return _buffer.toString();
+    }
+
+}
diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java
new file mode 100644
index 0000000..204ad2e
--- /dev/null
+++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java
@@ -0,0 +1,129 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.JarResource;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.xml.XmlConfiguration;
+
+public class PreconfigureQuickStartWar
+{
+    private static final Logger LOG = Log.getLogger(PreconfigureQuickStartWar.class);
+    static final boolean ORIGIN=LOG.isDebugEnabled();
+    
+
+    public static void main(String... args) throws Exception
+    {
+        Resource war = null;
+        Resource dir = null;
+        Resource xml = null;
+
+        switch (args.length)
+        {
+            case 0:
+                error("No WAR file or directory given");
+                break;
+
+            case 1:
+                dir = Resource.newResource(args[0]);
+
+            case 2:
+                war = Resource.newResource(args[0]);
+                if (war.isDirectory())
+                {
+                    dir = war;
+                    war = null;
+                    xml = Resource.newResource(args[1]);
+                }
+                else
+                {
+                    dir = Resource.newResource(args[1]);
+                }
+
+                break;
+
+            case 3:
+                war = Resource.newResource(args[0]);
+                dir = Resource.newResource(args[1]);
+                xml = Resource.newResource(args[2]);
+                break;
+
+            default:
+                error("Too many args");
+                break;
+        }
+
+        
+        preconfigure(war,dir,xml);
+    }
+
+    /**
+     * @param war The war (or directory) to preconfigure
+     * @param dir The directory to expand the war into (or null if war is a directory)
+     * @param xml A context XML to apply (or null if none)
+     * @throws Exception
+     */
+    public static void preconfigure(Resource war, Resource dir, Resource xml) throws Exception 
+    {
+        // Do we need to unpack a war?
+        if (war != null)
+        {
+            if (war.isDirectory())
+                error("war file is directory");
+
+            if (!dir.exists())
+                dir.getFile().mkdirs();
+            JarResource.newJarResource(war).copyTo(dir.getFile());
+        }
+        
+        final Server server = new Server();
+
+        QuickStartWebApp webapp = new QuickStartWebApp();
+
+        if (xml != null)
+        {
+            if (xml.isDirectory() || !xml.toString().toLowerCase().endsWith(".xml"))
+                error("Bad context.xml: "+xml);
+            XmlConfiguration xmlConfiguration = new XmlConfiguration(xml.getURL());
+            xmlConfiguration.configure(webapp);
+        }
+        webapp.setResourceBase(dir.getFile().getAbsolutePath());
+        webapp.setPreconfigure(true);
+        server.setHandler(webapp);
+        server.start();
+        server.stop();
+    }
+    
+    
+    
+
+    private static void error(String message)
+    {
+        System.err.println("ERROR: " + message);
+        System.err.println("Usage: java -jar PreconfigureQuickStartWar.jar <war-directory>");
+        System.err.println("       java -jar PreconfigureQuickStartWar.jar <war-directory> <context-xml-file>");
+        System.err.println("       java -jar PreconfigureQuickStartWar.jar <war-file> <target-war-directory>");
+        System.err.println("       java -jar PreconfigureQuickStartWar.jar <war-file> <target-war-directory> <context-xml-file>");
+        System.exit(1);
+    }
+
+}
diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java
new file mode 100644
index 0000000..313c5c0
--- /dev/null
+++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java
@@ -0,0 +1,139 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import org.eclipse.jetty.annotations.AnnotationConfiguration;
+import org.eclipse.jetty.annotations.AnnotationDecorator;
+import org.eclipse.jetty.annotations.ServletContainerInitializersStarter;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.StandardDescriptorProcessor;
+import org.eclipse.jetty.webapp.WebAppClassLoader;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.webapp.WebInfConfiguration;
+
+/**
+ * QuickStartConfiguration
+ * 
+ * Re-inflate a deployable webapp from a saved effective-web.xml
+ * which combines all pre-parsed web xml descriptors and annotations.
+ * 
+ */
+public class QuickStartConfiguration extends WebInfConfiguration
+{
+    private static final Logger LOG = Log.getLogger(QuickStartConfiguration.class);
+
+    /**
+     * @see org.eclipse.jetty.webapp.AbstractConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext)
+     */
+    @Override
+    public void preConfigure(WebAppContext context) throws Exception
+    {
+        //check that webapp is suitable for quick start - it is not a packed war
+        String war = context.getWar();
+        if (war == null || war.length()<=0)
+            throw new IllegalStateException ("No location for webapp");  
+        
+        //Make a temp directory for the webapp if one is not already set
+        resolveTempDirectory(context);
+
+        Resource webApp = context.newResource(war);
+
+        // Accept aliases for WAR files
+        if (webApp.getAlias() != null)
+        {
+            LOG.debug(webApp + " anti-aliased to " + webApp.getAlias());
+            webApp = context.newResource(webApp.getAlias());
+        }
+
+        // Is the WAR usable directly?
+        if (!webApp.exists() || !webApp.isDirectory() || webApp.toString().startsWith("jar:"))
+            throw new IllegalStateException("Webapp does not exist or is not unpacked");
+        
+        context.setBaseResource(webApp);
+
+        LOG.debug("webapp={}",webApp);
+
+        
+        //look for effective-web.xml in WEB-INF of webapp
+        Resource webInf = context.getWebInf();
+        if (webInf == null || !webInf.exists())
+            throw new IllegalStateException("No WEB-INF");
+        LOG.debug("webinf={}",webInf);
+  
+        Resource quickStartWebXml = webInf.addPath("quickstart-web.xml");
+        if (!quickStartWebXml.exists())
+            throw new IllegalStateException ("No WEB-INF/quickstart-web.xml");
+        LOG.debug("quickStartWebXml={}",quickStartWebXml);
+        
+        context.getMetaData().setWebXml(quickStartWebXml);
+    }
+
+    
+    /**
+     * @see org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
+     */
+    @Override
+    public void configure(WebAppContext context) throws Exception
+    {
+        LOG.debug("configure {}",this);
+        if (context.isStarted())
+        {
+            LOG.warn("Cannot configure webapp after it is started");
+            return;
+        }
+        
+        //Temporary:  set up the classpath here. This should be handled by the QuickStartDescriptorProcessor
+        Resource webInf = context.getWebInf();
+
+        if (webInf != null && webInf.isDirectory() && context.getClassLoader() instanceof WebAppClassLoader)
+        {
+            // Look for classes directory
+            Resource classes= webInf.addPath("classes/");
+            if (classes.exists())
+                ((WebAppClassLoader)context.getClassLoader()).addClassPath(classes);
+
+            // Look for jars
+            Resource lib= webInf.addPath("lib/");
+            if (lib.exists() || lib.isDirectory())
+                ((WebAppClassLoader)context.getClassLoader()).addJars(lib);
+        }
+
+        //add the processor to handle normal web.xml content
+        context.getMetaData().addDescriptorProcessor(new StandardDescriptorProcessor());
+        
+        //add a processor to handle extended web.xml format
+        context.getMetaData().addDescriptorProcessor(new QuickStartDescriptorProcessor());
+        
+        //add a decorator that will find introspectable annotations
+        context.addDecorator(new AnnotationDecorator(context)); //this must be the last Decorator because they are run in reverse order!
+        
+        //add a context bean that will run ServletContainerInitializers as the context starts
+        ServletContainerInitializersStarter starter = (ServletContainerInitializersStarter)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZER_STARTER);
+        if (starter != null)
+            throw new IllegalStateException("ServletContainerInitializersStarter already exists");
+        starter = new ServletContainerInitializersStarter(context);
+        context.setAttribute(AnnotationConfiguration.CONTAINER_INITIALIZER_STARTER, starter);
+        context.addBean(starter, true);       
+
+        LOG.debug("configured {}",this);
+    }
+
+}
diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorProcessor.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorProcessor.java
new file mode 100644
index 0000000..7e5a6c8
--- /dev/null
+++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorProcessor.java
@@ -0,0 +1,218 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+
+import org.eclipse.jetty.annotations.AnnotationConfiguration;
+import org.eclipse.jetty.annotations.ServletContainerInitializersStarter;
+import org.eclipse.jetty.plus.annotation.ContainerInitializer;
+import org.eclipse.jetty.util.QuotedStringTokenizer;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.resource.ResourceCollection;
+import org.eclipse.jetty.webapp.Descriptor;
+import org.eclipse.jetty.webapp.IterativeDescriptorProcessor;
+import org.eclipse.jetty.webapp.MetaInfConfiguration;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.xml.XmlParser;
+
+/**
+ * QuickStartDescriptorProcessor
+ * 
+ * Handle  extended elements for quickstart-web.xml
+ */
+public class QuickStartDescriptorProcessor extends IterativeDescriptorProcessor
+{
+    /**
+     * 
+     */
+    public QuickStartDescriptorProcessor()
+    {
+        try
+        {
+            registerVisitor("context-param", this.getClass().getDeclaredMethod("visitContextParam", __signature));
+        }    
+        catch (Exception e)
+        {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    /**
+     * @see org.eclipse.jetty.webapp.IterativeDescriptorProcessor#start(org.eclipse.jetty.webapp.WebAppContext, org.eclipse.jetty.webapp.Descriptor)
+     */
+    @Override
+    public void start(WebAppContext context, Descriptor descriptor)
+    {
+    }
+
+    /**
+     * @see org.eclipse.jetty.webapp.IterativeDescriptorProcessor#end(org.eclipse.jetty.webapp.WebAppContext, org.eclipse.jetty.webapp.Descriptor)
+     */
+    @Override
+    public void end(WebAppContext context, Descriptor descriptor)
+    { 
+    }
+    
+
+    /**
+     * @param context
+     * @param descriptor
+     * @param node
+     */
+    public void visitContextParam (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
+            throws Exception
+    {
+        String name = node.getString("param-name", false, true);
+        String value = node.getString("param-value", false, true);
+        List<String> values = new ArrayList<>();
+        
+        // extract values
+        switch(name)
+        {
+            case ServletContext.ORDERED_LIBS:
+            case AnnotationConfiguration.CONTAINER_INITIALIZERS:
+            case MetaInfConfiguration.METAINF_TLDS:
+            case MetaInfConfiguration.METAINF_RESOURCES:
+
+                context.removeAttribute(name);
+                
+                QuotedStringTokenizer tok = new QuotedStringTokenizer(value,",");
+                while(tok.hasMoreElements())
+                    values.add(tok.nextToken().trim());
+                
+                break;
+                
+            default:
+                values.add(value);
+        }
+
+        // handle values
+        switch(name)
+        {
+            case ServletContext.ORDERED_LIBS:
+            {
+                List<Object> libs = new ArrayList<>();
+                Object o=context.getAttribute(ServletContext.ORDERED_LIBS);
+                if (o instanceof Collection<?>)
+                    libs.addAll((Collection<?>)o);
+                libs.addAll(values);
+                if (libs.size()>0)
+                    context.setAttribute(ServletContext.ORDERED_LIBS,libs);
+                
+                break;
+            }
+                
+            case AnnotationConfiguration.CONTAINER_INITIALIZERS:
+            {
+                for (String i : values)
+                    visitContainerInitializer(context, new ContainerInitializer(Thread.currentThread().getContextClassLoader(), i));
+                break;
+            }
+            
+            case MetaInfConfiguration.METAINF_TLDS:
+            {
+                List<Object> tlds = new ArrayList<>();
+                String war=context.getBaseResource().getURI().toString();
+                Object o=context.getAttribute(MetaInfConfiguration.METAINF_TLDS);
+                if (o instanceof Collection<?>)
+                    tlds.addAll((Collection<?>)o);
+                for (String i : values)
+                {
+                    Resource r = Resource.newResource(i.replace("${WAR}/",war));
+                    if (r.exists())
+                        tlds.add(r.getURL());
+                    else
+                        throw new IllegalArgumentException("TLD not found: "+r);                    
+                }
+                
+                if (tlds.size()>0)
+                    context.setAttribute(MetaInfConfiguration.METAINF_TLDS,tlds);
+                break;
+            }
+            
+            case MetaInfConfiguration.METAINF_RESOURCES:
+            {
+                String war=context.getBaseResource().getURI().toString();
+                for (String i : values)
+                {
+                    Resource r = Resource.newResource(i.replace("${WAR}/",war));
+                    if (r.exists())
+                        visitMetaInfResource(context,r); 
+                    else
+                        throw new IllegalArgumentException("Resource not found: "+r);                    
+                }
+                break;
+            }
+                
+            default:
+                
+        }
+    }
+    
+
+    public void visitContainerInitializer (WebAppContext context, ContainerInitializer containerInitializer)
+    {
+        if (containerInitializer == null)
+            return;
+        
+        //add the ContainerInitializer to the list of container initializers
+        List<ContainerInitializer> containerInitializers = (List<ContainerInitializer>)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS);
+        if (containerInitializers == null)
+        {
+            containerInitializers = new ArrayList<ContainerInitializer>();
+            context.setAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS, containerInitializers);
+        }
+        
+        containerInitializers.add(containerInitializer);
+
+        //Ensure a bean is set up on the context that will invoke the ContainerInitializers as the context starts
+        ServletContainerInitializersStarter starter = (ServletContainerInitializersStarter)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZER_STARTER);
+        if (starter == null)
+        {
+            starter = new ServletContainerInitializersStarter(context);
+            context.setAttribute(AnnotationConfiguration.CONTAINER_INITIALIZER_STARTER, starter);
+            context.addBean(starter, true);
+        }
+    }
+    
+    
+    public void visitMetaInfResource (WebAppContext context, Resource dir)
+    {
+        Collection<Resource> metaInfResources =  (Collection<Resource>)context.getAttribute(MetaInfConfiguration.METAINF_RESOURCES);
+        if (metaInfResources == null)
+        {
+            metaInfResources = new HashSet<Resource>();
+            context.setAttribute(MetaInfConfiguration.METAINF_RESOURCES, metaInfResources);
+        }
+        metaInfResources.add(dir);
+        //also add to base resource of webapp
+        Resource[] collection=new Resource[metaInfResources.size()+1];
+        int i=0;
+        collection[i++]=context.getBaseResource();
+        for (Resource resource : metaInfResources)
+            collection[i++]=resource;
+        context.setBaseResource(new ResourceCollection(collection));
+    }
+}
diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java
new file mode 100644
index 0000000..bcbcab8
--- /dev/null
+++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java
@@ -0,0 +1,720 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.ServletContext;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.descriptor.JspPropertyGroupDescriptor;
+import javax.servlet.descriptor.TaglibDescriptor;
+
+import org.eclipse.jetty.annotations.AnnotationConfiguration;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.plus.annotation.LifeCycleCallback;
+import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
+import org.eclipse.jetty.security.ConstraintAware;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.SecurityHandler;
+import org.eclipse.jetty.security.authentication.FormAuthenticator;
+import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.FilterMapping;
+import org.eclipse.jetty.servlet.Holder;
+import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlet.ServletMapping;
+import org.eclipse.jetty.util.QuotedStringTokenizer;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.JarResource;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.webapp.MetaData;
+import org.eclipse.jetty.webapp.MetaData.OriginInfo;
+import org.eclipse.jetty.webapp.MetaInfConfiguration;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.xml.XmlAppendable;
+
+/**
+ * QuickStartWar
+ *
+ */
+public class QuickStartWebApp extends WebAppContext
+{
+    private static final Logger LOG = Log.getLogger(QuickStartWebApp.class);
+    
+    public static final String[] __configurationClasses = new String[] 
+            {
+                org.eclipse.jetty.quickstart.QuickStartConfiguration.class.getCanonicalName(),
+                org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(),
+                org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(),
+                org.eclipse.jetty.webapp.JettyWebXmlConfiguration.class.getCanonicalName()
+            };
+    
+    
+    private boolean _preconfigure=false;
+    private boolean _autoPreconfigure=false;
+    private boolean _startWebapp=false;
+    private PreconfigureDescriptorProcessor _preconfigProcessor;
+    
+
+    public static final String[] __preconfigurationClasses = new String[]
+    { 
+        org.eclipse.jetty.webapp.WebInfConfiguration.class.getCanonicalName(), 
+        org.eclipse.jetty.webapp.WebXmlConfiguration.class.getCanonicalName(),
+        org.eclipse.jetty.webapp.MetaInfConfiguration.class.getCanonicalName(), 
+        org.eclipse.jetty.webapp.FragmentConfiguration.class.getCanonicalName(),
+        org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(), 
+        org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(),
+        org.eclipse.jetty.annotations.AnnotationConfiguration.class.getCanonicalName(),
+    };
+    
+    public QuickStartWebApp()
+    {
+        super();
+        setConfigurationClasses(__preconfigurationClasses);
+        setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",".*\\.jar");
+    }
+
+    public boolean isPreconfigure()
+    {
+        return _preconfigure;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Preconfigure webapp
+     * @param preconfigure  If true, then starting the webapp will generate 
+     * the WEB-INF/quickstart-web.xml rather than start the webapp.
+     */
+    public void setPreconfigure(boolean preconfigure)
+    {
+        _preconfigure = preconfigure;
+    }
+
+    public boolean isAutoPreconfigure()
+    {
+        return _autoPreconfigure;
+    }
+    
+    public void setAutoPreconfigure(boolean autoPrecompile)
+    {
+        _autoPreconfigure = autoPrecompile;
+    }
+    
+    @Override
+    protected void startWebapp() throws Exception
+    {
+        if (isPreconfigure())
+            generateQuickstartWebXml(_preconfigProcessor.getXML());
+        
+        if (_startWebapp)
+            super.startWebapp();
+    }
+    
+    @Override
+    protected void doStart() throws Exception
+    {
+        // unpack and Adjust paths.
+        Resource war = null;
+        Resource dir = null;
+
+        Resource base = getBaseResource();
+        if (base==null)
+            base=Resource.newResource(getWar());
+
+        if (base.isDirectory())
+            dir=base;
+        else if (base.toString().toLowerCase().endsWith(".war"))
+        {
+            war=base;
+            String w=war.toString();
+            dir=Resource.newResource(w.substring(0,w.length()-4));
+
+            if (!dir.exists())
+            {                       
+                LOG.info("Quickstart Extract " + war + " to " + dir);
+                dir.getFile().mkdirs();
+                JarResource.newJarResource(war).copyTo(dir.getFile());
+            }
+
+            setWar(null);
+            setBaseResource(dir);
+        }
+        else 
+            throw new IllegalArgumentException();
+
+
+        Resource qswebxml=dir.addPath("/WEB-INF/quickstart-web.xml");
+        
+        if (isPreconfigure())
+        {
+            _preconfigProcessor = new PreconfigureDescriptorProcessor();
+            getMetaData().addDescriptorProcessor(_preconfigProcessor);
+            _startWebapp=false;
+        }
+        else if (qswebxml.exists())
+        {
+            setConfigurationClasses(__configurationClasses);
+            _startWebapp=true;
+        }
+        else if (_autoPreconfigure)
+        {   
+            LOG.info("Quickstart preconfigure: {}(war={},dir={})",this,war,dir);
+
+            _preconfigProcessor = new PreconfigureDescriptorProcessor();
+            
+            getMetaData().addDescriptorProcessor(_preconfigProcessor);
+            setPreconfigure(true);
+            _startWebapp=true;
+        }
+        else
+            _startWebapp=true;
+            
+        super.doStart();
+    }
+
+
+    public void generateQuickstartWebXml(String extraXML) throws IOException
+    {
+        getMetaData().getOrigins();
+        // dumpStdErr();
+
+        if (getBaseResource()==null)
+            throw new IllegalArgumentException("No base resource for "+this);
+        
+        File webxml = new File(getWebInf().getFile(),"quickstart-web.xml");
+
+        LOG.info("Quickstart generate {}",webxml);
+
+        XmlAppendable out = new XmlAppendable(new FileOutputStream(webxml),"UTF-8");
+        MetaData md = getMetaData();
+
+        Map<String, String> webappAttr = new HashMap<>();
+        webappAttr.put("xmlns","http://xmlns.jcp.org/xml/ns/javaee");
+        webappAttr.put("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");
+        webappAttr.put("xsi:schemaLocation","http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd");
+        webappAttr.put("metadata-complete","true");
+        webappAttr.put("version","3.1");
+
+        out.open("web-app",webappAttr);
+
+        if (getDisplayName() != null)
+            out.tag("display-name",getDisplayName());
+
+        // Set some special context parameters
+
+        // The location of the war file on disk
+        String resourceBase=getBaseResource().getFile().getCanonicalFile().getAbsoluteFile().toURI().toString();
+        
+        // The library order
+        addContextParamFromAttribute(out,ServletContext.ORDERED_LIBS);
+        //the servlet container initializers
+        addContextParamFromAttribute(out,AnnotationConfiguration.CONTAINER_INITIALIZERS);
+        //the tlds discovered
+        addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_TLDS,resourceBase);
+        //the META-INF/resources discovered
+        addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_RESOURCES,resourceBase);
+
+        
+        // init params
+        for (String p : getInitParams().keySet())
+            out.open("context-param",origin(md,"context-param." + p))
+            .tag("param-name",p)
+            .tag("param-value",getInitParameter(p))
+            .close();
+
+        if (getEventListeners() != null)
+            for (EventListener e : getEventListeners())
+                out.open("listener",origin(md,e.getClass().getCanonicalName() + ".listener"))
+                .tag("listener-class",e.getClass().getCanonicalName())
+                .close();
+
+        ServletHandler servlets = getServletHandler();
+
+        if (servlets.getFilters() != null)
+        {
+            for (FilterHolder holder : servlets.getFilters())
+                outholder(out,md,"filter",holder);
+        }
+
+        if (servlets.getFilterMappings() != null)
+        {
+            for (FilterMapping mapping : servlets.getFilterMappings())
+            {
+                out.open("filter-mapping");
+                out.tag("filter-name",mapping.getFilterName());
+                if (mapping.getPathSpecs() != null)
+                    for (String s : mapping.getPathSpecs())
+                        out.tag("url-pattern",s);
+                if (mapping.getServletNames() != null)
+                    for (String n : mapping.getServletNames())
+                        out.tag("servlet-name",n);
+
+                if (!mapping.isDefaultDispatches())
+                {
+                    if (mapping.appliesTo(DispatcherType.REQUEST))
+                        out.tag("dispatcher","REQUEST");
+                    if (mapping.appliesTo(DispatcherType.ASYNC))
+                        out.tag("dispatcher","ASYNC");
+                    if (mapping.appliesTo(DispatcherType.ERROR))
+                        out.tag("dispatcher","ERROR");
+                    if (mapping.appliesTo(DispatcherType.FORWARD))
+                        out.tag("dispatcher","FORWARD");
+                    if (mapping.appliesTo(DispatcherType.INCLUDE))
+                        out.tag("dispatcher","INCLUDE");
+                }
+                out.close();
+            }
+        }
+
+        if (servlets.getServlets() != null)
+        {
+            for (ServletHolder holder : servlets.getServlets())
+                outholder(out,md,"servlet",holder);
+        }
+
+        if (servlets.getServletMappings() != null)
+        {
+            for (ServletMapping mapping : servlets.getServletMappings())
+            {
+                out.open("servlet-mapping",origin(md,mapping.getServletName() + ".servlet.mappings"));
+                out.tag("servlet-name",mapping.getServletName());
+                if (mapping.getPathSpecs() != null)
+                    for (String s : mapping.getPathSpecs())
+                        out.tag("url-pattern",s);
+                out.close();
+            }
+        }
+
+        // Security elements
+        SecurityHandler security = getSecurityHandler();
+        
+        if (security!=null && (security.getRealmName()!=null || security.getAuthMethod()!=null))
+        {
+            out.open("login-config");
+            if (security.getAuthMethod()!=null)
+                out.tag("auth-method",origin(md,"auth-method"),security.getAuthMethod());
+            if (security.getRealmName()!=null)
+                out.tag("realm-name",origin(md,"realm-name"),security.getRealmName());
+            
+            
+            if (Constraint.__FORM_AUTH.equalsIgnoreCase(security.getAuthMethod()))
+            {
+                out.open("form-login-config");
+                out.tag("form-login-page",origin(md,"form-login-page"),security.getInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE));
+                out.tag("form-error-page",origin(md,"form-error-page"),security.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE));
+                out.close();
+            }
+            
+            out.close();
+        }
+        
+        if (security instanceof ConstraintAware)
+        {
+            ConstraintAware ca = (ConstraintAware)security;
+            for (String r:ca.getRoles())
+                out.open("security-role")
+                .tag("role-name",r)
+                .close();
+            
+            for (ConstraintMapping m : ca.getConstraintMappings())
+            {
+                out.open("security-constraint");
+                
+                if (m.getConstraint().getAuthenticate())
+                {
+                    out.open("auth-constraint");
+                    if (m.getConstraint().getRoles()!=null)
+                        for (String r : m.getConstraint().getRoles())
+                            out.tag("role-name",r);
+
+                    out.close();
+                }
+                
+                switch (m.getConstraint().getDataConstraint())
+                {
+                    case Constraint.DC_NONE:
+                        out.open("user-data-constraint").tag("transport-guarantee","NONE").close();
+                        break;
+                        
+                    case Constraint.DC_INTEGRAL:
+                        out.open("user-data-constraint").tag("transport-guarantee","INTEGRAL").close();
+                        break;
+                        
+                    case Constraint.DC_CONFIDENTIAL:
+                        out.open("user-data-constraint").tag("transport-guarantee","CONFIDENTIAL").close();
+                        break;
+                        
+                    default:
+                            break;
+                        
+                }
+
+                out.open("web-resource-collection");
+                {
+                    if (m.getConstraint().getName()!=null)
+                        out.tag("web-resource-name",m.getConstraint().getName());
+                    if (m.getPathSpec()!=null)
+                        out.tag("url-pattern",origin(md,"constraint.url."+m.getPathSpec()),m.getPathSpec());
+                    if (m.getMethod()!=null)
+                        out.tag("http-method",m.getMethod());
+
+                    if (m.getMethodOmissions()!=null)
+                        for (String o:m.getMethodOmissions())
+                            out.tag("http-method-omission",o);
+
+                    out.close();
+                }
+                
+                out.close();
+                
+            }
+        }
+        
+        if (getWelcomeFiles() != null)
+        {
+            out.open("welcome-file-list");
+            for (String welcomeFile:getWelcomeFiles())
+            {
+                out.tag("welcome-file", welcomeFile);
+            }
+            out.close();
+        }
+    
+        Map<String,String> localeEncodings = getLocaleEncodings();
+        if (localeEncodings != null && !localeEncodings.isEmpty())
+        {
+            out.open("locale-encoding-mapping-list");
+            for (Map.Entry<String, String> entry:localeEncodings.entrySet())
+            {
+                out.open("locale-encoding-mapping", origin(md,"locale-encoding."+entry.getKey()));
+                out.tag("locale", entry.getKey());
+                out.tag("encoding", entry.getValue());
+                out.close();
+            }
+            out.close();
+        }
+
+        //session-config
+        if (getSessionHandler().getSessionManager() != null)
+        {
+            out.open("session-config");
+            int maxInactiveSec = getSessionHandler().getSessionManager().getMaxInactiveInterval();
+            out.tag("session-timeout", (maxInactiveSec==0?"0":Integer.toString(maxInactiveSec/60)));
+ 
+            Set<SessionTrackingMode> modes = getSessionHandler().getSessionManager().getEffectiveSessionTrackingModes();
+            if (modes != null)
+            {
+                for (SessionTrackingMode mode:modes)
+                    out.tag("tracking-mode", mode.toString());
+            }
+            
+            //cookie-config
+            SessionCookieConfig cookieConfig = getSessionHandler().getSessionManager().getSessionCookieConfig();
+            if (cookieConfig != null)
+            {
+                out.open("cookie-config");
+                if (cookieConfig.getName() != null)
+                    out.tag("name", origin(md,"cookie-config.name"), cookieConfig.getName());
+                
+                if (cookieConfig.getDomain() != null)
+                    out.tag("domain", origin(md, "cookie-config.domain"), cookieConfig.getDomain());
+                
+                if (cookieConfig.getPath() != null)
+                    out.tag("path", origin(md, "cookie-config.path"), cookieConfig.getPath());
+                
+                if (cookieConfig.getComment() != null)
+                    out.tag("comment", origin(md, "cookie-config.comment"), cookieConfig.getComment());
+                
+                out.tag("http-only", origin(md, "cookie-config.http-only"), Boolean.toString(cookieConfig.isHttpOnly()));
+                out.tag("secure", origin(md, "cookie-config.secure"), Boolean.toString(cookieConfig.isSecure()));
+                out.tag("max-age", origin(md, "cookie-config.max-age"), Integer.toString(cookieConfig.getMaxAge()));
+                out.close();
+            }
+            out.close();     
+        }
+        
+        //error-pages
+        Map<String,String> errorPages = ((ErrorPageErrorHandler)getErrorHandler()).getErrorPages();
+        if (errorPages != null)
+        {
+            for (Map.Entry<String, String> entry:errorPages.entrySet())
+            {
+                out.open("error-page", origin(md, "error."+entry.getKey()));
+                //a global or default error page has no code or exception               
+                if (!ErrorPageErrorHandler.GLOBAL_ERROR_PAGE.equals(entry.getKey()))
+                {
+                    if (entry.getKey().matches("\\d{3}"))
+                        out.tag("error-code", entry.getKey());
+                    else
+                        out.tag("exception-type", entry.getKey());
+                }
+                out.tag("location", entry.getValue());
+                out.close();
+            }
+        }
+        
+        //mime-types
+        MimeTypes mimeTypes = getMimeTypes();
+        if (mimeTypes != null)
+        {
+            for (Map.Entry<String, String> entry:mimeTypes.getMimeMap().entrySet())
+            {
+                out.open("mime-mapping");
+                out.tag("extension", origin(md, "extension."+entry.getKey()), entry.getKey());
+                out.tag("mime-type", entry.getValue());
+                out.close();
+            }
+        }
+        
+        //jsp-config
+        JspConfig jspConfig = (JspConfig)getServletContext().getJspConfigDescriptor();
+        if (jspConfig != null)
+        {
+            out.open("jsp-config");
+            Collection<TaglibDescriptor> tlds = jspConfig.getTaglibs();
+            if (tlds != null && !tlds.isEmpty())
+            {
+                for (TaglibDescriptor tld:tlds)
+                {
+                    out.open("taglib");
+                    out.tag("taglib-uri", tld.getTaglibURI());
+                    out.tag("taglib-location", tld.getTaglibLocation());
+                    out.close();
+                }
+            }
+            
+            Collection<JspPropertyGroupDescriptor> jspPropertyGroups = jspConfig.getJspPropertyGroups();
+            if (jspPropertyGroups != null && !jspPropertyGroups.isEmpty())
+            {
+                for (JspPropertyGroupDescriptor jspPropertyGroup:jspPropertyGroups)
+                {
+                    out.open("jsp-property-group");
+                    Collection<String> strings = jspPropertyGroup.getUrlPatterns();
+                    if (strings != null && !strings.isEmpty())
+                    {
+                        for (String urlPattern:strings)
+                            out.tag("url-pattern", urlPattern);
+                    }
+
+                    if (jspPropertyGroup.getElIgnored() != null)
+                        out.tag("el-ignored", jspPropertyGroup.getElIgnored());
+
+                    if (jspPropertyGroup.getPageEncoding() != null)
+                        out.tag("page-encoding", jspPropertyGroup.getPageEncoding());
+
+                    if (jspPropertyGroup.getScriptingInvalid() != null)
+                        out.tag("scripting-invalid", jspPropertyGroup.getScriptingInvalid());
+
+                    if (jspPropertyGroup.getIsXml() != null)
+                        out.tag("is-xml", jspPropertyGroup.getIsXml());
+
+                    if (jspPropertyGroup.getDeferredSyntaxAllowedAsLiteral() != null)
+                        out.tag("deferred-syntax-allowed-as-literal", jspPropertyGroup.getDeferredSyntaxAllowedAsLiteral());
+
+                    if (jspPropertyGroup.getTrimDirectiveWhitespaces() != null)
+                        out.tag("trim-directive-whitespaces", jspPropertyGroup.getTrimDirectiveWhitespaces());
+
+                    if (jspPropertyGroup.getDefaultContentType() != null)
+                        out.tag("default-content-type", jspPropertyGroup.getDefaultContentType());
+
+                    if (jspPropertyGroup.getBuffer() != null)
+                        out.tag("buffer", jspPropertyGroup.getBuffer());
+
+                    if (jspPropertyGroup.getErrorOnUndeclaredNamespace() != null)
+                        out.tag("error-on-undeclared-namespace", jspPropertyGroup.getErrorOnUndeclaredNamespace());
+
+                    strings = jspPropertyGroup.getIncludePreludes();
+                    if (strings != null && !strings.isEmpty())
+                    {
+                        for (String prelude:strings)
+                            out.tag("include-prelude", prelude);
+                    }
+                   
+                    strings = jspPropertyGroup.getIncludeCodas();
+                    if (strings != null && !strings.isEmpty())
+                    {
+                        for (String coda:strings)
+                            out.tag("include-coda", coda);
+                    }
+                   
+                    out.close();
+                }
+            }
+            
+            out.close();
+        }
+
+        //lifecycle: post-construct, pre-destroy
+        LifeCycleCallbackCollection lifecycles = ((LifeCycleCallbackCollection)getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION));
+        if (lifecycles != null)
+        {
+            Collection<LifeCycleCallback> tmp = lifecycles.getPostConstructCallbacks();
+
+            for (LifeCycleCallback c:tmp)
+            {
+                out.open("post-construct");
+                out.tag("lifecycle-callback-class", c.getTargetClassName());
+                out.tag("lifecycle-callback-method", c.getMethodName());
+                out.close();
+            }
+            
+            tmp = lifecycles.getPreDestroyCallbacks();
+            for (LifeCycleCallback c:tmp)
+            {
+                out.open("pre-destroy");
+                out.tag("lifecycle-callback-class", c.getTargetClassName());
+                out.tag("lifecycle-callback-method", c.getMethodName());
+                out.close();
+            }
+        }
+
+        out.literal(extraXML);
+        
+        out.close();
+    }
+
+    private void addContextParamFromAttribute(XmlAppendable out, String attribute) throws IOException
+    {
+        addContextParamFromAttribute(out,attribute,null);
+    }
+    
+    private void addContextParamFromAttribute(XmlAppendable out, String attribute, String resourceBase) throws IOException
+    {
+        Object o=getAttribute(attribute);
+        if (o==null)
+            return;
+                
+        Collection<?> c =  (o instanceof Collection)? (Collection<?>)o:Collections.singletonList(o);
+        StringBuilder v=new StringBuilder();
+        for (Object i:c)
+        {
+            if (i!=null)
+            {
+                if (v.length()>0)
+                    v.append(",\n    ");
+                else
+                    v.append("\n    ");
+                if (resourceBase==null)
+                    QuotedStringTokenizer.quote(v,i.toString());
+                else
+                    QuotedStringTokenizer.quote(v,i.toString().replace(resourceBase,"${WAR}/"));
+            }
+        }
+        out.open("context-param")
+        .tag("param-name",attribute)
+        .tagCDATA("param-value",v.toString())
+        .close();        
+    }
+
+    private static void outholder(XmlAppendable out, MetaData md, String tag, Holder<?> holder) throws IOException
+    {
+        out.open(tag,Collections.singletonMap("source",holder.getSource().toString()));
+        String n = holder.getName();
+        out.tag(tag + "-name",n);
+
+        String ot = n + "." + tag + ".";
+
+        out.tag(tag + "-class",origin(md,ot + tag + "-class"),holder.getClassName());
+
+        for (String p : holder.getInitParameters().keySet())
+        {
+            if ("scratchdir".equalsIgnoreCase(p)) //don't preconfigure the temp dir for jsp output
+                continue;
+            out.open("init-param",origin(md,ot + "init-param." + p))
+            .tag("param-name",p)
+            .tag("param-value",holder.getInitParameter(p))
+            .close();
+        }
+
+        if (holder instanceof ServletHolder)
+        {
+            ServletHolder s = (ServletHolder)holder;
+            if (s.getForcedPath() != null)
+                out.tag("jsp-file",s.getForcedPath());
+
+            if (s.getInitOrder() != 0)
+                out.tag("load-on-startup",Integer.toString(s.getInitOrder()));
+
+            if (s.getRunAsRole() != null)
+                out.open("run-as",origin(md,ot + "run-as"))
+                .tag("role-name",s.getRunAsRole())
+                .close();
+
+            Map<String,String> roles = s.getRoleRefMap();
+            if (roles!=null)
+            {
+                for (Map.Entry<String, String> e : roles.entrySet())
+                {
+                    out.open("security-role-ref",origin(md,ot+"role-name."+e.getKey()))
+                    .tag("role-name",e.getKey())
+                    .tag("role-link",e.getValue())
+                    .close();
+                }
+            }
+            
+            if (!s.isEnabled())
+                out.tag("enabled",origin(md,ot + "enabled"),"false");
+
+            //multipart-config
+            MultipartConfigElement multipartConfig = ((ServletHolder.Registration)s.getRegistration()).getMultipartConfig();
+            if (multipartConfig != null)
+            {
+                out.open("multipart-config", origin(md, s.getName()+".servlet.multipart-config"));
+                if (multipartConfig.getLocation() != null)
+                    out.tag("location", multipartConfig.getLocation());
+                out.tag("max-file-size", Long.toString(multipartConfig.getMaxFileSize()));
+                out.tag("max-request-size", Long.toString(multipartConfig.getMaxRequestSize()));
+                out.tag("file-size-threshold", Long.toString(multipartConfig.getFileSizeThreshold()));
+                out.close();
+            }
+        }
+
+        out.tag("async-supported",origin(md,ot + "async-supported"),holder.isAsyncSupported()?"true":"false");
+        out.close();
+    }
+
+    public static Map<String, String> origin(MetaData md, String name)
+    {
+        if (!LOG.isDebugEnabled())
+            return Collections.emptyMap();
+        if (name == null)
+            return Collections.emptyMap();
+        OriginInfo origin = md.getOriginInfo(name);
+        // System.err.println("origin of "+name+" is "+origin);
+        if (origin == null)
+            return Collections.emptyMap();
+        return Collections.singletonMap("origin",origin.toString());
+
+    }
+    
+}
diff --git a/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureJNDIWar.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureJNDIWar.java
new file mode 100644
index 0000000..570b724
--- /dev/null
+++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureJNDIWar.java
@@ -0,0 +1,49 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class PreconfigureJNDIWar
+{
+    private static final long __start=System.nanoTime();
+    private static final Logger LOG = Log.getLogger(Server.class);
+    
+    public static void main(String[] args) throws Exception
+    {
+        String target="target/test-jndi-preconfigured";
+        File file = new File(target);
+        if (file.exists())
+            IO.delete(file);
+        
+        PreconfigureQuickStartWar.main("target/test-jndi.war",target, "src/test/resources/test-jndi.xml");
+
+        LOG.info("Preconfigured in {}ms",TimeUnit.NANOSECONDS.toMillis(System.nanoTime()-__start));
+        
+        IO.copy(new FileInputStream("target/test-jndi-preconfigured/WEB-INF/quickstart-web.xml"),System.out);
+    }
+
+}
diff --git a/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureSpecWar.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureSpecWar.java
new file mode 100644
index 0000000..f8b49c7
--- /dev/null
+++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureSpecWar.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+
+public class PreconfigureSpecWar
+{
+    private static final long __start=System.nanoTime();
+    private static final Logger LOG = Log.getLogger(Server.class);
+    
+    public static void main(String[] args) throws Exception
+    {
+        String target="target/test-spec-preconfigured";
+        File file = new File(target);
+        if (file.exists())
+            IO.delete(file);
+        
+        File realmPropertiesDest = new File ("target/test-spec-realm.properties");
+        if (realmPropertiesDest.exists())
+            IO.delete(realmPropertiesDest);
+        
+        Resource realmPropertiesSrc = Resource.newResource("src/test/resources/realm.properties");
+        realmPropertiesSrc.copyTo(realmPropertiesDest);
+        System.setProperty("jetty.home", "target");
+        
+        PreconfigureQuickStartWar.main("target/test-spec.war",target, "src/test/resources/test-spec.xml");
+
+        LOG.info("Preconfigured in {}ms",TimeUnit.NANOSECONDS.toMillis(System.nanoTime()-__start));
+        
+        IO.copy(new FileInputStream("target/test-spec-preconfigured/WEB-INF/quickstart-web.xml"),System.out);
+    }
+
+}
diff --git a/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureStandardTestWar.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureStandardTestWar.java
new file mode 100644
index 0000000..91e4241
--- /dev/null
+++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/PreconfigureStandardTestWar.java
@@ -0,0 +1,62 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+
+/**
+ * PreconfigureStandardTestWar
+ *
+ */
+public class PreconfigureStandardTestWar
+{
+    
+    private static final long __start=System.nanoTime();
+    private static final Logger LOG = Log.getLogger(Server.class);
+    
+    public static void main(String[] args) throws Exception
+    {
+        String target="target/test-standard-preconfigured";
+        File file = new File(target);
+        if (file.exists())
+            IO.delete(file);
+        
+        File realmPropertiesDest = new File ("target/test-standard-realm.properties");
+        if (realmPropertiesDest.exists())
+            IO.delete(realmPropertiesDest);
+        
+        Resource realmPropertiesSrc = Resource.newResource("src/test/resources/realm.properties");
+        realmPropertiesSrc.copyTo(realmPropertiesDest);
+        System.setProperty("jetty.home", "target");
+        
+        PreconfigureQuickStartWar.main("target/test-standard.war",target, "src/test/resources/test.xml");
+
+        LOG.info("Preconfigured in {}ms",TimeUnit.NANOSECONDS.toMillis(System.nanoTime()-__start));
+        
+        IO.copy(new FileInputStream("target/test-standard-preconfigured/WEB-INF/quickstart-web.xml"),System.out);
+    }
+}
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartJNDIWar.java
similarity index 60%
copy from jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
copy to jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartJNDIWar.java
index 50a1e5b..07bc1b6 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
+++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartJNDIWar.java
@@ -16,21 +16,16 @@
 //  ========================================================================
 //
 
-package org.eclipse.jetty.spdy.client;
+package org.eclipse.jetty.quickstart;
 
-import org.eclipse.jetty.io.ClientConnectionFactory;
+public class QuickStartJNDIWar 
+{    
 
-public abstract class NegotiatingClientConnectionFactory implements ClientConnectionFactory
-{
-    private final ClientConnectionFactory connectionFactory;
-
-    protected NegotiatingClientConnectionFactory(ClientConnectionFactory connectionFactory)
-    {
-        this.connectionFactory = connectionFactory;
+    public static void main(String... args) throws Exception
+    {   
+        // Log.getRootLogger().setDebugEnabled(true);
+        Quickstart.main("target/test-jndi-preconfigured", "src/test/resources/test-jndi.xml");
     }
-
-    public ClientConnectionFactory getClientConnectionFactory()
-    {
-        return connectionFactory;
-    }
+    
+    
 }
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartSpecWar.java
similarity index 60%
copy from jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
copy to jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartSpecWar.java
index 50a1e5b..b84a12f 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
+++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartSpecWar.java
@@ -16,21 +16,15 @@
 //  ========================================================================
 //
 
-package org.eclipse.jetty.spdy.client;
+package org.eclipse.jetty.quickstart;
 
-import org.eclipse.jetty.io.ClientConnectionFactory;
 
-public abstract class NegotiatingClientConnectionFactory implements ClientConnectionFactory
-{
-    private final ClientConnectionFactory connectionFactory;
-
-    protected NegotiatingClientConnectionFactory(ClientConnectionFactory connectionFactory)
-    {
-        this.connectionFactory = connectionFactory;
-    }
-
-    public ClientConnectionFactory getClientConnectionFactory()
-    {
-        return connectionFactory;
+public class QuickStartSpecWar 
+{    
+    public static void main(String... args) throws Exception
+    {   
+        // Log.getRootLogger().setDebugEnabled(true);
+       System.setProperty("jetty.home", "target");
+       Quickstart.main("target/test-spec-preconfigured", "src/test/resources/test-spec.xml");
     }
 }
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartStandardTestWar.java
similarity index 60%
copy from jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
copy to jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartStandardTestWar.java
index 50a1e5b..7da8b23 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
+++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartStandardTestWar.java
@@ -16,21 +16,15 @@
 //  ========================================================================
 //
 
-package org.eclipse.jetty.spdy.client;
+package org.eclipse.jetty.quickstart;
 
-import org.eclipse.jetty.io.ClientConnectionFactory;
-
-public abstract class NegotiatingClientConnectionFactory implements ClientConnectionFactory
+public class QuickStartStandardTestWar 
 {
-    private final ClientConnectionFactory connectionFactory;
 
-    protected NegotiatingClientConnectionFactory(ClientConnectionFactory connectionFactory)
-    {
-        this.connectionFactory = connectionFactory;
-    }
-
-    public ClientConnectionFactory getClientConnectionFactory()
-    {
-        return connectionFactory;
+    public static void main(String... args) throws Exception
+    {   
+        // Log.getRootLogger().setDebugEnabled(true);
+       System.setProperty("jetty.home", "target");
+       Quickstart.main("target/test-standard-preconfigured", "src/test/resources/test.xml");
     }
 }
diff --git a/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/Quickstart.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/Quickstart.java
new file mode 100644
index 0000000..75dc114
--- /dev/null
+++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/Quickstart.java
@@ -0,0 +1,73 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.quickstart;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.xml.XmlConfiguration;
+
+public class Quickstart
+{
+    
+    public static void main(String... args) throws Exception
+    {   
+        if (args.length<1)
+            error("No WAR file or directory given");
+        
+        //war file or dir to start
+        String war = args[0];
+        
+        //optional jetty context xml file to configure the webapp
+        Resource contextXml = null;
+        if (args.length > 1)
+            contextXml = Resource.newResource(args[1]);
+        
+        Server server = new Server(8080);
+        
+        QuickStartWebApp webapp = new QuickStartWebApp();
+        webapp.setAutoPreconfigure(true);
+        webapp.setWar(war);
+        webapp.setContextPath("/");
+
+        //apply context xml file
+        if (contextXml != null)
+        {
+            // System.err.println("Applying "+contextXml);
+            XmlConfiguration xmlConfiguration = new XmlConfiguration(contextXml.getURL());  
+            xmlConfiguration.configure(webapp);   
+        }
+        
+        server.setHandler(webapp);
+
+        server.start();
+
+        
+      
+        server.join();
+    }
+    
+
+    private static void error(String message)
+    {
+        System.err.println("ERROR: "+message);
+        System.err.println("Usage: java -jar QuickStartWar.jar <war-directory> <context-xml>");
+        System.err.println("       java -jar QuickStartWar.jar <war-file> <context-xml>");
+        System.exit(1);
+    }
+}
diff --git a/jetty-quickstart/src/test/resources/realm.properties b/jetty-quickstart/src/test/resources/realm.properties
new file mode 100644
index 0000000..9d88b85
--- /dev/null
+++ b/jetty-quickstart/src/test/resources/realm.properties
@@ -0,0 +1,21 @@
+#
+# This file defines users passwords and roles for a HashUserRealm
+#
+# The format is
+#  <username>: <password>[,<rolename> ...]
+#
+# Passwords may be clear text, obfuscated or checksummed.  The class 
+# org.eclipse.util.Password should be used to generate obfuscated
+# passwords or password checksums
+#
+# If DIGEST Authentication is used, the password must be in a recoverable
+# format, either plain text or OBF:.
+#
+jetty: MD5:164c88b302622e17050af52c89945d44,user
+admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin,user
+other: OBF:1xmk1w261u9r1w1c1xmq,user
+plain: plain,user
+user: password,user
+
+# This entry is for digest auth.  The credential is a MD5 hash of username:realmname:password
+digest: MD5:6e120743ad67abfbc385bc2bb754e297,user
diff --git a/jetty-quickstart/src/test/resources/test-jndi.xml b/jetty-quickstart/src/test/resources/test-jndi.xml
new file mode 100644
index 0000000..14c0934
--- /dev/null
+++ b/jetty-quickstart/src/test/resources/test-jndi.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<!-- =============================================================== -->
+<!-- Configure the test-jndi webapp                                  -->
+<!-- =============================================================== -->
+<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
+
+  <New id="tx" class="org.eclipse.jetty.plus.jndi.Transaction">
+    <Arg>
+      <New class="com.acme.MockUserTransaction"/>
+    </Arg>
+  </New>
+
+  <!-- Define an env entry with Server scope for java:comp/env                   -->
+  <New id="woggle"  class="org.eclipse.jetty.plus.jndi.EnvEntry">
+    <Arg><Property name='server'/></Arg>
+    <Arg>woggle</Arg>
+    <Arg type="java.lang.Integer">4000</Arg>
+    <Arg type="boolean">false</Arg>
+  </New>
+
+  <!-- Define an env entry with webapp scope for java:comp/env                   -->
+  <New id="wiggle"  class="org.eclipse.jetty.plus.jndi.EnvEntry">
+    <Arg><Ref refid='wac'/></Arg>
+    <Arg>wiggle</Arg>
+    <Arg type="java.lang.Double">100</Arg>
+    <Arg type="boolean">true</Arg>
+  </New>
+
+ <!-- Mail Session setup                                          -->
+ <New id="xxxmail" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg><Ref refid='wac'/></Arg>
+     <Arg>mail/Session</Arg>
+     <Arg>
+       <New class="org.eclipse.jetty.jndi.factories.MailSessionReference">
+         <Set name="user">CHANGE-ME</Set>
+         <Set name="password">CHANGE-ME</Set>
+         <Set name="properties">
+           <New class="java.util.Properties">
+             <Put name="mail.smtp.auth">false</Put> <!-- change to true if you want to authenticate -->
+             <Put name="mail.smtp.host">CHANGE-ME</Put>
+             <Put name="mail.from">CHANGE-ME</Put>
+             <Put name="mail.debug">false</Put>
+           </New>
+          </Set>
+       </New>
+     </Arg>
+ </New>
+
+  <!-- A mock DataSource                                           -->
+  <New id="mydatasource" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg><Ref refid='wac'/></Arg>
+    <Arg>jdbc/mydatasource</Arg>
+    <Arg>
+      <New class="com.acme.MockDataSource"/>
+    </Arg>
+  </New>
+
+</Configure>
diff --git a/jetty-quickstart/src/test/resources/test-spec.xml b/jetty-quickstart/src/test/resources/test-spec.xml
new file mode 100644
index 0000000..99fc577
--- /dev/null
+++ b/jetty-quickstart/src/test/resources/test-spec.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
+
+  <New id="tx" class="org.eclipse.jetty.plus.jndi.Transaction">
+    <Arg>
+      <New class="com.acme.MockUserTransaction"/>
+    </Arg>
+  </New>
+
+  <Get name="securityHandler">
+    <Set name="loginService">
+      <New class="org.eclipse.jetty.security.HashLoginService">
+        <Set name="name">Test Realm</Set>
+        <Set name="config"><SystemProperty name="jetty.home" default="."/>/test-spec-realm.properties</Set>
+      </New>
+    </Set>
+  </Get>
+
+
+  <New id="maxAmount"  class="org.eclipse.jetty.plus.jndi.EnvEntry">
+    <Arg><Ref refid='wac'/></Arg>
+    <Arg>maxAmount</Arg>
+    <Arg type="java.lang.Double">100</Arg>
+    <Arg type="boolean">true</Arg>
+  </New>
+
+
+  <New id="mydatasource" class="org.eclipse.jetty.plus.jndi.Resource">
+    <Arg><Ref refid='wac'/></Arg>
+    <Arg>jdbc/mydatasource</Arg>
+    <Arg>
+     <New class="com.acme.MockDataSource">
+     </New>
+    </Arg>
+   </New>
+
+</Configure>
diff --git a/jetty-quickstart/src/test/resources/test.xml b/jetty-quickstart/src/test/resources/test.xml
new file mode 100644
index 0000000..bbdf08a
--- /dev/null
+++ b/jetty-quickstart/src/test/resources/test.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0"  encoding="ISO-8859-1"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<!-- ==================================================================
+Configure and deploy the test web application in $(jetty.home)/webapps/test
+
+Note. If this file did not exist or used a context path other that /test
+then the default configuration of jetty.xml would discover the test
+webapplication with a WebAppDeployer.  By specifying a context in this
+directory, additional configuration may be specified and hot deployments
+detected.
+===================================================================== -->
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+
+  <Set name="contextPath">/test</Set>
+
+  <Get name="securityHandler">
+    <Set name="loginService">
+      <New class="org.eclipse.jetty.security.HashLoginService">
+        <Set name="name">Test Realm</Set>        
+        <Set name="config"><SystemProperty name="jetty.home" default="."/>/test-standard-realm.properties</Set>
+      </New>
+    </Set>
+    <Set name="authenticator">
+      <New class="org.eclipse.jetty.security.authentication.FormAuthenticator">
+        <Set name="alwaysSaveUri">true</Set>
+      </New>
+    </Set>
+    <Set name="checkWelcomeFiles">true</Set>
+  </Get>
+
+  <!-- Non standard error page mapping -->
+  <!--
+  <Get name="errorHandler">
+    <Call name="addErrorPage">
+      <Arg type="int">500</Arg>
+      <Arg type="int">599</Arg>
+      <Arg type="String">/dump/errorCodeRangeMapping</Arg>
+    </Call>
+  </Get>
+  -->
+
+
+</Configure>
diff --git a/jetty-rewrite/pom.xml b/jetty-rewrite/pom.xml
index ef63744..6b611c3 100644
--- a/jetty-rewrite/pom.xml
+++ b/jetty-rewrite/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-rewrite</artifactId>
diff --git a/jetty-runner/pom.xml b/jetty-runner/pom.xml
index 4c98ff3..950f3b5 100644
--- a/jetty-runner/pom.xml
+++ b/jetty-runner/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-runner</artifactId>
diff --git a/jetty-security/pom.xml b/jetty-security/pom.xml
index 6dbc0e1..e4071d2 100644
--- a/jetty-security/pom.xml
+++ b/jetty-security/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-security</artifactId>
diff --git a/jetty-server/pom.xml b/jetty-server/pom.xml
index d7d2a18..f860621 100644
--- a/jetty-server/pom.xml
+++ b/jetty-server/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-server</artifactId>
diff --git a/jetty-server/src/main/config/modules/ext.mod b/jetty-server/src/main/config/modules/ext.mod
index 1207bdd..56b10f7 100644
--- a/jetty-server/src/main/config/modules/ext.mod
+++ b/jetty-server/src/main/config/modules/ext.mod
@@ -1,9 +1,9 @@
 #
-# Module to add all lib/ext/*.jar files to classpath
+# Module to add all lib/ext/**.jar files to classpath
 #
 
 [lib]
-regex:lib/ext/.*\.jar$
+lib/ext/**.jar
 
 [files]
 lib/
diff --git a/jetty-server/src/main/config/modules/server.mod b/jetty-server/src/main/config/modules/server.mod
index b0c3d08..3cdac35 100644
--- a/jetty-server/src/main/config/modules/server.mod
+++ b/jetty-server/src/main/config/modules/server.mod
@@ -4,7 +4,6 @@
 
 [optional]
 jvm
-jmx
 ext
 resources
 
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
index 9d8b0ae..cd4a169 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
@@ -68,7 +68,7 @@
  * HttpTransport.completed().
  *
  */
-public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
+public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, HttpParser.ProxyHandler
 {
     private static final Logger LOG = Log.getLogger(HttpChannel.class);
     private static final ThreadLocal<HttpChannel<?>> __currentChannel = new ThreadLocal<>();
@@ -478,6 +478,15 @@
     }
 
     @Override
+    public void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort)
+    {
+        _request.setAttribute("PROXY", protocol);
+        _request.setServerName(sAddr);
+        _request.setServerPort(dPort);
+        _request.setRemoteAddr(InetSocketAddress.createUnresolved(sAddr,sPort));
+    }
+    
+    @Override
     public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version)
     {
         _expect = false;
@@ -842,4 +851,5 @@
         }
 
     }
+
 }
diff --git a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NegotiatingServerConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnection.java
similarity index 98%
rename from jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NegotiatingServerConnection.java
rename to jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnection.java
index b1fb87e..3d01069 100644
--- a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NegotiatingServerConnection.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnection.java
@@ -16,7 +16,7 @@
 //  ========================================================================
 //
 
-package org.eclipse.jetty.spdy.server;
+package org.eclipse.jetty.server;
 
 import java.io.IOException;
 import java.util.List;
diff --git a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NegotiatingServerConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java
similarity index 98%
rename from jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NegotiatingServerConnectionFactory.java
rename to jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java
index 4ec3e91..f826d71 100644
--- a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NegotiatingServerConnectionFactory.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java
@@ -16,7 +16,7 @@
 //  ========================================================================
 //
 
-package org.eclipse.jetty.spdy.server;
+package org.eclipse.jetty.server;
 
 import java.util.Arrays;
 import java.util.Iterator;
diff --git a/jetty-servlet/pom.xml b/jetty-servlet/pom.xml
index 46325d6..b9542c9 100644
--- a/jetty-servlet/pom.xml
+++ b/jetty-servlet/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <artifactId>jetty-project</artifactId>
     <groupId>org.eclipse.jetty</groupId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-servlet</artifactId>
diff --git a/jetty-servlets/pom.xml b/jetty-servlets/pom.xml
index 3c8210c..8f3d5af 100644
--- a/jetty-servlets/pom.xml
+++ b/jetty-servlets/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <artifactId>jetty-project</artifactId>
     <groupId>org.eclipse.jetty</groupId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-servlets</artifactId>
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java
index dc2a9c6..ba56bc7 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java
@@ -47,32 +47,33 @@
 //-----------------------------------------------------------------------------
 /**
  * CGI Servlet.
- * <p/>
- * The cgi bin directory can be set with the "cgibinResourceBase" init parameter or it will default to the resource base of the context. If the
- * "cgibinResourceBaseIsRelative" init parameter is set the resource base is relative to the webapp. For example "WEB-INF/cgi" would work.
- * <br/>
- * Not that this only works for extracted war files as "jar cf" will not reserve the execute permissions on the cgi files.
- * <p/>
- * The "commandPrefix" init parameter may be used to set a prefix to all commands passed to exec. This can be used on systems that need assistance to execute a
- * particular file type. For example on windows this can be set to "perl" so that perl scripts are executed.
- * <p/>
- * The "Path" init param is passed to the exec environment as PATH. Note: Must be run unpacked somewhere in the filesystem.
- * <p/>
- * Any initParameter that starts with ENV_ is used to set an environment variable with the name stripped of the leading ENV_ and using the init parameter value.
+ * <p>
+ * 
+ * The following init parameters are used to configure this servlet:
+ * <dl>
+ * <dt>cgibinResourceBase</dt><dd>Path to the cgi bin directory if set or it will default to the resource base of the context.</dd>
+ * <dt>resourceBase</dt><dd>An alias for cgibinResourceBase.</dd>
+ * <dt>cgibinResourceBaseIsRelative</dt><dd>If true then cgibinResourceBase is relative to the webapp (eg "WEB-INF/cgi")</dd>
+ * <dt>commandPrefix</dt><dd>may be used to set a prefix to all commands passed to exec. This can be used on systems that need assistance to execute a
+ * particular file type. For example on windows this can be set to "perl" so that perl scripts are executed.</dd>
+ * <dt>Path</dt><dd>passed to the exec environment as PATH.</dd>
+ * <dt>ENV_*</dt><dd>used to set an arbitrary environment variable with the name stripped of the leading ENV_ and using the init parameter value</dd>
+ * <dt>useFullPath</dt><dd>If true, the full URI path within the context is used for the exec command, otherwise a search is done for a partial URL that matches an exec Command</dd>
+ * </dl>
+ * 
  */
 public class CGI extends HttpServlet
 {
-    /**
-     *
-     */
-    private static final long serialVersionUID = -6182088932884791073L;
+    private static final long serialVersionUID = -6182088932884791074L;
 
     private static final Logger LOG = Log.getLogger(CGI.class);
 
     private boolean _ok;
     private File _docRoot;
+    private boolean _cgiBinProvided;
     private String _path;
     private String _cmdPrefix;
+    private boolean _useFullPath;
     private EnvList _env;
     private boolean _ignoreExitState;
     private boolean _relative;
@@ -83,16 +84,22 @@
     {
         _env = new EnvList();
         _cmdPrefix = getInitParameter("commandPrefix");
+        _useFullPath = Boolean.parseBoolean(getInitParameter("useFullPath"));
         _relative = Boolean.parseBoolean(getInitParameter("cgibinResourceBaseIsRelative"));
 
         String tmp = getInitParameter("cgibinResourceBase");
-        if (tmp == null)
+        if (tmp != null)
+            _cgiBinProvided = true;
+        else
         {
             tmp = getInitParameter("resourceBase");
-            if (tmp == null)
+            if (tmp != null)
+                _cgiBinProvided = true;
+            else
                 tmp = getServletContext().getRealPath("/");
         }
-        else if (_relative)
+
+        if (_relative && _cgiBinProvided)
         {
             tmp = getServletContext().getRealPath(tmp);
         }
@@ -137,10 +144,10 @@
             _env.set("PATH",_path);
 
         _ignoreExitState = "true".equalsIgnoreCase(getInitParameter("ignoreExitState"));
-        Enumeration e = getInitParameterNames();
+        Enumeration<String> e = getInitParameterNames();
         while (e.hasMoreElements())
         {
-            String n = (String)e.nextElement();
+            String n = e.nextElement();
             if (n != null && n.startsWith("ENV_"))
                 _env.set(n.substring(4),getInitParameter(n));
         }
@@ -166,7 +173,6 @@
             return;
         }
 
-        String pathInContext = (_relative?"":StringUtil.nonNull(req.getServletPath())) + StringUtil.nonNull(req.getPathInfo());
         if (LOG.isDebugEnabled())
         {
             LOG.debug("CGI: ContextPath : " + req.getContextPath());
@@ -180,63 +186,69 @@
         // pathInContext may actually comprises scriptName/pathInfo...We will
         // walk backwards up it until we find the script - the rest must
         // be the pathInfo;
+        String pathInContext = (_relative ? "" : StringUtil.nonNull(req.getServletPath())) + StringUtil.nonNull(req.getPathInfo());
+        File execCmd = new File(_docRoot, pathInContext);
+        String pathInfo = pathInContext;
 
-        String both = pathInContext;
-        String first = both;
-        String last = "";
-
-        File exe = new File(_docRoot,first);
-
-        while ((first.endsWith("/") || !exe.exists()) && first.length() >= 0)
+        if(!_useFullPath)
         {
-            int index = first.lastIndexOf('/');
+            String path = pathInContext;
+            String info = "";
 
-            first = first.substring(0,index);
-            last = both.substring(index,both.length());
-            exe = new File(_docRoot,first);
-        }
-
-        if (first.length() == 0 || !exe.exists() || exe.isDirectory() || !exe.getCanonicalPath().equals(exe.getAbsolutePath()))
-        {
-            res.sendError(404);
-        }
-        else
-        {
-            if (LOG.isDebugEnabled())
+            // Search docroot for a matching execCmd
+            while (path.endsWith("/") && path.length() >= 0)
             {
-                LOG.debug("CGI: script is " + exe);
-                LOG.debug("CGI: pathInfo is " + last);
+                if(!execCmd.exists())
+                    break;
+    
+                int index = path.lastIndexOf('/');
+    
+                path = path.substring(0,index);
+                info = pathInContext.substring(index,pathInContext.length());
+                execCmd = new File(_docRoot,path);
             }
-            exec(exe,last,req,res);
+    
+            if (path.length() == 0 || !execCmd.exists() || execCmd.isDirectory() || !execCmd.getCanonicalPath().equals(execCmd.getAbsolutePath()))
+            {
+                res.sendError(404);
+            }
+            
+            pathInfo = info;
         }
+        exec(execCmd,pathInfo,req,res);
     }
 
-    /* ------------------------------------------------------------ */
+    /** executes the CGI process
     /*
-     * @param root @param path @param req @param res @exception IOException
+     * @param command the command to execute, this command is prefixed by
+     *  the context parameter "commandPrefix".
+     * @param pathInfo The PATH_INFO to process,
+     *  see http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getPathInfo%28%29. Cannot be null
+     * @param req
+     * @param res
+     * @exception IOException
      */
     private void exec(File command, String pathInfo, HttpServletRequest req, HttpServletResponse res) throws IOException
     {
-        String path = command.getAbsolutePath();
-        File dir = command.getParentFile();
-        String scriptName = req.getRequestURI().substring(0,req.getRequestURI().length() - pathInfo.length());
-        String scriptPath = getServletContext().getRealPath(scriptName);
-        String pathTranslated = req.getPathTranslated();
+        assert req != null;
+        assert res != null;
+        assert pathInfo != null;
+        assert command != null;
 
-        int len = req.getContentLength();
-        if (len < 0)
-            len = 0;
-        if ((pathTranslated == null) || (pathTranslated.length() == 0))
-            pathTranslated = path;
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("CGI: script is " + command);
+            LOG.debug("CGI: pathInfo is " + pathInfo);
+        }
 
         String bodyFormEncoded = null;
         if ((HttpMethod.POST.equals(req.getMethod()) || HttpMethod.PUT.equals(req.getMethod())) && "application/x-www-form-urlencoded".equals(req.getContentType()))
         {
             MultiMap<String> parameterMap = new MultiMap<String>();
-            Enumeration names = req.getParameterNames();
+            Enumeration<String> names = req.getParameterNames();
             while (names.hasMoreElements())
             {
-                String parameterName = (String)names.nextElement();
+                String parameterName = names.nextElement();
                 parameterMap.addValues(parameterName, req.getParameterValues(parameterName));
             }
             bodyFormEncoded = UrlEncoded.encode(parameterMap, Charset.forName(req.getCharacterEncoding()), true);
@@ -247,24 +259,33 @@
         // look at :
         // http://Web.Golux.Com/coar/cgi/draft-coar-cgi-v11-03-clean.html#6.1.1
         env.set("AUTH_TYPE", req.getAuthType());
+
+        int contentLen = req.getContentLength();
+        if (contentLen < 0)
+            contentLen = 0;
         if (bodyFormEncoded != null)
         {
             env.set("CONTENT_LENGTH", Integer.toString(bodyFormEncoded.length()));
         }
         else
         {
-            env.set("CONTENT_LENGTH", Integer.toString(len));
+            env.set("CONTENT_LENGTH", Integer.toString(contentLen));
         }
         env.set("CONTENT_TYPE", req.getContentType());
         env.set("GATEWAY_INTERFACE", "CGI/1.1");
-        if ((pathInfo != null) && (pathInfo.length() > 0))
+        if (pathInfo.length() > 0)
         {
             env.set("PATH_INFO", pathInfo);
         }
+
+        String pathTranslated = req.getPathTranslated();
+        if ((pathTranslated == null) || (pathTranslated.length() == 0))
+            pathTranslated = pathInfo;
         env.set("PATH_TRANSLATED", pathTranslated);
         env.set("QUERY_STRING", req.getQueryString());
         env.set("REMOTE_ADDR", req.getRemoteAddr());
         env.set("REMOTE_HOST", req.getRemoteHost());
+
         // The identity information reported about the connection by a
         // RFC 1413 [11] request to the remote agent, if
         // available. Servers MAY choose not to support this feature, or
@@ -272,17 +293,33 @@
         // "REMOTE_IDENT" => "NYI"
         env.set("REMOTE_USER", req.getRemoteUser());
         env.set("REQUEST_METHOD", req.getMethod());
-        env.set("SCRIPT_NAME", scriptName);
+
+        String scriptPath;
+        String scriptName;
+        // use docRoot for scriptPath, too
+        if(_cgiBinProvided) 
+        {
+            scriptPath = command.getAbsolutePath();
+            scriptName = scriptPath.substring(_docRoot.getAbsolutePath().length());
+        } 
+        else 
+        {
+            String requestURI = req.getRequestURI();
+            scriptName = requestURI.substring(0,requestURI.length() - pathInfo.length());
+            scriptPath = getServletContext().getRealPath(scriptName);
+        }
         env.set("SCRIPT_FILENAME", scriptPath);
+        env.set("SCRIPT_NAME", scriptName);
+
         env.set("SERVER_NAME", req.getServerName());
         env.set("SERVER_PORT", Integer.toString(req.getServerPort()));
         env.set("SERVER_PROTOCOL", req.getProtocol());
         env.set("SERVER_SOFTWARE", getServletContext().getServerInfo());
 
-        Enumeration enm = req.getHeaderNames();
+        Enumeration<String> enm = req.getHeaderNames();
         while (enm.hasMoreElements())
         {
-            String name = (String)enm.nextElement();
+            String name = enm.nextElement();
             String value = req.getHeader(name);
             env.set("HTTP_" + name.toUpperCase(Locale.ENGLISH).replace('-','_'),value);
         }
@@ -293,29 +330,30 @@
         // "SERVER_URL" => "NYI - http://us0245",
         // "TZ" => System.getProperty("user.timezone"),
 
-        // are we meant to decode args here ? or does the script get them
-        // via PATH_INFO ? if we are, they should be decoded and passed
+        // are we meant to decode args here? or does the script get them
+        // via PATH_INFO? if we are, they should be decoded and passed
         // into exec here...
-        String execCmd = path;
-        if ((execCmd.charAt(0) != '"') && (execCmd.indexOf(" ") >= 0))
+        String absolutePath = command.getAbsolutePath();
+        String execCmd = absolutePath;
+
+        // escape the execCommand
+        if (execCmd.length() > 0 && execCmd.charAt(0) != '"' && execCmd.indexOf(" ") >= 0)
             execCmd = "\"" + execCmd + "\"";
+
         if (_cmdPrefix != null)
             execCmd = _cmdPrefix + " " + execCmd;
 
+        assert execCmd != null;
         LOG.debug("Environment: " + env.getExportString());
         LOG.debug("Command: " + execCmd);
 
-        final Process p;
-        if (dir == null)
-            p = Runtime.getRuntime().exec(execCmd, env.getEnvArray());
-        else
-            p = Runtime.getRuntime().exec(execCmd, env.getEnvArray(), dir);
+        final Process p = Runtime.getRuntime().exec(execCmd, env.getEnvArray(), _docRoot);
 
         // hook processes input to browser's output (async)
         if (bodyFormEncoded != null)
             writeProcessInput(p, bodyFormEncoded);
-        else if (len > 0)
-            writeProcessInput(p, req.getInputStream(), len);
+        else if (contentLen > 0)
+            writeProcessInput(p, req.getInputStream(), contentLen);
 
         // hook processes output to browser's input (sync)
         // if browser closes stream, we should detect it and kill process...
@@ -336,9 +374,9 @@
                     {
                         LOG.warn(e);
                     }
-                }     
+                }
             });
-            
+
             // read any headers off the top of our input stream
             // NOTE: Multiline header items not supported!
             String line = null;
@@ -383,7 +421,7 @@
                 int exitValue = p.exitValue();
                 if (0 != exitValue)
                 {
-                    LOG.warn("Non-zero exit status (" + exitValue + ") from CGI program: " + path);
+                    LOG.warn("Non-zero exit status (" + exitValue + ") from CGI program: " + absolutePath);
                     if (!res.isCommitted())
                         res.sendError(500,"Failed to exec CGI");
                 }
@@ -393,7 +431,7 @@
         {
             // browser has probably closed its input stream - we
             // terminate and clean up...
-            LOG.debug("CGI: Client closed connection!");
+            LOG.debug("CGI: Client closed connection!", e);
         }
         catch (InterruptedException ie)
         {
@@ -422,6 +460,7 @@
     {
         new Thread(new Runnable()
         {
+            @Override
             public void run()
             {
                 try
@@ -445,6 +484,7 @@
 
         new Thread(new Runnable()
         {
+            @Override
             public void run()
             {
                 try
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java
index 53931e4..3151ab8 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java
@@ -115,37 +115,13 @@
     
     private void gzip(ByteBuffer content, boolean complete, final Callback callback)
     {
-        if (content.hasRemaining())
+        if (content.hasRemaining() || complete)
         {
             if (content.hasArray())
                 new GzipArrayCB(content,complete,callback).iterate();
             else
                 new GzipBufferCB(content,complete,callback).iterate();
         }
-        else if (complete)
-        {
-            _deflater.finish();
-
-            int produced=_deflater.deflate(_buffer.array(),_buffer.arrayOffset()+_buffer.limit(),_buffer.capacity()-_buffer.limit(),Deflater.NO_FLUSH);
-            _buffer.limit(_buffer.limit()+produced);
-            addTrailer();
-            superWrite(_buffer,complete,new Callback()
-            {
-                @Override
-                public void succeeded()
-                {
-                    getHttpChannel().getByteBufferPool().release(_buffer);
-                    _buffer=null;
-                    callback.succeeded();
-                }
-
-                @Override
-                public void failed(Throwable x)
-                {
-                    callback.failed(x);
-                }
-            });
-        }
     }
 
     protected void commit(ByteBuffer content, boolean complete, Callback callback)
diff --git a/jetty-spdy/pom.xml b/jetty-spdy/pom.xml
index 5027233..716ea6c 100644
--- a/jetty-spdy/pom.xml
+++ b/jetty-spdy/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty</groupId>
         <artifactId>jetty-project</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
@@ -21,18 +21,19 @@
         <module>spdy-http-server</module>
         <module>spdy-http-client-transport</module>
         <module>spdy-example-webapp</module>
+        <module>spdy-alpn-tests</module>
     </modules>
 
     <profiles>
-        <profile>
-            <id>jdk7-npn</id>
-            <activation>
-                <jdk>[1.7,1.8)</jdk>
-            </activation>
-            <modules>
-                <module>spdy-npn-tests</module>
-            </modules>
-        </profile>
+      <profile>
+        <id>npn</id>
+        <activation>
+          <jdk>1.7</jdk>
+        </activation>
+        <modules>
+          <module>spdy-npn-tests</module>
+        </modules>
+      </profile>
     </profiles>
 
     <build>
diff --git a/jetty-spdy/spdy-alpn-tests/pom.xml b/jetty-spdy/spdy-alpn-tests/pom.xml
new file mode 100644
index 0000000..393532c
--- /dev/null
+++ b/jetty-spdy/spdy-alpn-tests/pom.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.eclipse.jetty.spdy</groupId>
+        <artifactId>spdy-parent</artifactId>
+        <version>9.2.0-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>spdy-alpn-tests</artifactId>
+    <name>Jetty :: SPDY :: ALPN Tests</name>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>org.mortbay.jetty.alpn</groupId>
+                                    <artifactId>alpn-boot</artifactId>
+                                    <version>${alpn.version}</version>
+                                    <type>jar</type>
+                                    <overWrite>false</overWrite>
+                                    <outputDirectory>${project.build.directory}/alpn</outputDirectory>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <argLine>-Xbootclasspath/p:${project.build.directory}/alpn/alpn-boot-${alpn.version}.jar</argLine>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.eclipse.jetty.alpn</groupId>
+            <artifactId>alpn-api</artifactId>
+            <version>${alpn.api.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-alpn-server</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.spdy</groupId>
+            <artifactId>spdy-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.spdy</groupId>
+            <artifactId>spdy-http-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.spdy</groupId>
+            <artifactId>spdy-http-server</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    
+</project>
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNNegotiationTest.java b/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNNegotiationTest.java
new file mode 100644
index 0000000..0f3c4be
--- /dev/null
+++ b/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNNegotiationTest.java
@@ -0,0 +1,216 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.spdy.server;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ALPNNegotiationTest extends AbstractALPNTest
+{
+    @Test
+    public void testClientAdvertisingHTTPServerSpeaksHTTP() throws Exception
+    {
+        InetSocketAddress address = prepare();
+        connector.addConnectionFactory(new HttpConnectionFactory());
+
+        SslContextFactory sslContextFactory = newSslContextFactory();
+        sslContextFactory.start();
+        SSLContext sslContext = sslContextFactory.getSslContext();
+
+        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
+        {
+            client.setUseClientMode(true);
+            client.setSoTimeout(5000);
+
+            ALPN.put(client, new ALPN.ClientProvider()
+            {
+                @Override
+                public boolean supports()
+                {
+                    return true;
+                }
+
+                @Override
+                public void unsupported()
+                {
+                }
+
+                @Override
+                public List<String> protocols()
+                {
+                    return Arrays.asList("http/1.1");
+                }
+
+                @Override
+                public void selected(String protocol)
+                {
+                    Assert.assertEquals("http/1.1", protocol);
+                }
+            });
+
+            client.startHandshake();
+
+            // Verify that the server really speaks http/1.1
+
+            OutputStream output = client.getOutputStream();
+            output.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost:" + address.getPort() + "\r\n" +
+                    "\r\n" +
+                    "").getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            InputStream input = client.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+            String line = reader.readLine();
+            Assert.assertTrue(line.contains(" 404 "));
+        }
+    }
+
+    @Test
+    public void testClientAdvertisingMultipleProtocolsServerSpeaksHTTPWhenNegotiated() throws Exception
+    {
+        InetSocketAddress address = prepare();
+        connector.addConnectionFactory(new HttpConnectionFactory());
+
+        SslContextFactory sslContextFactory = newSslContextFactory();
+        sslContextFactory.start();
+        SSLContext sslContext = sslContextFactory.getSslContext();
+        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
+        {
+            client.setUseClientMode(true);
+            client.setSoTimeout(5000);
+
+            ALPN.put(client, new ALPN.ClientProvider()
+            {
+                @Override
+                public boolean supports()
+                {
+                    return true;
+                }
+
+                @Override
+                public void unsupported()
+                {
+                }
+
+                @Override
+                public List<String> protocols()
+                {
+                    return Arrays.asList("unknown/1.0", "http/1.1");
+                }
+
+                @Override
+                public void selected(String protocol)
+                {
+                    Assert.assertEquals("http/1.1", protocol);
+                }
+            });
+
+            client.startHandshake();
+
+            // Verify that the server really speaks http/1.1
+
+            OutputStream output = client.getOutputStream();
+            output.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost:" + address.getPort() + "\r\n" +
+                    "\r\n" +
+                    "").getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            InputStream input = client.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+            String line = reader.readLine();
+            Assert.assertTrue(line.contains(" 404 "));
+        }
+    }
+
+    @Test
+    public void testClientNotSupportingALPNServerSpeaksDefaultProtocol() throws Exception
+    {
+        InetSocketAddress address = prepare();
+        connector.addConnectionFactory(new HttpConnectionFactory());
+
+        SslContextFactory sslContextFactory = newSslContextFactory();
+        sslContextFactory.start();
+        SSLContext sslContext = sslContextFactory.getSslContext();
+        try (SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()))
+        {
+            client.setUseClientMode(true);
+            client.setSoTimeout(5000);
+
+            ALPN.put(client, new ALPN.ClientProvider()
+            {
+                @Override
+                public boolean supports()
+                {
+                    return false;
+                }
+
+                @Override
+                public void unsupported()
+                {
+                }
+
+                @Override
+                public List<String> protocols()
+                {
+                    return null;
+                }
+
+                @Override
+                public void selected(String s)
+                {
+                }
+            });
+
+            client.startHandshake();
+
+            // Verify that the server really speaks http/1.1
+
+            OutputStream output = client.getOutputStream();
+            output.write(("" +
+                    "GET / HTTP/1.1\r\n" +
+                    "Host: localhost:" + address.getPort() + "\r\n" +
+                    "\r\n" +
+                    "").getBytes(StandardCharsets.UTF_8));
+            output.flush();
+
+            InputStream input = client.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+            String line = reader.readLine();
+            Assert.assertTrue(line.contains(" 404 "));
+        }
+    }
+}
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNSynReplyTest.java b/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNSynReplyTest.java
new file mode 100644
index 0000000..d9f9270
--- /dev/null
+++ b/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/ALPNSynReplyTest.java
@@ -0,0 +1,160 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+
+package org.eclipse.jetty.spdy.server;
+
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.List;
+import javax.net.ssl.SSLEngine;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ALPNSynReplyTest extends AbstractALPNTest
+{
+    @Test
+    public void testGentleCloseDuringHandshake() throws Exception
+    {
+        InetSocketAddress address = prepare();
+        SslContextFactory sslContextFactory = newSslContextFactory();
+        sslContextFactory.start();
+        SSLEngine sslEngine = sslContextFactory.newSSLEngine(address);
+        sslEngine.setUseClientMode(true);
+        ALPN.put(sslEngine, new ALPN.ClientProvider()
+        {
+            @Override
+            public boolean supports()
+            {
+                return true;
+            }
+
+            @Override
+            public void unsupported()
+            {
+            }
+
+            @Override
+            public List<String> protocols()
+            {
+                return null;
+            }
+
+            @Override
+            public void selected(String protocol)
+            {
+            }
+        });
+        sslEngine.beginHandshake();
+
+        ByteBuffer encrypted = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
+        sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
+        encrypted.flip();
+
+        try (SocketChannel channel = SocketChannel.open(address))
+        {
+            // Send ClientHello, immediately followed by TLS Close Alert and then by FIN
+            channel.write(encrypted);
+            sslEngine.closeOutbound();
+            encrypted.clear();
+            sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
+            encrypted.flip();
+            channel.write(encrypted);
+            channel.shutdownOutput();
+
+            // Read ServerHello from server
+            encrypted.clear();
+            int read = channel.read(encrypted);
+            encrypted.flip();
+            Assert.assertTrue(read > 0);
+            // Cannot decrypt, as the SSLEngine has been already closed
+
+            // Now if we read more, we should either read the TLS Close Alert, or directly -1
+            encrypted.clear();
+            read = channel.read(encrypted);
+            // Sending a TLS Close Alert during handshake results in an exception when
+            // unwrapping that the server react to by closing the connection abruptly.
+            Assert.assertTrue(read < 0);
+        }
+    }
+
+    @Test
+    public void testAbruptCloseDuringHandshake() throws Exception
+    {
+        InetSocketAddress address = prepare();
+        SslContextFactory sslContextFactory = newSslContextFactory();
+        sslContextFactory.start();
+        SSLEngine sslEngine = sslContextFactory.newSSLEngine(address);
+        sslEngine.setUseClientMode(true);
+        ALPN.put(sslEngine, new ALPN.ClientProvider()
+        {
+            @Override
+            public boolean supports()
+            {
+                return true;
+            }
+
+            @Override
+            public void unsupported()
+            {
+            }
+
+            @Override
+            public List<String> protocols()
+            {
+                return null;
+            }
+
+            @Override
+            public void selected(String s)
+            {
+            }
+        });
+        sslEngine.beginHandshake();
+
+        ByteBuffer encrypted = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
+        sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted);
+        encrypted.flip();
+
+        try (SocketChannel channel = SocketChannel.open(address))
+        {
+            // Send ClientHello, immediately followed by FIN (no TLS Close Alert)
+            channel.write(encrypted);
+            channel.shutdownOutput();
+
+            // Read ServerHello from server
+            encrypted.clear();
+            int read = channel.read(encrypted);
+            encrypted.flip();
+            Assert.assertTrue(read > 0);
+            ByteBuffer decrypted = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize());
+            sslEngine.unwrap(encrypted, decrypted);
+
+            // Now if we read more, we should either read the TLS Close Alert, or directly -1
+            encrypted.clear();
+            read = channel.read(encrypted);
+            // Since we have close the connection abruptly, the server also does so
+            Assert.assertTrue(read < 0);
+        }
+    }
+}
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/AbstractALPNTest.java b/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/AbstractALPNTest.java
new file mode 100644
index 0000000..652d887
--- /dev/null
+++ b/jetty-spdy/spdy-alpn-tests/src/test/java/org/eclipse/jetty/spdy/server/AbstractALPNTest.java
@@ -0,0 +1,78 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.spdy.server;
+
+import java.net.InetSocketAddress;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.spdy.client.SPDYClient;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Rule;
+
+public class AbstractALPNTest
+{
+    @Rule
+    public final TestTracker tracker = new TestTracker();
+    protected Server server;
+    protected SPDYServerConnector connector;
+    protected SPDYClient.Factory clientFactory;
+
+    protected InetSocketAddress prepare() throws Exception
+    {
+        server = new Server();
+        connector = new SPDYServerConnector(server, newSslContextFactory(), null, new ALPNServerConnectionFactory("spdy/3", "spdy/2", "http/1.1"));
+        connector.setPort(0);
+        connector.setIdleTimeout(30000);
+        server.addConnector(connector);
+        server.start();
+
+        QueuedThreadPool threadPool = new QueuedThreadPool();
+        threadPool.setName(threadPool.getName() + "-client");
+        clientFactory = new SPDYClient.Factory(threadPool);
+        clientFactory.start();
+
+        ALPN.debug = true;
+
+        return new InetSocketAddress("localhost", connector.getLocalPort());
+    }
+
+    protected SslContextFactory newSslContextFactory()
+    {
+        SslContextFactory sslContextFactory = new SslContextFactory();
+        sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+        sslContextFactory.setKeyStorePassword("storepwd");
+        sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
+        sslContextFactory.setTrustStorePassword("storepwd");
+        sslContextFactory.setProtocol("TLSv1");
+        sslContextFactory.setIncludeProtocols("TLSv1");
+        return sslContextFactory;
+    }
+
+    @After
+    public void dispose() throws Exception
+    {
+        clientFactory.stop();
+        server.stop();
+    }
+}
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/resources/jetty-logging.properties b/jetty-spdy/spdy-alpn-tests/src/test/resources/jetty-logging.properties
new file mode 100644
index 0000000..ead13ec
--- /dev/null
+++ b/jetty-spdy/spdy-alpn-tests/src/test/resources/jetty-logging.properties
@@ -0,0 +1,2 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.spdy.LEVEL=DEBUG
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/resources/keystore.jks b/jetty-spdy/spdy-alpn-tests/src/test/resources/keystore.jks
new file mode 100644
index 0000000..428ba54
--- /dev/null
+++ b/jetty-spdy/spdy-alpn-tests/src/test/resources/keystore.jks
Binary files differ
diff --git a/jetty-spdy/spdy-alpn-tests/src/test/resources/truststore.jks b/jetty-spdy/spdy-alpn-tests/src/test/resources/truststore.jks
new file mode 100644
index 0000000..839cb8c
--- /dev/null
+++ b/jetty-spdy/spdy-alpn-tests/src/test/resources/truststore.jks
Binary files differ
diff --git a/jetty-spdy/spdy-client/pom.xml b/jetty-spdy/spdy-client/pom.xml
index f3e2ed1..5bbfd33 100644
--- a/jetty-spdy/spdy-client/pom.xml
+++ b/jetty-spdy/spdy-client/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.spdy</groupId>
         <artifactId>spdy-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
@@ -29,7 +29,7 @@
                         <configuration>
                             <instructions>
                                 <Export-Package>org.eclipse.jetty.spdy.client;version="9.1"</Export-Package>
-                                <Import-Package>!org.eclipse.jetty.npn,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
+                                <Import-Package>!org.eclipse.jetty.npn,!org.eclipse.jetty.alpn,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
                             </instructions>
                           </configuration>
                        </execution>
@@ -45,6 +45,17 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-alpn-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.alpn</groupId>
+            <artifactId>alpn-api</artifactId>
+            <version>${alpn.api.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.eclipse.jetty.npn</groupId>
             <artifactId>npn-api</artifactId>
             <version>${npn.api.version}</version>
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnection.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnection.java
index c8e85f2..69c11d8 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnection.java
+++ b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnection.java
@@ -25,6 +25,7 @@
 
 import org.eclipse.jetty.io.ClientConnectionFactory;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.NegotiatingClientConnection;
 import org.eclipse.jetty.npn.NextProtoNego;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnectionFactory.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnectionFactory.java
index d045801..2a7d332 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnectionFactory.java
+++ b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnectionFactory.java
@@ -26,6 +26,7 @@
 import org.eclipse.jetty.io.ClientConnectionFactory;
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
 import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
 
 public class NPNClientConnectionFactory extends NegotiatingClientConnectionFactory
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java
index d30bc99..786833d 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java
+++ b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java
@@ -37,6 +37,7 @@
 import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
 import org.eclipse.jetty.io.SelectChannelEndPoint;
 import org.eclipse.jetty.io.SelectorManager;
 import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
diff --git a/jetty-spdy/spdy-core/pom.xml b/jetty-spdy/spdy-core/pom.xml
index db39f06..8154ab7 100644
--- a/jetty-spdy/spdy-core/pom.xml
+++ b/jetty-spdy/spdy-core/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.spdy</groupId>
         <artifactId>spdy-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-spdy/spdy-example-webapp/pom.xml b/jetty-spdy/spdy-example-webapp/pom.xml
index 5cd47a6..6cfb0d8 100644
--- a/jetty-spdy/spdy-example-webapp/pom.xml
+++ b/jetty-spdy/spdy-example-webapp/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.spdy</groupId>
         <artifactId>spdy-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>spdy-example-webapp</artifactId>
diff --git a/jetty-spdy/spdy-http-client-transport/pom.xml b/jetty-spdy/spdy-http-client-transport/pom.xml
index e428e24..2d1d1d0 100644
--- a/jetty-spdy/spdy-http-client-transport/pom.xml
+++ b/jetty-spdy/spdy-http-client-transport/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.spdy</groupId>
         <artifactId>spdy-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-spdy/spdy-http-common/pom.xml b/jetty-spdy/spdy-http-common/pom.xml
index 0184925..61fa901 100644
--- a/jetty-spdy/spdy-http-common/pom.xml
+++ b/jetty-spdy/spdy-http-common/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.spdy</groupId>
         <artifactId>spdy-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-spdy/spdy-http-server/pom.xml b/jetty-spdy/spdy-http-server/pom.xml
index af685e8..dea1bf5 100644
--- a/jetty-spdy/spdy-http-server/pom.xml
+++ b/jetty-spdy/spdy-http-server/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.spdy</groupId>
         <artifactId>spdy-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>spdy-http-server</artifactId>
diff --git a/jetty-spdy/spdy-http-server/src/main/config/etc/jetty-spdy.xml b/jetty-spdy/spdy-http-server/src/main/config/etc/jetty-spdy.xml
index 41bfb86..b094d7c 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/etc/jetty-spdy.xml
+++ b/jetty-spdy/spdy-http-server/src/main/config/etc/jetty-spdy.xml
@@ -9,12 +9,6 @@
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
 
     <!-- =========================================================== -->
-    <!-- Enables NPN debugging on System.err                         -->
-    <!-- ===========================================================
-     <Set class="org.eclipse.jetty.npn.NextProtoNego" name="debug" type="boolean">true</Set>
-    -->
-
-    <!-- =========================================================== -->
     <!-- Create a push strategy which can be used by reference by    -->
     <!-- individual connection factories below.                      -->
     <!--                                                             -->
@@ -53,16 +47,17 @@
     <!-- =========================================================== -->
     <!-- Add a SPDY/HTTPS Connector.                                 -->
     <!-- Configure an o.e.j.server.ServerConnector with connection   -->
-    <!-- factories for TLS (aka SSL), NPN, SPDY and HTTP to provide  -->
-    <!-- a connector that can accept HTTPS or SPDY connections.      -->
+    <!-- factories for TLS (aka SSL), ProtoNego, SPDY and HTTP to    -->
+    <!-- provide a connector that can accept HTTPS or SPDY           -->
+    <!-- connections.                                                -->
     <!--                                                             -->
-    <!-- All accepted TLS connections are initially wired to a NPN   -->
-    <!-- connection, which attempts to use a TLS extension to        -->
-    <!-- negotiation the protocol.     If NPN is not supported by    -->
-    <!-- the client, then the NPN connection is replaced by a HTTP   -->
+    <!-- All accepted TLS connections are initially wired to a       -->
+    <!-- Protonego connection, which attempts to use a TLS extension -->
+    <!-- to negotiation the protocol.  If it is not supported by     -->
+    <!-- the client, then the connection is replaced by a HTTP       -->
     <!-- connection.  If a specific protocol version (eg spdy/3) is  -->
-    <!-- negotiated by NPN, then the appropriate connection factory  -->
-    <!-- is used to create a connection to replace the NPN connection-->
+    <!-- negotiated, then the appropriate connection factory         -->
+    <!-- is used to create a connection to replace the connection    -->
     <!--                                                             -->
     <!-- The final result is a SPDY or HTTP connection wired behind  -->
     <!-- a TLS (aka SSL) connection.                                 -->
@@ -80,10 +75,10 @@
                 <Arg name="factories">
                     <Array type="org.eclipse.jetty.server.ConnectionFactory">
 
-                        <!-- SSL Connection factory with NPN as next protocol -->
+                        <!-- SSL Connection factory with Protonego as next protocol -->
                         <Item>
                             <New class="org.eclipse.jetty.server.SslConnectionFactory">
-                                <Arg name="next">npn</Arg>
+                                <Arg name="next"><Property name="protonego"/></Arg>
                                 <Arg name="sslContextFactory">
                                     <Ref refid="sslContextFactory"/>
                                 </Arg>
@@ -92,16 +87,7 @@
 
                         <!-- NPN Connection factory with HTTP as default protocol -->
                         <Item>
-                            <New class="org.eclipse.jetty.spdy.server.NPNServerConnectionFactory">
-                                <Arg name="protocols">
-                                    <Array type="String">
-                                        <Item>spdy/3</Item>
-                                        <Item>spdy/2</Item>
-                                        <Item>http/1.1</Item>
-                                    </Array>
-                                </Arg>
-                                <Set name="defaultProtocol">http/1.1</Set>
-                            </New>
+			    <Ref refid="protonego"/>
                         </Item>
 
                         <!-- SPDY/3 Connection factory -->
diff --git a/jetty-spdy/spdy-http-server/src/main/config/etc/protonego-npn.xml b/jetty-spdy/spdy-http-server/src/main/config/etc/protonego-npn.xml
new file mode 100644
index 0000000..6e30f39
--- /dev/null
+++ b/jetty-spdy/spdy-http-server/src/main/config/etc/protonego-npn.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure id="protonego" class="org.eclipse.jetty.spdy.server.NPNServerConnectionFactory">
+    <Arg name="protocols">
+	<Array type="String">
+	    <Item>spdy/3</Item>
+	    <Item>spdy/2</Item>
+	    <Item>http/1.1</Item>
+	</Array>
+    </Arg>
+   
+    <Set name="defaultProtocol">http/1.1</Set>
+
+    <!-- =========================================================== -->
+    <!-- Enables NPN debugging on System.err                         -->
+    <!-- ===========================================================
+     <Set class="org.eclipse.jetty.npn.NextProtoNego" name="debug" type="boolean">true</Set>
+    -->
+
+</Configure>
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_04.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_04.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_04.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_04.mod
index 7a6b0ca..3d1312b 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_04.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_04.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.0.v20120525/npn-boot-1.1.0.v20120525.jar:lib/npn/npn-boot-1.1.0.v20120525.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.0.v20120525.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_05.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_05.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_05.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_05.mod
index 7a6b0ca..3d1312b 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_05.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_05.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.0.v20120525/npn-boot-1.1.0.v20120525.jar:lib/npn/npn-boot-1.1.0.v20120525.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.0.v20120525.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_06.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_06.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_06.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_06.mod
index fb258e1..8ae3b1c 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_06.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_06.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.1.v20121030/npn-boot-1.1.1.v20121030.jar:lib/npn/npn-boot-1.1.1.v20121030.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.1.v20121030.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_07.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_07.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_07.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_07.mod
index fb258e1..8ae3b1c 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_07.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_07.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.1.v20121030/npn-boot-1.1.1.v20121030.jar:lib/npn/npn-boot-1.1.1.v20121030.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.1.v20121030.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_09.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_09.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_09.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_09.mod
index b2d4780..b4415d2 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_09.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_09.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar:lib/npn/npn-boot-1.1.3.v20130313.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_10.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_10.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_10.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_10.mod
index b2d4780..b4415d2 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_10.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_10.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar:lib/npn/npn-boot-1.1.3.v20130313.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_11.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_11.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_11.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_11.mod
index b2d4780..b4415d2 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_11.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_11.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.3.v20130313/npn-boot-1.1.3.v20130313.jar:lib/npn/npn-boot-1.1.3.v20130313.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.3.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_13.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_13.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_13.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_13.mod
index d177c20..c557a7c 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_13.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_13.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.4.v20130313/npn-boot-1.1.4.v20130313.jar:lib/npn/npn-boot-1.1.4.v20130313.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.4.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_15.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_15.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_15.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_15.mod
index 0c1bc8e..98ddbe7 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_15.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_15.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar:lib/npn/npn-boot-1.1.5.v20130313.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_17.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_17.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_17.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_17.mod
index 0c1bc8e..98ddbe7 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_17.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_17.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar:lib/npn/npn-boot-1.1.5.v20130313.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_21.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_21.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_21.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_21.mod
index 0c1bc8e..98ddbe7 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_21.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_21.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar:lib/npn/npn-boot-1.1.5.v20130313.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_25.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_25.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_25.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_25.mod
index 0c1bc8e..98ddbe7 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_25.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_25.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar:lib/npn/npn-boot-1.1.5.v20130313.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.5.v20130313.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_40.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_40.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_40.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_40.mod
index a067626..a7e47a5 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_40.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_40.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar:lib/npn/npn-boot-1.1.6.v20130911.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_45.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_45.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_45.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_45.mod
index a067626..a7e47a5 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_45.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_45.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar:lib/npn/npn-boot-1.1.6.v20130911.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_51.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_51.mod
similarity index 87%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_51.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_51.mod
index a067626..a7e47a5 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_51.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn-1.7.0_51.mod
@@ -1,9 +1,8 @@
 [name]
-npn-boot
+protonego-boot
 
 [files]
 http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar:lib/npn/npn-boot-1.1.6.v20130911.jar
 
-[ini-template]
---exec
+[exec]
 -Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn.mod
similarity index 83%
rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn.mod
rename to jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn.mod
index 36681ee..040aad1 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/npn.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/protonego-impl/npn.mod
@@ -1,20 +1,8 @@
-#
-# NPN Module
-#
-
-[depend]
-npn/npn-${java.version}
-
-[files]
-lib/
-lib/npn/
-
-[ini-template]
 # NPN is provided via a -Xbootclasspath that modifies the secure connections
 # in java to support the NPN layer needed for SPDY.
 #
 # This modification has a tight dependency on specific updates of Java 1.7.
-# (No support for Java 8 currently exists for npn / npn-boot)
+# (No support for Java 8 exists for npn / npn-boot, use alpn instead)
 #
 # The npn module will use an appropriate npn-boot jar for your specific
 # version of Java.
@@ -29,4 +17,15 @@
 # http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/
 
 
+[name]
+protonego-impl
 
+[depend]
+protonego-impl/npn-${java.version}
+
+[xml]
+etc/protonego-npn.xml
+
+[files]
+lib/
+lib/npn/
diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/spdy.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/spdy.mod
index eab614b..cf79dfa 100644
--- a/jetty-spdy/spdy-http-server/src/main/config/modules/spdy.mod
+++ b/jetty-spdy/spdy-http-server/src/main/config/modules/spdy.mod
@@ -4,7 +4,7 @@
 
 [depend]
 ssl
-npn
+protonego
 
 [lib]
 lib/spdy/*.jar
@@ -15,9 +15,12 @@
 
 [ini-template]
 ## SPDY Configuration
+
 # Port for SPDY connections
 spdy.port=8443
+
 # SPDY idle timeout in milliseconds
 spdy.timeout=30000
+
 # Initial Window Size for SPDY
 #spdy.initialWindowSize=65536
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPSPDYProxyServerConnector.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPSPDYProxyServerConnector.java
index 4c3015e..65d16e2 100644
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPSPDYProxyServerConnector.java
+++ b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPSPDYProxyServerConnector.java
@@ -22,11 +22,11 @@
 import java.util.Objects;
 
 import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.spdy.api.SPDY;
 import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
-import org.eclipse.jetty.spdy.server.NegotiatingServerConnectionFactory;
 import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 
diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPLoadTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPLoadTest.java
index 94e481b..9e72303 100644
--- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPLoadTest.java
+++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPLoadTest.java
@@ -40,6 +40,7 @@
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
@@ -53,7 +54,6 @@
 import org.eclipse.jetty.spdy.api.StringDataInfo;
 import org.eclipse.jetty.spdy.api.SynInfo;
 import org.eclipse.jetty.spdy.client.SPDYClient;
-import org.eclipse.jetty.spdy.server.NegotiatingServerConnectionFactory;
 import org.eclipse.jetty.spdy.server.http.SPDYTestUtils;
 import org.eclipse.jetty.toolchain.test.TestTracker;
 import org.eclipse.jetty.util.Callback;
diff --git a/jetty-spdy/spdy-npn-tests/pom.xml b/jetty-spdy/spdy-npn-tests/pom.xml
index 8fc12c2..23464c5 100644
--- a/jetty-spdy/spdy-npn-tests/pom.xml
+++ b/jetty-spdy/spdy-npn-tests/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.spdy</groupId>
         <artifactId>spdy-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/NPNModuleTest.java b/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/NPNModuleTest.java
index 8464832..b9c74a6 100644
--- a/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/NPNModuleTest.java
+++ b/jetty-spdy/spdy-npn-tests/src/test/java/org/eclipse/jetty/spdy/server/NPNModuleTest.java
@@ -18,6 +18,9 @@
 
 package org.eclipse.jetty.spdy.server;
 
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -26,8 +29,10 @@
 import java.net.MalformedURLException;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.regex.Pattern;
 
 import org.eclipse.jetty.start.BaseHome;
 import org.eclipse.jetty.start.FileArg;
@@ -42,12 +47,6 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
 @RunWith(Parameterized.class)
 public class NPNModuleTest
 {
@@ -70,11 +69,11 @@
     @Parameters(name = "{index}: mod:{0}")
     public static List<Object[]> data()
     {
-        File npnBootModDir = MavenTestingUtils.getProjectDir("../spdy-http-server/src/main/config/modules/npn");
+        File npnBootModDir = MavenTestingUtils.getProjectDir("../spdy-http-server/src/main/config/modules/protonego-impl");
         List<Object[]> data = new ArrayList<>();
         for (File file : npnBootModDir.listFiles())
         {
-            if (file.getName().endsWith(".mod"))
+            if (Pattern.matches("npn-.*\\.mod",file.getName()))
             {
                 data.add(new Object[] { file.getName() });
             }
@@ -88,12 +87,14 @@
     private static BaseHome basehome;
 
     @BeforeClass
-    public static void initBaseHome()
+    public static void initBaseHome() throws IOException
     {
         File homeDir = MavenTestingUtils.getProjectDir("../spdy-http-server/src/main/config");
         File baseDir = MavenTestingUtils.getTargetTestingDir(NPNModuleTest.class.getName());
         FS.ensureEmpty(baseDir);
-        basehome = new BaseHome(homeDir,baseDir);
+        
+        String cmdLine[] = { "jetty.home="+homeDir.getAbsolutePath(),"jetty.base="+baseDir.getAbsolutePath() };
+        basehome = new BaseHome(cmdLine);
     }
 
     /**
@@ -102,12 +103,12 @@
     @Test
     public void testModuleValues() throws IOException
     {
-        File modFile = basehome.getFile("modules/npn/" + modBootFile);
+        Path modFile = basehome.getPath("modules/protonego-impl/" + modBootFile);
         Module mod = new Module(basehome,modFile);
         assertNotNull("module",mod);
         
         // Validate logical name
-        assertThat("Module name",mod.getName(),is("npn-boot"));
+        assertThat("Module name",mod.getName(),is("protonego-boot"));
 
         List<String> expectedBootClasspath = new ArrayList<>();
 
@@ -121,7 +122,7 @@
             }
         }
 
-        for (String line : mod.getInitialise())
+        for (String line : mod.getJvmArgs())
         {
             expectedBootClasspath.remove(line);
         }
@@ -129,10 +130,10 @@
         if (expectedBootClasspath.size() > 0)
         {
             StringBuilder err = new StringBuilder();
-            err.append("XBootClasspath mismatch between [files] and [ini-template]");
+            err.append("XBootClasspath mismatch between [files] and [exec]");
             err.append("\nThe following are inferred from your [files] definition in ");
-            err.append(modFile.getAbsolutePath());
-            err.append("\nbut are not referenced in your [ini-template] section");
+            err.append(modFile.toAbsolutePath().toString());
+            err.append("\nbut are not referenced in your [exec] section");
             for (String entry : expectedBootClasspath)
             {
                 err.append("\n").append(entry);
diff --git a/jetty-spdy/spdy-server/pom.xml b/jetty-spdy/spdy-server/pom.xml
index 8266726..a67c838 100644
--- a/jetty-spdy/spdy-server/pom.xml
+++ b/jetty-spdy/spdy-server/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.spdy</groupId>
         <artifactId>spdy-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
@@ -29,7 +29,7 @@
                         <configuration>
                             <instructions>
                                 <Export-Package>org.eclipse.jetty.spdy.server;version="9.1"</Export-Package>
-                                <Import-Package>org.eclipse.jetty.npn,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
+                                <Import-Package>org.eclipse.jetty.alpn;resolution:=optional,org.eclipse.jetty.alpn.server;resolution:=optional, org.eclipse.jetty.npn;resolution:=optional,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
                                 <_nouses>true</_nouses>
                             </instructions>
                           </configuration>
@@ -61,6 +61,12 @@
             <version>${npn.api.version}</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.alpn</groupId>
+            <artifactId>alpn-api</artifactId>
+            <version>${alpn.api.version}</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnection.java b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnection.java
index b58a01e..1a9e291 100644
--- a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnection.java
+++ b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnection.java
@@ -24,6 +24,7 @@
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.npn.NextProtoNego;
 import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.NegotiatingServerConnection;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
diff --git a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnectionFactory.java b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnectionFactory.java
index f0e9e01..c69f0fc 100644
--- a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnectionFactory.java
+++ b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/NPNServerConnectionFactory.java
@@ -25,6 +25,7 @@
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.npn.NextProtoNego;
 import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
 import org.eclipse.jetty.util.annotation.Name;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
diff --git a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnector.java b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnector.java
index 35dd6ff..cf73fe9 100644
--- a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnector.java
+++ b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnector.java
@@ -21,6 +21,7 @@
 import java.util.Objects;
 
 import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.spdy.api.SPDY;
diff --git a/jetty-spring/pom.xml b/jetty-spring/pom.xml
index 01e1448..f467399 100644
--- a/jetty-spring/pom.xml
+++ b/jetty-spring/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-spring</artifactId>
diff --git a/jetty-start/pom.xml b/jetty-start/pom.xml
index d0d01b0..9704036 100644
--- a/jetty-start/pom.xml
+++ b/jetty-start/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-start</artifactId>
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java b/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java
index 8fb7842..70ca6be 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java
@@ -19,20 +19,24 @@
 package org.eclipse.jetty.start;
 
 import java.io.File;
-import java.io.FileFilter;
 import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
+import java.nio.file.FileVisitOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
-import org.eclipse.jetty.start.FS.RelativeRegexFilter;
+import org.eclipse.jetty.start.config.CommandLineConfigSource;
+import org.eclipse.jetty.start.config.ConfigSource;
+import org.eclipse.jetty.start.config.ConfigSources;
+import org.eclipse.jetty.start.config.DirConfigSource;
+import org.eclipse.jetty.start.config.JettyBaseConfigSource;
+import org.eclipse.jetty.start.config.JettyHomeConfigSource;
 
 /**
  * File access for <code>${jetty.home}</code>, <code>${jetty.base}</code>, directories.
@@ -45,45 +49,129 @@
  */
 public class BaseHome
 {
-    private File homeDir;
-    private File baseDir;
-
-    public BaseHome()
+    public static class SearchDir
     {
-        try
-        {
-            this.baseDir = new File(System.getProperty("jetty.base",System.getProperty("user.dir",".")));
-            URL jarfile = this.getClass().getClassLoader().getResource("org/eclipse/jetty/start/BaseHome.class");
-            if (jarfile != null)
-            {
-                Matcher m = Pattern.compile("jar:(file:.*)!/org/eclipse/jetty/start/BaseHome.class").matcher(jarfile.toString());
-                if (m.matches())
-                {
-                    homeDir = new File(new URI(m.group(1))).getParentFile();
-                }
-            }
-            homeDir = new File(System.getProperty("jetty.home",(homeDir == null?baseDir:homeDir).getAbsolutePath()));
+        private Path dir;
+        private String name;
 
-            baseDir = baseDir.getAbsoluteFile().getCanonicalFile();
-            homeDir = homeDir.getAbsoluteFile().getCanonicalFile();
-        }
-        catch (IOException | URISyntaxException e)
+        public SearchDir(String name)
         {
-            throw new RuntimeException(e);
+            this.name = name;
+        }
+
+        public Path getDir()
+        {
+            return dir;
+        }
+
+        public Path resolve(Path subpath)
+        {
+            return dir.resolve(subpath);
+        }
+
+        public Path resolve(String subpath)
+        {
+            return dir.resolve(FS.separators(subpath));
+        }
+
+        public SearchDir setDir(File path)
+        {
+            if (path != null)
+            {
+                return setDir(path.toPath());
+            }
+            return this;
+        }
+
+        public SearchDir setDir(Path path)
+        {
+            if (path != null)
+            {
+                this.dir = path.toAbsolutePath();
+            }
+            return this;
+        }
+
+        public SearchDir setDir(String path)
+        {
+            if (path != null)
+            {
+                return setDir(FS.toPath(path));
+            }
+            return this;
+        }
+
+        public String toShortForm(Path path)
+        {
+            Path relative = dir.relativize(path);
+            return String.format("${%s}%c%s",name,File.separatorChar,relative.toString());
         }
     }
 
-    public BaseHome(File homeDir, File baseDir)
+    public static final String JETTY_BASE = "jetty.base";
+    public static final String JETTY_HOME = "jetty.home";
+    private final static EnumSet<FileVisitOption> SEARCH_VISIT_OPTIONS = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
+
+    private final static int MAX_SEARCH_DEPTH = Integer.getInteger("org.eclipse.jetty.start.searchDepth",10);
+
+    private final ConfigSources sources;
+    private final Path homeDir;
+    private final Path baseDir;
+
+    public BaseHome() throws IOException
     {
-        try
+        this(new String[0]);
+    }
+
+    public BaseHome(String cmdLine[]) throws IOException
+    {
+        this(new CommandLineConfigSource(cmdLine));
+    }
+
+    public BaseHome(CommandLineConfigSource cmdLineSource) throws IOException
+    {
+        StartLog.getInstance().initialize(this,cmdLineSource);
+
+        sources = new ConfigSources();
+        sources.add(cmdLineSource);
+        this.homeDir = cmdLineSource.getHomePath();
+        this.baseDir = cmdLineSource.getBasePath();
+        sources.add(new JettyBaseConfigSource(cmdLineSource.getBasePath()));
+        sources.add(new JettyHomeConfigSource(cmdLineSource.getHomePath()));
+
+        System.setProperty(JETTY_HOME,homeDir.toAbsolutePath().toString());
+        System.setProperty(JETTY_BASE,baseDir.toAbsolutePath().toString());
+    }
+
+    public BaseHome(ConfigSources sources)
+    {
+        this.sources = sources;
+        Path home = null;
+        Path base = null;
+        for (ConfigSource source : sources)
         {
-            this.homeDir = homeDir.getCanonicalFile();
-            this.baseDir = baseDir == null?this.homeDir:baseDir.getCanonicalFile();
+            if (source instanceof CommandLineConfigSource)
+            {
+                CommandLineConfigSource cmdline = (CommandLineConfigSource)source;
+                home = cmdline.getHomePath();
+                base = cmdline.getBasePath();
+            }
+            else if (source instanceof JettyBaseConfigSource)
+            {
+                base = ((JettyBaseConfigSource)source).getDir();
+            }
+            else if (source instanceof JettyHomeConfigSource)
+            {
+                home = ((JettyHomeConfigSource)source).getDir();
+            }
         }
-        catch (IOException e)
-        {
-            throw new RuntimeException(e);
-        }
+
+        Objects.requireNonNull(home,"jetty.home cannot be null");
+        this.homeDir = home;
+        this.baseDir = (base != null)?base:home;
+
+        System.setProperty(JETTY_HOME,homeDir.toAbsolutePath().toString());
+        System.setProperty(JETTY_BASE,baseDir.toAbsolutePath().toString());
     }
 
     public String getBase()
@@ -92,120 +180,227 @@
         {
             return null;
         }
-        return baseDir.getAbsolutePath();
+        return baseDir.toString();
     }
 
-    public File getBaseDir()
+    public Path getBasePath()
     {
         return baseDir;
     }
 
     /**
-     * Create a file reference to some content in <code>"${jetty.base}"</code>
+     * Create a {@link Path} reference to some content in <code>"${jetty.base}"</code>
      * 
      * @param path
      *            the path to reference
      * @return the file reference
      */
-    public File getBaseFile(String path)
+    public Path getBasePath(String path)
     {
-        return new File(baseDir,FS.separators(path));
+        return baseDir.resolve(path);
     }
 
-    /**
-     * Get a specific file reference.
-     * <p>
-     * File references go through 3 possibly scenarios.
-     * <ol>
-     * <li>If exists relative to <code>${jetty.base}</code>, return that reference</li>
-     * <li>If exists relative to <code>${jetty.home}</code>, return that reference</li>
-     * <li>Otherwise return absolute path reference</li>
-     * </ol>
-     * 
-     * @param path
-     *            the path to get.
-     * @return the file reference.
-     */
-    public File getFile(String path)
+    public ConfigSources getConfigSources()
     {
-        String rpath = FS.separators(path);
-
-        // Relative to Base Directory First
-        if (isBaseDifferent())
-        {
-            File file = new File(baseDir,rpath);
-            if (file.exists())
-            {
-                return file;
-            }
-        }
-
-        // Then relative to Home Directory
-        File file = new File(homeDir,rpath);
-        if (file.exists())
-        {
-            return file;
-        }
-
-        // Finally, as an absolute path
-        return new File(rpath);
+        return this.sources;
     }
 
     public String getHome()
     {
-        return homeDir.getAbsolutePath();
+        return homeDir.toString();
     }
 
-    public File getHomeDir()
+    public Path getHomePath()
     {
         return homeDir;
     }
 
-    public void initialize(StartArgs args)
+    /**
+     * Get a specific path reference.
+     * <p>
+     * Path references are searched based on the config source search order.
+     * <ol>
+     * <li>If provided path is an absolute reference., and exists, return that reference</li>
+     * <li>If exists relative to <code>${jetty.base}</code>, return that reference</li>
+     * <li>If exists relative to and <code>extra-start-dir</code> locations, return that reference</li>
+     * <li>If exists relative to <code>${jetty.home}</code>, return that reference</li>
+     * <li>Return standard {@link Path} reference obtained from {@link java.nio.file.FileSystem#getPath(String, String...)} (no exists check performed)</li>
+     * </ol>
+     * 
+     * @param path
+     *            the path to get.
+     * @return the path reference.
+     */
+    public Path getPath(final String path)
     {
-        Pattern jetty_home = Pattern.compile("(-D)?jetty.home=(.*)");
-        Pattern jetty_base = Pattern.compile("(-D)?jetty.base=(.*)");
+        Path apath = FS.toPath(path);
 
-        File homePath = null;
-        File basePath = null;
-
-        for (String arg : args.getCommandLine())
+        if (apath.isAbsolute())
         {
-            Matcher home_match = jetty_home.matcher(arg);
-            if (home_match.matches())
+            if (FS.exists(apath))
             {
-                homePath = new File(home_match.group(2));
-            }
-            Matcher base_match = jetty_base.matcher(arg);
-            if (base_match.matches())
-            {
-                basePath = new File(base_match.group(2));
+                return apath;
             }
         }
 
-        if (homePath != null)
+        for (ConfigSource source : sources)
         {
-            // logic if home is specified
-            this.homeDir = homePath.getAbsoluteFile();
-            if (basePath == null)
+            if (source instanceof DirConfigSource)
             {
-                this.baseDir = homePath.getAbsoluteFile();
-                args.getProperties().setProperty("jetty.base",this.baseDir.toString(),"<internal-fallback>");
+                DirConfigSource dirsource = (DirConfigSource)source;
+                Path file = dirsource.getDir().resolve(apath);
+                if (FS.exists(file))
+                {
+                    return file;
+                }
             }
-            else
-            {
-                this.baseDir = basePath.getAbsoluteFile();
-            }
-        }
-        else if (basePath != null)
-        {
-            // logic if home is undeclared
-            this.baseDir = basePath.getAbsoluteFile();
         }
 
-        // Update System Properties
-        args.addSystemProperty("jetty.home",this.homeDir.getAbsolutePath());
-        args.addSystemProperty("jetty.base",this.baseDir.getAbsolutePath());
+        // Finally, as an anonymous path
+        return FS.toPath(path);
+    }
+
+    /**
+     * Search specified Path with pattern and return hits
+     * 
+     * @param dir
+     *            the path to a directory to start search from
+     * @param searchDepth
+     *            the number of directories deep to perform the search
+     * @param pattern
+     *            the raw pattern to use for the search (must be relative)
+     * @return the list of Paths found
+     * @throws IOException
+     *             if unable to search the path
+     */
+    public List<Path> getPaths(Path dir, int searchDepth, String pattern) throws IOException
+    {
+        if (PathMatchers.isAbsolute(pattern))
+        {
+            throw new RuntimeException("Pattern cannot be absolute: " + pattern);
+        }
+
+        List<Path> hits = new ArrayList<>();
+        if (FS.isValidDirectory(dir))
+        {
+            PathMatcher matcher = PathMatchers.getMatcher(pattern);
+            PathFinder finder = new PathFinder();
+            finder.setFileMatcher(matcher);
+            finder.setBase(dir);
+            Files.walkFileTree(dir,SEARCH_VISIT_OPTIONS,searchDepth,finder);
+            hits.addAll(finder.getHits());
+            Collections.sort(hits,new NaturalSort.Paths());
+        }
+        return hits;
+    }
+
+    /**
+     * Get a List of {@link Path}s from a provided pattern.
+     * <p>
+     * Resolution Steps:
+     * <ol>
+     * <li>If the pattern starts with "regex:" or "glob:" then a standard {@link PathMatcher} is built using
+     * {@link java.nio.file.FileSystem#getPathMatcher(String)} as a file search.</li>
+     * <li>If pattern starts with a known filesystem root (using information from {@link java.nio.file.FileSystem#getRootDirectories()}) then this is assumed to
+     * be a absolute file system pattern.</li>
+     * <li>All other patterns are treated as relative to BaseHome information:
+     * <ol>
+     * <li>Search ${jetty.home} first</li>
+     * <li>Search ${jetty.base} for overrides</li>
+     * </ol>
+     * </li>
+     * </ol>
+     * <p>
+     * Pattern examples:
+     * <dl>
+     * <dt><code>lib/logging/*.jar</code></dt>
+     * <dd>Relative pattern, not recursive, search <code>${jetty.home}</code> then <code>${jetty.base}</code> for lib/logging/*.jar content</dd>
+     * 
+     * <dt><code>lib/**&#47;*-dev.jar</code></dt>
+     * <dd>Relative pattern, recursive search <code>${jetty.home}</code> then <code>${jetty.base}</code> for files under <code>lib</code> ending in
+     * <code>-dev.jar</code></dd>
+     * </dl>
+     * 
+     * <dt><code>etc/jetty.xml</code></dt>
+     * <dd>Relative pattern, no glob, search for <code>${jetty.home}/etc/jetty.xml</code> then <code>${jetty.base}/etc/jetty.xml</code></dd>
+     * 
+     * <dt><code>glob:/opt/app/common/*-corp.jar</code></dt>
+     * <dd>PathMapper pattern, glob, search <code>/opt/app/common/</code> for <code>*-corp.jar</code></code></dd>
+     * 
+     * </dl>
+     * 
+     * <p>
+     * Notes:
+     * <ul>
+     * <li>FileSystem case sensitivity is implementation specific (eg: linux is case-sensitive, windows is case-insensitive).<br/>
+     * See {@link java.nio.file.FileSystem#getPathMatcher(String)} for more details</li>
+     * <li>Pattern slashes are implementation neutral (use '/' always and you'll be fine)</li>
+     * <li>Recursive searching is limited to 30 levels deep (not configurable)</li>
+     * <li>File System loops are detected and skipped</li>
+     * </ul>
+     * 
+     * @param pattern
+     *            the pattern to search.
+     * @return the collection of paths found
+     * @throws IOException
+     *             if error during search operation
+     */
+    public List<Path> getPaths(String pattern) throws IOException
+    {
+        StartLog.debug("getPaths('%s')",pattern);
+        List<Path> hits = new ArrayList<>();
+
+        if (PathMatchers.isAbsolute(pattern))
+        {
+            // Perform absolute path pattern search
+
+            // The root to start search from
+            Path root = PathMatchers.getSearchRoot(pattern);
+            // The matcher for file hits
+            PathMatcher matcher = PathMatchers.getMatcher(pattern);
+
+            if (FS.isValidDirectory(root))
+            {
+                PathFinder finder = new PathFinder();
+                finder.setIncludeDirsInResults(true);
+                finder.setFileMatcher(matcher);
+                finder.setBase(root);
+                Files.walkFileTree(root,SEARCH_VISIT_OPTIONS,MAX_SEARCH_DEPTH,finder);
+                hits.addAll(finder.getHits());
+            }
+        }
+        else
+        {
+            // Perform relative path pattern search
+            Path relativePath = PathMatchers.getSearchRoot(pattern);
+            PathMatcher matcher = PathMatchers.getMatcher(pattern);
+            PathFinder finder = new PathFinder();
+            finder.setIncludeDirsInResults(true);
+            finder.setFileMatcher(matcher);
+
+            // walk config sources backwards ...
+            ListIterator<ConfigSource> iter = sources.reverseListIterator();
+            while (iter.hasPrevious())
+            {
+                ConfigSource source = iter.previous();
+                if (source instanceof DirConfigSource)
+                {
+                    DirConfigSource dirsource = (DirConfigSource)source;
+                    Path dir = dirsource.getDir();
+                    Path deepDir = dir.resolve(relativePath);
+                    if (FS.isValidDirectory(deepDir))
+                    {
+                        finder.setBase(dir);
+                        Files.walkFileTree(deepDir,SEARCH_VISIT_OPTIONS,MAX_SEARCH_DEPTH,finder);
+                    }
+                }
+            }
+
+            hits.addAll(finder.getHits());
+        }
+
+        Collections.sort(hits,new NaturalSort.Paths());
+        return hits;
     }
 
     public boolean isBaseDifferent()
@@ -214,228 +409,11 @@
     }
 
     /**
-     * Get all of the files that are in a specific relative directory.
-     * <p>
-     * If the same found path exists in both <code>${jetty.base}</code> and <code>${jetty.home}</code>, then the one in <code>${jetty.base}</code> is returned
-     * (it overrides the one in ${jetty.home})
-     * 
-     * @param relPathToDirectory
-     *            the relative path to the directory
-     * @return the list of files found.
+     * Convenience method for <code>toShortForm(file.toPath())</code>
      */
-    public List<File> listFiles(String relPathToDirectory)
+    public String toShortForm(final File path)
     {
-        return listFiles(relPathToDirectory,FS.AllFilter.INSTANCE);
-    }
-
-    /**
-     * Get all of the files that are in a specific relative directory, with applied {@link FileFilter}
-     * <p>
-     * If the same found path exists in both <code>${jetty.base}</code> and <code>${jetty.home}</code>, then the one in <code>${jetty.base}</code> is returned
-     * (it overrides the one in ${jetty.home})
-     * 
-     * @param relPathToDirectory
-     *            the relative path to the directory
-     * @param filter
-     *            the filter to use
-     * @return the list of files found.
-     */
-    public List<File> listFiles(String relPathToDirectory, FileFilter filter)
-    {
-        Objects.requireNonNull(filter,"FileFilter cannot be null");
-
-        File homePath = new File(homeDir,FS.separators(relPathToDirectory));
-        List<File> homeFiles = new ArrayList<>();
-        if (FS.canReadDirectory(homePath))
-        {
-            homeFiles.addAll(Arrays.asList(homePath.listFiles(filter)));
-        }
-
-        if (isBaseDifferent())
-        {
-            // merge
-            File basePath = new File(baseDir,FS.separators(relPathToDirectory));
-            List<File> ret = new ArrayList<>();
-            if (FS.canReadDirectory(basePath))
-            {
-                File baseFiles[] = basePath.listFiles(filter);
-
-                if (baseFiles != null)
-                {
-                    for (File base : baseFiles)
-                    {
-                        String relpath = toRelativePath(baseDir,base);
-                        File home = new File(homeDir,FS.separators(relpath));
-                        if (home.exists())
-                        {
-                            homeFiles.remove(home);
-                        }
-                        ret.add(base);
-                    }
-                }
-            }
-
-            // add any remaining home files.
-            ret.addAll(homeFiles);
-
-            Collections.sort(ret,new NaturalSort.Files());
-            return ret;
-        }
-        else
-        {
-            // simple return
-            Collections.sort(homeFiles,new NaturalSort.Files());
-            return homeFiles;
-        }
-    }
-    
-    /**
-     * Get all of the files that are in a specific relative directory, with applied regex.
-     * <p>
-     * If the same found path exists in both <code>${jetty.base}</code> and <code>${jetty.home}</code>, then the one in <code>${jetty.base}</code> is returned
-     * (it overrides the one in ${jetty.home})
-     * <p>
-     * All regex paths are assumed to be in unix notation (use of <code>"/"</code> to separate paths, as <code>"\"</code> is used to escape in regex)
-     * 
-     * @param regex
-     *            the regex to use to match against the found files.
-     * @return the list of files found.
-     */
-    public List<File> listFilesRegex(String regex)
-    {
-        Objects.requireNonNull(regex,"Glob cannot be null");
-
-        Pattern pattern = Pattern.compile(regex);
-
-        List<File> homeFiles = new ArrayList<>();
-        if (FS.canReadDirectory(homeDir))
-        {
-            StartLog.debug("Finding files in ${jetty.home} that match: %s",regex);
-            recurseDir(homeFiles,homeDir,new FS.RelativeRegexFilter(homeDir,pattern));
-            StartLog.debug("Found %,d files",homeFiles.size());
-        }
-
-        if (isBaseDifferent())
-        {
-            // merge
-            List<File> ret = new ArrayList<>();
-            if (FS.canReadDirectory(baseDir))
-            {
-                List<File> baseFiles = new ArrayList<>();
-                StartLog.debug("Finding files in ${jetty.base} that match: %s",regex);
-                recurseDir(baseFiles,baseDir,new FS.RelativeRegexFilter(baseDir,pattern));
-                StartLog.debug("Found %,d files",baseFiles.size());
-
-                for (File base : baseFiles)
-                {
-                    String relpath = toRelativePath(baseDir,base);
-                    File home = new File(homeDir,FS.separators(relpath));
-                    if (home.exists())
-                    {
-                        homeFiles.remove(home);
-                    }
-                    ret.add(base);
-                }
-            }
-
-            // add any remaining home files.
-            ret.addAll(homeFiles);
-            StartLog.debug("Merged Files: %,d files%n",ret.size());
-
-            Collections.sort(ret,new NaturalSort.Files());
-            return ret;
-        }
-        else
-        {
-            // simple return
-            Collections.sort(homeFiles,new NaturalSort.Files());
-            return homeFiles;
-        }
-    }
-
-    private void recurseDir(List<File> files, File dir, RelativeRegexFilter filter)
-    {
-        // find matches first
-        files.addAll(Arrays.asList(dir.listFiles(filter)));
-
-        // now dive down into sub-directories
-        for (File subdir : dir.listFiles(FS.DirFilter.INSTANCE))
-        {
-            recurseDir(files,subdir,filter);
-        }
-    }
-
-    /**
-     * Collect the list of files in both <code>${jetty.base}</code> and <code>${jetty.home}</code>, even if the same file shows up in both places.
-     */
-    public List<File> rawListFiles(String relPathToDirectory, FileFilter filter)
-    {
-        Objects.requireNonNull(filter,"FileFilter cannot be null");
-
-        List<File> ret = new ArrayList<>();
-
-        // Home Dir
-        File homePath = new File(homeDir,FS.separators(relPathToDirectory));
-        ret.addAll(Arrays.asList(homePath.listFiles(filter)));
-
-        if (isBaseDifferent())
-        {
-            // Base Dir
-            File basePath = new File(baseDir,FS.separators(relPathToDirectory));
-            ret.addAll(Arrays.asList(basePath.listFiles(filter)));
-        }
-
-        // Sort
-        Collections.sort(ret,new NaturalSort.Files());
-        return ret;
-    }
-
-    public void setBaseDir(File dir)
-    {
-        try
-        {
-            this.baseDir = dir.getCanonicalFile();
-            System.setProperty("jetty.base",dir.getCanonicalPath());
-        }
-        catch (IOException e)
-        {
-            e.printStackTrace(System.err);
-        }
-    }
-
-    public void setHomeDir(File dir)
-    {
-        try
-        {
-            this.homeDir = dir.getCanonicalFile();
-            System.setProperty("jetty.home",dir.getCanonicalPath());
-        }
-        catch (IOException e)
-        {
-            e.printStackTrace(System.err);
-        }
-    }
-
-    // TODO - inline
-    private String toRelativePath(File dir, File path)
-    {
-        return FS.toRelativePath(dir,path);
-    }
-
-    /**
-     * Convenience method for <code>toShortForm(file.getCanonicalPath())</code>
-     */
-    public String toShortForm(File path)
-    {
-        try
-        {
-            return toShortForm(path.getCanonicalPath());
-        }
-        catch (IOException ignore)
-        {
-            /* ignore */
-        }
-        return toShortForm(path.getAbsolutePath());
+        return toShortForm(path.toPath());
     }
 
     /**
@@ -445,32 +423,48 @@
      *            the path to shorten
      * @return the potentially shortened path
      */
-    public String toShortForm(String path)
+    public String toShortForm(final Path path)
+    {
+        Path apath = path.toAbsolutePath();
+
+        for (ConfigSource source : sources)
+        {
+            if (source instanceof DirConfigSource)
+            {
+                DirConfigSource dirsource = (DirConfigSource)source;
+                Path dir = dirsource.getDir();
+                if (apath.startsWith(dir))
+                {
+                    if (dirsource.isPropertyBased())
+                    {
+                        Path relative = dir.relativize(apath);
+                        return String.format("%s%c%s",dirsource.getId(),File.separatorChar,relative.toString());
+                    }
+                    else
+                    {
+                        return apath.toString();
+                    }
+                }
+            }
+        }
+
+        return apath.toString();
+    }
+
+    /**
+     * Replace/Shorten arbitrary path with property strings <code>"${jetty.home}"</code> or <code>"${jetty.base}"</code> where appropriate.
+     * 
+     * @param path
+     *            the path to shorten
+     * @return the potentially shortened path
+     */
+    public String toShortForm(final String path)
     {
         if (path == null)
         {
             return path;
         }
 
-        String value;
-
-        if (isBaseDifferent())
-        {
-            value = baseDir.getAbsolutePath();
-            if (path.startsWith(value))
-            {
-                return "${jetty.base}" + path.substring(value.length());
-            }
-        }
-
-        value = homeDir.getAbsolutePath();
-
-        if (path.startsWith(value))
-        {
-            return "${jetty.home}" + path.substring(value.length());
-        }
-
-        return path;
+        return toShortForm(FS.toPath(path));
     }
-
 }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java b/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
index ccf127e..8f4fd8c 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/CommandLineBuilder.java
@@ -192,4 +192,19 @@
 
         return buf.toString();
     }
+
+    public void debug()
+    {
+        if (!StartLog.isDebugEnabled())
+        {
+            return;
+        }
+
+        int len = args.size();
+        StartLog.debug("Command Line: %,d entries",args.size());
+        for (int i = 0; i < len; i++)
+        {
+            StartLog.debug(" [%d]: \"%s\"",i,args.get(i));
+        }
+    }
 }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java b/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java
index 5fd41b9..9b55b45 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java
@@ -20,124 +20,28 @@
 
 import java.io.Closeable;
 import java.io.File;
-import java.io.FileFilter;
 import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
 import java.util.Locale;
-import java.util.regex.Pattern;
 
 public class FS
 {
-    public static class AllFilter implements FileFilter
+    public static boolean canReadDirectory(Path path)
     {
-        public static final AllFilter INSTANCE = new AllFilter();
-
-        @Override
-        public boolean accept(File pathname)
-        {
-            return true;
-        }
-    }
-    
-    public static class DirFilter implements FileFilter
-    {
-        public static final DirFilter INSTANCE = new DirFilter();
-
-        @Override
-        public boolean accept(File path)
-        {
-            return path.isDirectory();
-        }
-    }
-    
-    public static class RelativeRegexFilter implements FileFilter
-    {
-        private final File baseDir;
-        private final Pattern pattern;
-
-        public RelativeRegexFilter(File baseDir, Pattern pattern)
-        {
-            this.baseDir = baseDir;
-            this.pattern = pattern;
-        }
-
-        @Override
-        public boolean accept(File path)
-        {
-            // get relative path
-            String relativePath = FS.toRelativePath(baseDir,path);
-            
-            // see if it matches
-            return (pattern.matcher(relativePath).matches());
-        }
+        return Files.exists(path) && Files.isDirectory(path) && Files.isReadable(path);
     }
 
-    public static class FilenameRegexFilter implements FileFilter
+    public static boolean canReadFile(Path path)
     {
-        private final Pattern pattern;
-
-        public FilenameRegexFilter(String regex)
-        {
-            pattern = Pattern.compile(regex,Pattern.CASE_INSENSITIVE);
-        }
-
-        @Override
-        public boolean accept(File path)
-        {
-            return path.isFile() && pattern.matcher(path.getName()).matches();
-        }
+        return Files.exists(path) && Files.isRegularFile(path) && Files.isReadable(path);
     }
 
-    public static class FileNamesFilter implements FileFilter
+    public static boolean canWrite(Path path)
     {
-        private final String filenames[];
-
-        public FileNamesFilter(String... names)
-        {
-            this.filenames = names;
-        }
-
-        @Override
-        public boolean accept(File path)
-        {
-            if (!path.isFile())
-            {
-                return false;
-            }
-            for (String name : filenames)
-            {
-                if (name.equalsIgnoreCase(path.getName()))
-                {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-
-    public static class IniFilter extends FilenameRegexFilter
-    {
-        public IniFilter()
-        {
-            super("^.*\\.ini$");
-        }
-    }
-
-    public static class XmlFilter extends FilenameRegexFilter
-    {
-        public XmlFilter()
-        {
-            super("^.*\\.xml$");
-        }
-    }
-
-    public static boolean canReadDirectory(File path)
-    {
-        return (path.exists() && path.isDirectory() && path.canRead());
-    }
-
-    public static boolean canReadFile(File path)
-    {
-        return (path.exists() && path.isFile() && path.canRead());
+        return Files.isWritable(path);
     }
 
     public static void close(Closeable c)
@@ -157,48 +61,65 @@
         }
     }
 
-    public static void ensureDirectoryExists(File dir) throws IOException
+    public static boolean createNewFile(Path path) throws IOException
     {
-        if (dir.exists())
+        Path ret = Files.createFile(path);
+        return Files.exists(ret);
+    }
+
+    public static void ensureDirectoryExists(Path dir) throws IOException
+    {
+        if (exists(dir))
         {
+            // exists already, nothing to do
             return;
         }
-        if (!dir.mkdirs())
-        {
-            throw new IOException("Unable to create directory: " + dir.getAbsolutePath());
-        }
+        Files.createDirectories(dir);
     }
-    
-    public static void ensureDirectoryWritable(File dir) throws IOException
+
+    public static void ensureDirectoryWritable(Path dir) throws IOException
     {
-        if (!dir.exists())
+        if (!Files.exists(dir))
         {
-            throw new IOException("Directory does not exist: " + dir.getAbsolutePath());
+            throw new IOException("Path does not exist: " + dir.toAbsolutePath());
         }
-        if (!dir.canWrite())
+        if (!Files.isDirectory(dir))
         {
-            throw new IOException("Unable to write to directory: " + dir.getAbsolutePath());
+            throw new IOException("Directory does not exist: " + dir.toAbsolutePath());
+        }
+        if (!Files.isWritable(dir))
+        {
+            throw new IOException("Unable to write to directory: " + dir.toAbsolutePath());
         }
     }
 
-    public static boolean isFile(File file)
+    public static boolean exists(Path path)
     {
-        if (file == null)
+        return Files.exists(path);
+    }
+
+    public static boolean isValidDirectory(Path path)
+    {
+        if (!Files.exists(path))
         {
+            // doesn't exist, not a valid directory
             return false;
         }
-        return file.exists() && file.isFile();
+
+        if (!Files.isDirectory(path))
+        {
+            // not a directory (as expected)
+            StartLog.warn("Not a directory: " + path);
+            return false;
+        }
+
+        return true;
     }
 
     public static boolean isXml(String filename)
     {
         return filename.toLowerCase(Locale.ENGLISH).endsWith(".xml");
     }
-    
-    public static String toRelativePath(File baseDir, File path)
-    {
-        return baseDir.toURI().relativize(path.toURI()).toASCIIString();
-    }
 
     public static String separators(String path)
     {
@@ -216,4 +137,20 @@
         }
         return ret.toString();
     }
+
+    public static Path toPath(String path)
+    {
+        return FileSystems.getDefault().getPath(FS.separators(path));
+    }
+
+    public static void touch(Path path) throws IOException
+    {
+        FileTime now = FileTime.fromMillis(System.currentTimeMillis());
+        Files.setLastModifiedTime(path,now);
+    }
+
+    public static Path toRealPath(Path path) throws IOException
+    {
+        return path.toRealPath();
+    }
 }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
index c196aff..9e46100 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
@@ -18,14 +18,11 @@
 
 package org.eclipse.jetty.start;
 
-import static org.eclipse.jetty.start.UsageException.ERR_INVOKE_MAIN;
-import static org.eclipse.jetty.start.UsageException.ERR_NOT_STOPPED;
-import static org.eclipse.jetty.start.UsageException.ERR_UNKNOWN;
+import static org.eclipse.jetty.start.UsageException.*;
 
 import java.io.BufferedReader;
+import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -39,14 +36,22 @@
 import java.net.Socket;
 import java.net.SocketTimeoutException;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.eclipse.jetty.start.config.CommandLineConfigSource;
+
 /**
  * Main start class.
  * <p>
@@ -130,11 +135,10 @@
         System.exit(exit);
     }
 
-    private final BaseHome baseHome;
+    private BaseHome baseHome;
 
     Main() throws IOException
     {
-        baseHome = new BaseHome();
     }
 
     private void copyInThread(final InputStream in, final OutputStream out)
@@ -167,11 +171,12 @@
     {
         try
         {
-            File file = baseHome.getBaseFile(arg.location);
+            Path file = baseHome.getBasePath(arg.location);
 
-            StartLog.debug("Module file %s %s",file.getAbsolutePath(),(file.exists()?"[Exists!]":""));
-            if (file.exists())
+            StartLog.debug("Module file %s %s",file.toAbsolutePath(),(FS.exists(file)?"[Exists!]":""));
+            if (FS.exists(file))
             {
+                // file already initialized / downloaded, skip it
                 return;
             }
 
@@ -181,10 +186,11 @@
 
                 System.err.println("DOWNLOAD: " + url + " to " + arg.location);
 
-                FS.ensureDirectoryExists(file.getParentFile());
+                FS.ensureDirectoryExists(file.getParent());
 
                 byte[] buf = new byte[8192];
-                try (InputStream in = url.openStream(); OutputStream out = new FileOutputStream(file);)
+                try (InputStream in = url.openStream(); 
+                     OutputStream out = Files.newOutputStream(file,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE))
                 {
                     while (true)
                     {
@@ -204,11 +210,12 @@
             else if (arg.location.endsWith("/"))
             {
                 System.err.println("MKDIR: " + baseHome.toShortForm(file));
-                file.mkdirs();
+                FS.ensureDirectoryExists(file);
             }
             else
+            {
                 StartLog.warn("MISSING: required file "+ baseHome.toShortForm(file));
-            
+            }
         }
         catch (Exception e)
         {
@@ -300,7 +307,7 @@
     public void listConfig(StartArgs args)
     {
         // Dump Jetty Home / Base
-        args.dumpEnvironment();
+        args.dumpEnvironment(baseHome);
 
         // Dump JVM Args
         args.dumpJvmArgs();
@@ -333,10 +340,23 @@
         modules.dumpEnabledTree();
     }
 
-    private void moduleIni(StartArgs args, String name, boolean topLevel, boolean appendStartIni) throws IOException
-    {
+    /**
+     * Build out INI file.
+     * <p>
+     * This applies equally for either <code>${jetty.base}/start.ini</code> or
+     * <code>${jetty.base}/start.d/${name}.ini</code> 
+     * 
+     * @param args the arguments of what modules are enabled
+     * @param name the name of the module to based the build of the ini
+     * @param topLevel 
+     * @param appendStartIni true to append to <code>${jetty.base}/start.ini</code>, 
+     * false to create a <code>${jetty.base}/start.d/${name}.ini</code> entry instead.
+     * @throws IOException
+     */
+    private void buildIni(StartArgs args, String name, boolean topLevel, boolean appendStartIni) throws IOException
+    {        
         // Find the start.d relative to the base directory only.
-        File start_d = baseHome.getBaseFile("start.d");
+        Path start_d = baseHome.getBasePath("start.d");
 
         // Is this a module?
         Modules modules = args.getAllModules();
@@ -348,12 +368,12 @@
         }
 
         // Find any named ini file and check it follows the convention
-        File start_ini = baseHome.getBaseFile("start.ini");
+        Path start_ini = baseHome.getBasePath("start.ini");
         String short_start_ini = baseHome.toShortForm(start_ini);
-        File ini = new File(start_d,name + ".ini");
+        Path ini = start_d.resolve(name + ".ini");
         String short_ini = baseHome.toShortForm(ini);
         StartIni module_ini = null;
-        if (ini.exists())
+        if (FS.exists(ini))
         {
             module_ini = new StartIni(ini);
             if (module_ini.getLineMatches(Pattern.compile("--module=(.*, *)*" + name)).size() == 0)
@@ -367,46 +387,30 @@
         boolean has_ini_lines = module.getInitialise().size() > 0;
 
         // If it is not enabled or is transitive with ini template lines or toplevel and doesn't exist
-        if (!module.isEnabled() || (transitive && has_ini_lines) || (topLevel && !ini.exists() && !appendStartIni))
+        if (!module.isEnabled() || (transitive && has_ini_lines) || (topLevel && !FS.exists(ini) && !appendStartIni))
         {
+            // File BufferedWriter
+            BufferedWriter writer = null;
             String source = null;
             PrintWriter out = null;
             try
             {
                 if (appendStartIni)
                 {
-                    if ((!start_ini.exists() && !start_ini.createNewFile()) || !start_ini.canWrite())
-                    {
-                        StartLog.warn("ERROR: Bad %s! ",start_ini);
-                        return;
-                    }
                     source = short_start_ini;
                     StartLog.info("%-15s initialised in %s (appended)",name,source);
-                    out = new PrintWriter(new FileWriter(start_ini,true));
+                    writer = Files.newBufferedWriter(start_ini,StandardCharsets.UTF_8,StandardOpenOption.CREATE,StandardOpenOption.APPEND);
+                    out = new PrintWriter(writer);
                 }
                 else
                 {
                     // Create the directory if needed
                     FS.ensureDirectoryExists(start_d);
                     FS.ensureDirectoryWritable(start_d);
-                    try
-                    {
-                        // Create a new ini file for it
-                        if (!ini.createNewFile())
-                        {
-                            StartLog.warn("ERROR: %s cannot be initialised in %s! ",name,short_ini);
-                            return;
-                        }
-                    }
-                    catch (IOException e)
-                    {
-                        StartLog.warn("ERROR: Unable to create %s!",ini);
-                        StartLog.warn(e);
-                        return;
-                    }
                     source = short_ini;
                     StartLog.info("%-15s initialised in %s (created)",name,source);
-                    out = new PrintWriter(ini);
+                    writer = Files.newBufferedWriter(ini,StandardCharsets.UTF_8,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE);
+                    out = new PrintWriter(writer);
                 }
 
                 if (appendStartIni)
@@ -454,11 +458,15 @@
                 }
             }
         }
-        else if (ini.exists())
+        else if (FS.exists(ini))
         {
             StartLog.info("%-15s initialised in %s",name,short_ini);
         }
-
+        else
+        {
+            StartLog.info("%-15s initialised transitively",name);
+        }
+        
         // Also list other places this module is enabled
         for (String source : module.getSources())
         {
@@ -474,23 +482,57 @@
             initFile(new FileArg(file));
         }
 
-        // Process dependencies from top level only
+        // Process dependencies
+        module.expandProperties(args.getProperties());
+        modules.registerParentsIfMissing(baseHome,args,module);
+        modules.buildGraph();
+        
+        
+        // process new ini modules
         if (topLevel)
         {
-            List<Module> parents = new ArrayList<>();
-            for (String parent : modules.resolveParentModulesOf(name))
+            List<Module> depends = new ArrayList<>();
+            for (String depend : modules.resolveParentModulesOf(name))
             {
-                if (!name.equals(parent))
+                if (!name.equals(depend))
                 {
-                    Module m = modules.get(parent);
+                    Module m = modules.get(depend);
                     m.setEnabled(true);
-                    parents.add(m);
+                    depends.add(m);
                 }
             }
-            Collections.sort(parents,Collections.reverseOrder(new Module.DepthComparator()));
-            for (Module m : parents)
+            Collections.sort(depends,Collections.reverseOrder(new Module.DepthComparator()));
+            
+            Set<String> done = new HashSet<>(0);
+            while (true)
             {
-                moduleIni(args,m.getName(),false,appendStartIni);
+                // initialize known dependencies
+                boolean complete=true;
+                for (Module m : depends)
+                {
+                    if (!done.contains(m.getName()))
+                    {
+                        complete=false;
+                        buildIni(args,m.getName(),false,appendStartIni);
+                        done.add(m.getName());
+                    }
+                }
+                
+                if (complete)
+                    break;
+                
+                // look for any new ones resolved via expansion
+                depends.clear();
+                for (String depend : modules.resolveParentModulesOf(name))
+                {
+                    if (!name.equals(depend))
+                    {
+                        Module m = modules.get(depend);
+                        m.setEnabled(true);
+                        depends.add(m);
+                    }
+                }
+                Collections.sort(depends,Collections.reverseOrder(new Module.DepthComparator()));
             }
         }
     }
@@ -505,66 +547,36 @@
 
     public StartArgs processCommandLine(String[] cmdLine) throws Exception
     {
-        StartArgs args = new StartArgs(cmdLine);
-
         // Processing Order is important!
         // ------------------------------------------------------------
-        // 1) Directory Locations
-
-        // Set Home and Base at the start, as all other paths encountered
-        // will be based off of them.
-        baseHome.initialize(args);
-
-        // ------------------------------------------------------------
-        // 2) Start Logging
-        StartLog.getInstance().initialize(baseHome,args);
+        // 1) Configuration Locations
+        CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
+        baseHome = new BaseHome(cmdLineSource);
 
         StartLog.debug("jetty.home=%s",baseHome.getHome());
         StartLog.debug("jetty.base=%s",baseHome.getBase());
 
         // ------------------------------------------------------------
-        // 3) Load Inis
-        File start_ini = baseHome.getBaseFile("start.ini");
-        if (FS.canReadFile(start_ini))
-        {
-            StartLog.debug("Reading ${jetty.base}/start.ini - %s",start_ini);
-            args.parse(baseHome,new StartIni(start_ini));
-        }
-
-        File start_d = baseHome.getBaseFile("start.d");
-        if (FS.canReadDirectory(start_d))
-        {
-            List<File> files = new ArrayList<>();
-            for (File file : start_d.listFiles(new FS.IniFilter()))
-            {
-                files.add(file);
-            }
-
-            Collections.sort(files,new NaturalSort.Files());
-            for (File file : files)
-            {
-                StartLog.debug("Reading ${jetty.base}/start.d/%s - %s",file.getName(),file);
-                args.parse(baseHome,new StartIni(file));
-            }
-        }
-
-        // 4) Parse everything provided.
+        // 2) Parse everything provided.
         // This would be the directory information +
         // the various start inis
         // and then the raw command line arguments
         StartLog.debug("Parsing collected arguments");
-        args.parseCommandLine();
+        StartArgs args = new StartArgs();
+        args.parse(baseHome.getConfigSources());
 
-        // 5) Module Registration
+        // ------------------------------------------------------------
+        // 3) Module Registration
         Modules modules = new Modules();
         StartLog.debug("Registering all modules");
         modules.registerAll(baseHome, args);
 
-        // 6) Active Module Resolution
+        // ------------------------------------------------------------
+        // 4) Active Module Resolution
         for (String enabledModule : args.getEnabledModules())
         {
-            List<String> sources = args.getSources(enabledModule);
-            modules.enable(enabledModule,sources);
+            List<String> msources = args.getSources(enabledModule);
+            modules.enable(enabledModule,msources);
         }
 
         StartLog.debug("Building Module Graph");
@@ -573,10 +585,13 @@
         args.setAllModules(modules);
         List<Module> activeModules = modules.resolveEnabled();
         
-        // 7) Lib & XML Expansion / Resolution
+        // ------------------------------------------------------------
+        // 5) Lib & XML Expansion / Resolution
+        args.expandLibs(baseHome);
         args.expandModules(baseHome,activeModules);
 
-        // 8) Resolve Extra XMLs
+        // ------------------------------------------------------------
+        // 6) Resolve Extra XMLs
         args.resolveExtraXmls(baseHome);
 
         return args;
@@ -618,7 +633,7 @@
         // Generate Module Graph File
         if (args.getModuleGraphFilename() != null)
         {
-            File outputFile = baseHome.getBaseFile(args.getModuleGraphFilename());
+            Path outputFile = baseHome.getBasePath(args.getModuleGraphFilename());
             System.out.printf("Generating GraphViz Graph of Jetty Modules at %s%n",baseHome.toShortForm(outputFile));
             ModuleGraphWriter writer = new ModuleGraphWriter();
             writer.config(args.getProperties());
@@ -649,26 +664,28 @@
             }
         }
 
-        // Initialize
-        for (String module : args.getModuleStartIni())
+        // Initialize start.ini
+        for (String module : args.getAddToStartIni())
         {
-            moduleIni(args,module,true,true);
+            buildIni(args,module,true,true);
         }
 
-        // Initialize
-        for (String module : args.getModuleStartdIni())
+        // Initialize start.d
+        for (String module : args.getAddToStartdIni())
         {
-            moduleIni(args,module,true,false);
+            buildIni(args,module,true,false);
         }
 
         // Check ini files for download possibilities
         for (FileArg arg : args.getFiles())
         {
-            File file = baseHome.getBaseFile(arg.location);
-            if (!file.exists() && args.isDownload())
+            Path file = baseHome.getBasePath(arg.location);
+            if (!FS.exists(file) && args.isDownload())
+            {
                 initFile(arg);
+            }
 
-            if (!file.exists())
+            if (!FS.exists(file))
             {
                 /* Startup should NEVER fail to run on missing content.
                  * See Bug #427204
@@ -692,6 +709,7 @@
         if (args.isExec())
         {
             CommandLineBuilder cmd = args.getMainArgs(baseHome,true);
+            cmd.debug();
             ProcessBuilder pbuilder = new ProcessBuilder(cmd.getArgs());
             final Process process = pbuilder.start();
             Runtime.getRuntime().addShutdownHook(new Thread()
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java
index 892f11a..2df1b05 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java
@@ -19,10 +19,11 @@
 package org.eclipse.jetty.start;
 
 import java.io.BufferedReader;
-import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.text.CollationKey;
 import java.text.Collator;
 import java.util.ArrayList;
@@ -75,7 +76,7 @@
     }
 
     /** The file of the module */
-    private File file;
+    private Path file;
     /** The name of this Module (as a filesystem reference) */
     private String fileRef;
     /**
@@ -100,18 +101,20 @@
     private List<String> libs;
     /** List of files for this Module */
     private List<String> files;
+    /** List of jvm Args */
+    private List<String> jvmArgs;
 
     /** Is this Module enabled via start.jar command line, start.ini, or start.d/*.ini ? */
     private boolean enabled = false;
     /** List of sources that enabled this module */
     private final Set<String> sources = new HashSet<>();
 
-    public Module(BaseHome basehome, File file) throws FileNotFoundException, IOException
+    public Module(BaseHome basehome, Path file) throws FileNotFoundException, IOException
     {
         this.file = file;
 
         // Strip .mod
-        this.fileRef = Pattern.compile(".mod$",Pattern.CASE_INSENSITIVE).matcher(file.getName()).replaceFirst("");
+        this.fileRef = Pattern.compile(".mod$",Pattern.CASE_INSENSITIVE).matcher(file.getFileName().toString()).replaceFirst("");
         this.logicalName = fileRef;
 
         init(basehome);
@@ -250,6 +253,11 @@
         return xmls;
     }
 
+    public List<String> getJvmArgs()
+    {
+        return jvmArgs;
+    }
+
     @Override
     public int hashCode()
     {
@@ -261,6 +269,16 @@
 
     private void init(BaseHome basehome)
     {
+        parentNames = new HashSet<>();
+        optionalParentNames = new HashSet<>();
+        parentEdges = new HashSet<>();
+        childEdges = new HashSet<>();
+        xmls = new ArrayList<>();
+        initialise = new ArrayList<>();
+        libs = new ArrayList<>();
+        files = new ArrayList<>();
+        jvmArgs = new ArrayList<>();
+
         String name = basehome.toShortForm(file);
 
         // Find module system name (usually in the form of a filesystem reference)
@@ -272,15 +290,6 @@
         }
         this.fileRef = mat.group(1).replace('\\','/');
         this.logicalName = this.fileRef;
-
-        parentNames = new HashSet<>();
-        optionalParentNames = new HashSet<>();
-        parentEdges = new HashSet<>();
-        childEdges = new HashSet<>();
-        xmls = new ArrayList<>();
-        initialise = new ArrayList<>();
-        libs = new ArrayList<>();
-        files = new ArrayList<>();
     }
 
     public boolean isEnabled()
@@ -298,63 +307,63 @@
             return;
         }
 
-        try (FileReader reader = new FileReader(file))
+        try (BufferedReader buf = Files.newBufferedReader(file,StandardCharsets.UTF_8))
         {
-            try (BufferedReader buf = new BufferedReader(reader))
+            String sectionType = "";
+            String line;
+            while ((line = buf.readLine()) != null)
             {
-                String sectionType = "";
-                String line;
-                while ((line = buf.readLine()) != null)
+                line = line.trim();
+
+                Matcher sectionMatcher = section.matcher(line);
+
+                if (sectionMatcher.matches())
                 {
-                    line = line.trim();
-
-                    Matcher sectionMatcher = section.matcher(line);
-
-                    if (sectionMatcher.matches())
+                    sectionType = sectionMatcher.group(1).trim().toUpperCase(Locale.ENGLISH);
+                }
+                else
+                {
+                    // blank lines and comments are valid for ini-template section
+                    if ((line.length() == 0) || line.startsWith("#"))
                     {
-                        sectionType = sectionMatcher.group(1).trim().toUpperCase(Locale.ENGLISH);
+                        if ("INI-TEMPLATE".equals(sectionType))
+                        {
+                            initialise.add(line);
+                        }
                     }
                     else
                     {
-                        // blank lines and comments are valid for ini-template section
-                        if ((line.length() == 0) || line.startsWith("#"))
+                        switch (sectionType)
                         {
-                            if ("INI-TEMPLATE".equals(sectionType))
-                            {
+                            case "":
+                                // ignore (this would be entries before first section)
+                                break;
+                            case "DEPEND":
+                                parentNames.add(line);
+                                break;
+                            case "FILES":
+                                files.add(line);
+                                break;
+                            case "INI-TEMPLATE":
                                 initialise.add(line);
-                            }
-                        }
-                        else
-                        {
-                            switch (sectionType)
-                            {
-                                case "":
-                                    // ignore (this would be entries before first section)
-                                    break;
-                                case "NAME":
-                                    logicalName = line;
-                                    break;
-                                case "DEPEND":
-                                    parentNames.add(line);
-                                    break;
-                                case "LIB":
-                                    libs.add(line);
-                                    break;
-                                case "XML":
-                                    xmls.add(line);
-                                    break;
-                                case "OPTIONAL":
-                                    optionalParentNames.add(line);
-                                    break;
-                                case "FILES":
-                                    files.add(line);
-                                    break;
-                                case "INI-TEMPLATE":
-                                    initialise.add(line);
-                                    break;
-                                default:
-                                    throw new IOException("Unrecognized Module section: [" + sectionType + "]");
-                            }
+                                break;
+                            case "LIB":
+                                libs.add(line);
+                                break;
+                            case "NAME":
+                                logicalName = line;
+                                break;
+                            case "OPTIONAL":
+                                optionalParentNames.add(line);
+                                break;
+                            case "EXEC":
+                                jvmArgs.add(line);
+                                break;
+                            case "XML":
+                                xmls.add(line);
+                                break;
+                            default:
+                                throw new IOException("Unrecognized Module section: [" + sectionType + "]");
                         }
                     }
                 }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java b/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java
index b078f51..7c612a7 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java
@@ -18,10 +18,13 @@
 
 package org.eclipse.jetty.start;
 
-import java.io.File;
-import java.io.FileWriter;
+import java.io.BufferedWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
 import java.util.Collection;
 import java.util.List;
 
@@ -73,9 +76,10 @@
         return val;
     }
 
-    public void write(Modules modules, File outputFile) throws IOException
+    public void write(Modules modules, Path outputFile) throws IOException
     {
-        try (FileWriter writer = new FileWriter(outputFile,false); PrintWriter out = new PrintWriter(writer);)
+        try (BufferedWriter writer = Files.newBufferedWriter(outputFile,StandardCharsets.UTF_8,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE); 
+             PrintWriter out = new PrintWriter(writer);)
         {
             writeHeaderMessage(out,outputFile);
 
@@ -112,7 +116,7 @@
         }
     }
 
-    private void writeHeaderMessage(PrintWriter out, File outputFile)
+    private void writeHeaderMessage(PrintWriter out, Path outputFile)
     {
         out.println("/*");
         out.println(" * GraphViz Graph of Jetty Modules");
@@ -121,7 +125,7 @@
         out.println(" * GraphViz: http://graphviz.org/");
         out.println(" * ");
         out.println(" * To Generate Graph image using graphviz:");
-        String filename = outputFile.getName();
+        String filename = outputFile.getFileName().toString();
         String basename = filename.substring(0,filename.indexOf('.'));
         out.printf(" *   $ dot -Tpng -Goverlap=false -o %s.png %s%n",basename,filename);
         out.println(" */");
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java
index 95bd807..61330ac 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java
@@ -18,9 +18,9 @@
 
 package org.eclipse.jetty.start;
 
-import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -45,7 +45,7 @@
      * ex: modules/npn/npn-1.7.0_01.mod (property expansion resolves to non-existent file)
      */
     private Set<String> missingModules = new HashSet<String>();
-    
+
     private int maxDepth = -1;
 
     private Set<String> asNameSet(Set<Module> moduleSet)
@@ -117,7 +117,10 @@
 
                 if (parent == null)
                 {
-                    StartLog.debug("module not found [%s]%n",parentName);
+                    if (parentName.contains("${"))
+                        StartLog.debug("module not found [%s]%n",parentName);
+                    else
+                        StartLog.warn("module not found [%s]%n",parentName);
                 }
                 else
                 {
@@ -196,7 +199,7 @@
             }
             for (String parent : module.getParentNames())
             {
-                System.out.printf("   Parent: %s%n",parent);
+                System.out.printf("   Depend: %s%n",parent);
             }
             for (String lib : module.getLibs())
             {
@@ -294,10 +297,10 @@
 
     private void findParents(Module module, Map<String, Module> ret)
     {
-        ret.put(module.getName(), module);
+        ret.put(module.getName(),module);
         for (Module parent : module.getParentEdges())
         {
-            ret.put(parent.getName(), parent);
+            ret.put(parent.getName(),parent);
             findParents(parent,ret);
         }
     }
@@ -369,15 +372,33 @@
         return module;
     }
 
+    public void registerParentsIfMissing(BaseHome basehome, StartArgs args, Module module) throws IOException
+    {
+        Set<String> parents = new HashSet<>(module.getParentNames());
+        for (String name : parents)
+        {
+            if (!modules.containsKey(name))
+            {
+                Path file = basehome.getPath("modules/" + name + ".mod");
+                if (FS.canReadFile(file))
+                {
+                    Module parent = registerModule(basehome,args,file);
+                    updateParentReferencesTo(parent);
+                    registerParentsIfMissing(basehome, args, parent);
+                }
+            }
+        }
+    }
+    
     public void registerAll(BaseHome basehome, StartArgs args) throws IOException
     {
-        for (File file : basehome.listFiles("modules",new FS.FilenameRegexFilter("^.*\\.mod$")))
+        for (Path path : basehome.getPaths("modules/*.mod"))
         {
-            registerModule(basehome,args,file);
+            registerModule(basehome,args,path);
         }
 
         // load missing post-expanded dependent modules
-        boolean done = false;     
+        boolean done = false;
         while (!done)
         {
             done = true;
@@ -398,22 +419,22 @@
 
             for (String missingParent : missingParents)
             {
-                File file = basehome.getFile("modules/" + missingParent + ".mod");
-                if ( FS.canReadFile(file) )
+                Path file = basehome.getPath("modules/" + missingParent + ".mod");
+                if (FS.canReadFile(file))
                 {
                     Module module = registerModule(basehome,args,file);
                     updateParentReferencesTo(module);
                 }
                 else
                 {
-                    StartLog.debug("Missing module definition: [ Mod: %s | File: %s]", missingParent, file);
+                    StartLog.debug("Missing module definition: [ Mod: %s | File: %s]",missingParent,file);
                     missingModules.add(missingParent);
                 }
             }
         }
     }
 
-    private Module registerModule(BaseHome basehome, StartArgs args, File file) throws FileNotFoundException, IOException
+    private Module registerModule(BaseHome basehome, StartArgs args, Path file) throws FileNotFoundException, IOException
     {
         if (!FS.canReadFile(file))
         {
@@ -440,7 +461,7 @@
      */
     public List<Module> resolveEnabled()
     {
-        Map<String, Module> active = new HashMap<String,Module>();
+        Map<String, Module> active = new HashMap<String, Module>();
 
         for (Module module : modules.values())
         {
@@ -455,20 +476,20 @@
          * 
          * Ex: npn should match anything under npn/
          */
-        for ( String missing : missingModules )
+        for (String missing : missingModules)
         {
-            for (String activeModule: active.keySet())
-            {                
-                if ( missing.startsWith(activeModule) )
+            for (String activeModule : active.keySet())
+            {
+                if (missing.startsWith(activeModule))
                 {
-                    StartLog.warn("** Unable to continue, required dependency missing. [%s]", missing);
+                    StartLog.warn("** Unable to continue, required dependency missing. [%s]",missing);
                     StartLog.warn("** As configured, Jetty is unable to start due to a missing enabled module dependency.");
                     StartLog.warn("** This may be due to a transitive dependency akin to spdy on npn, which resolves based on the JDK in use.");
                     return Collections.emptyList();
                 }
             }
         }
-        
+
         List<Module> ordered = new ArrayList<>();
         ordered.addAll(active.values());
         Collections.sort(ordered,new Module.DepthComparator());
@@ -477,7 +498,7 @@
 
     public Set<String> resolveParentModulesOf(String moduleName)
     {
-        Map<String,Module> ret = new HashMap<>();
+        Map<String, Module> ret = new HashMap<>();
         Module module = get(moduleName);
         findParents(module,ret);
         return ret.keySet();
@@ -524,4 +545,26 @@
             m.setParentNames(resolvedParents);
         }
     }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append("Modules[");
+        str.append("count=").append(modules.size());
+        str.append(",<");
+        boolean delim = false;
+        for (String name : modules.keySet())
+        {
+            if (delim)
+            {
+                str.append(',');
+            }
+            str.append(name);
+            delim = true;
+        }
+        str.append(">");
+        str.append("]");
+        return str.toString();
+    }
 }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/NaturalSort.java b/jetty-start/src/main/java/org/eclipse/jetty/start/NaturalSort.java
index 042b326..838a96d 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/NaturalSort.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/NaturalSort.java
@@ -19,6 +19,7 @@
 package org.eclipse.jetty.start;
 
 import java.io.File;
+import java.nio.file.Path;
 import java.text.CollationKey;
 import java.text.Collator;
 import java.util.Comparator;
@@ -28,6 +29,19 @@
  */
 public class NaturalSort
 {
+    public static class Paths implements Comparator<Path>
+    {
+        private final Collator collator = Collator.getInstance();
+
+        @Override
+        public int compare(Path o1, Path o2)
+        {
+            CollationKey key1 = collator.getCollationKey(o1.toString());
+            CollationKey key2 = collator.getCollationKey(o2.toString());
+            return key1.compareTo(key2);
+        }
+    }
+
     public static class Files implements Comparator<File>
     {
         private final Collator collator = Collator.getInstance();
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/PathFinder.java b/jetty-start/src/main/java/org/eclipse/jetty/start/PathFinder.java
new file mode 100644
index 0000000..1f06f06
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/PathFinder.java
@@ -0,0 +1,169 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystemLoopException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class PathFinder extends SimpleFileVisitor<Path>
+{
+    // internal tracking of prior notified paths (to avoid repeated notification of same ignored path)
+    private static Set<Path> NOTIFIED_PATHS = new HashSet<>();
+
+    private boolean includeDirsInResults = false;
+    private Map<String, Path> hits = new HashMap<>();
+    private Path basePath = null;
+    private PathMatcher dirMatcher = PathMatchers.getNonHidden();
+    private PathMatcher fileMatcher = PathMatchers.getNonHidden();
+
+    private void addHit(Path path)
+    {
+        String relPath = basePath.relativize(path).toString();
+        StartLog.debug("Found [" + relPath + "]  " + path);
+        hits.put(relPath,path);
+    }
+
+    public PathMatcher getDirMatcher()
+    {
+        return dirMatcher;
+    }
+
+    public PathMatcher getFileMatcher()
+    {
+        return fileMatcher;
+    }
+
+    public List<File> getHitList()
+    {
+        List<File> ret = new ArrayList<>();
+        for (Path path : hits.values())
+        {
+            ret.add(path.toFile());
+        }
+        return ret;
+    }
+
+    public Collection<Path> getHits()
+    {
+        return hits.values();
+    }
+
+    public boolean isIncludeDirsInResults()
+    {
+        return includeDirsInResults;
+    }
+
+    @Override
+    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
+    {
+        if (dirMatcher.matches(dir))
+        {
+            StartLog.debug("Following dir: " + dir);
+            if (includeDirsInResults && fileMatcher.matches(dir))
+            {
+                addHit(dir);
+            }
+            return FileVisitResult.CONTINUE;
+        }
+        else
+        {
+            StartLog.debug("Skipping dir: " + dir);
+            return FileVisitResult.SKIP_SUBTREE;
+        }
+    }
+
+    /**
+     * Set the active basePath, used for resolving relative paths.
+     * <p>
+     * When a hit arrives for a subsequent find that has the same relative path as a prior hit, the new hit overrides the prior path as the active hit.
+     * 
+     * @param basePath
+     *            the basePath to tag all hits with
+     */
+    public void setBase(Path basePath)
+    {
+        this.basePath = basePath;
+    }
+
+    public void setDirMatcher(PathMatcher dirMatcher)
+    {
+        this.dirMatcher = dirMatcher;
+    }
+
+    public void setFileMatcher(PathMatcher fileMatcher)
+    {
+        this.fileMatcher = fileMatcher;
+    }
+
+    public void setFileMatcher(String pattern)
+    {
+        this.fileMatcher = PathMatchers.getMatcher(pattern);
+    }
+
+    public void setIncludeDirsInResults(boolean includeDirsInResults)
+    {
+        this.includeDirsInResults = includeDirsInResults;
+    }
+
+    @Override
+    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
+    {
+        if (fileMatcher.matches(file))
+        {
+            addHit(file);
+        }
+        else
+        {
+            StartLog.debug("Ignoring file: " + file);
+        }
+        return FileVisitResult.CONTINUE;
+    }
+
+    @Override
+    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
+    {
+        if (exc instanceof FileSystemLoopException)
+        {
+            if (!NOTIFIED_PATHS.contains(file))
+            {
+                StartLog.warn("skipping detected filesystem loop: " + file);
+                NOTIFIED_PATHS.add(file);
+            }
+            return FileVisitResult.SKIP_SUBTREE;
+        }
+        else
+        {
+            StartLog.warn(exc);
+            return super.visitFileFailed(file,exc);
+        }
+    }
+}
\ No newline at end of file
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java b/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java
new file mode 100644
index 0000000..609c8b2
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java
@@ -0,0 +1,208 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+
+/**
+ * Common PathMatcher implementations.
+ */
+public class PathMatchers
+{
+    private static class NonHiddenMatcher implements PathMatcher
+    {
+        @Override
+        public boolean matches(Path path)
+        {
+            try
+            {
+                return !Files.isHidden(path);
+            }
+            catch (IOException e)
+            {
+                StartLog.debug(e);
+                return false;
+            }
+        }
+    }
+    
+    private static final char GLOB_CHARS[] = "*?".toCharArray();
+    private static final char SYNTAXED_GLOB_CHARS[] = "{}[]|:".toCharArray();
+    private static final Path EMPTY_PATH = new File(".").toPath();
+
+    /**
+     * Convert a pattern to a Path object.
+     * 
+     * @param pattern
+     *            the raw pattern (can contain "glob:" or "regex:" syntax indicator)
+     * @return the Path version of the pattern provided.
+     */
+    private static Path asPath(String pattern)
+    {
+        String test = pattern;
+        if (test.startsWith("glob:"))
+        {
+            test = test.substring("glob:".length());
+        }
+        else if (test.startsWith("regex:"))
+        {
+            test = test.substring("regex:".length());
+        }
+        return new File(test).toPath();
+    }
+
+    public static PathMatcher getMatcher(String pattern)
+    {
+        FileSystem fs = FileSystems.getDefault();
+
+        // If using FileSystem.getPathMatcher() with "glob:" or "regex:"
+        // use FileSystem default pattern behavior
+        if (pattern.startsWith("glob:") || pattern.startsWith("regex:"))
+        {
+            StartLog.debug("Using Standard " + fs.getClass().getName() + " pattern: " + pattern);
+            return fs.getPathMatcher(pattern);
+        }
+
+        // If the pattern starts with a root path then its assumed to
+        // be a full system path
+        for (Path root : fs.getRootDirectories())
+        {
+            StartLog.debug("root: " + root);
+            if (pattern.startsWith(root.toString()))
+            {
+                String pat = "glob:" + pattern;
+                StartLog.debug("Using absolute path pattern: " + pat);
+                return fs.getPathMatcher(pat);
+            }
+        }
+
+        // Doesn't start with filesystem root, then assume the pattern
+        // is a relative file path pattern.
+        String pat = "glob:**/" + pattern;
+        StartLog.debug("Using relative path pattern: " + pat);
+        return fs.getPathMatcher(pat);
+    }
+
+    public static PathMatcher getNonHidden()
+    {
+        return new NonHiddenMatcher();
+    }
+
+    /**
+     * Provide the non-glob / non-regex prefix on the pattern as a Path reference.
+     * 
+     * @param pattern
+     *            the pattern to test
+     * @return the Path representing the search root for the pattern provided.
+     */
+    public static Path getSearchRoot(final String pattern)
+    {
+        Path path = asPath(pattern);
+        Path test = path.getRoot();
+
+        boolean isSyntaxed = pattern.startsWith("glob:") || pattern.startsWith("regex:");
+
+        int len = path.getNameCount();
+        for (int i = 0; i < len; i++)
+        {
+            Path part = path.getName(i);
+            if (isGlob(part.toString(),isSyntaxed))
+            {
+                // found a glob part, return prior parts now
+                break;
+            }
+
+            // is this the last entry?
+            if (i == (len - 1))
+            {
+                // always return prior entries
+                break;
+            }
+
+            if (test == null)
+            {
+                test = part;
+            }
+            else
+            {
+                test = test.resolve(part);
+            }
+        }
+
+        if (test == null)
+        {
+            return EMPTY_PATH;
+        }
+        return test;
+    }
+
+    /**
+     * Tests if provided pattern is an absolute reference (or not)
+     * 
+     * @param pattern
+     *            the pattern to test
+     * @return true if pattern is an absolute reference.
+     */
+    public static boolean isAbsolute(final String pattern)
+    {
+        return asPath(pattern).isAbsolute();
+    }
+
+    /**
+     * Determine if part is a glob pattern.
+     * 
+     * @param part
+     *            the string to check
+     * @param syntaxed
+     *            true if overall pattern is syntaxed with <code>"glob:"</code> or <code>"regex:"</code>
+     * @return true if part has glob characters
+     */
+    private static boolean isGlob(String part, boolean syntaxed)
+    {
+        int len = part.length();
+        for (int i = 0; i < len; i++)
+        {
+            char c = part.charAt(i);
+            for (char g : GLOB_CHARS)
+            {
+                if (c == g)
+                {
+                    return true;
+                }
+            }
+            if (syntaxed)
+            {
+                for (char g : SYNTAXED_GLOB_CHARS)
+                {
+                    if (c == g)
+                    {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java
index 838cf46..f760f6e 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java
@@ -18,10 +18,14 @@
 
 package org.eclipse.jetty.start;
 
+import static org.eclipse.jetty.start.UsageException.*;
+
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Stack;
@@ -60,8 +64,91 @@
     }
 
     public static final String ORIGIN_SYSPROP = "<system-property>";
+    
+    public static String getValue(String arg)
+    {
+        int idx = arg.indexOf('=');
+        if (idx == (-1))
+        {
+            throw new UsageException(ERR_BAD_ARG,"Argument is missing a required value: %s",arg);
+        }
+        String value = arg.substring(idx + 1).trim();
+        if (value.length() <= 0)
+        {
+            throw new UsageException(ERR_BAD_ARG,"Argument is missing a required value: %s",arg);
+        }
+        return value;
+    }
+
+    public static List<String> getValues(String arg)
+    {
+        String v = getValue(arg);
+        ArrayList<String> l = new ArrayList<>();
+        for (String s : v.split(","))
+        {
+            if (s != null)
+            {
+                s = s.trim();
+                if (s.length() > 0)
+                {
+                    l.add(s);
+                }
+            }
+        }
+        return l;
+    }
 
     private Map<String, Prop> props = new HashMap<>();
+    private List<String> sysPropTracking = new ArrayList<>();
+
+    public void addAll(Props other)
+    {
+        this.props.putAll(other.props);
+        this.sysPropTracking.addAll(other.sysPropTracking);
+    }
+    
+    /**
+     * Add a potential argument as a property.
+     * <p>
+     * If arg is not a property, ignore it.
+     * @param arg the argument to parse for a potential property
+     * @param source the source for this argument (to track origin of property from)
+     */
+    public void addPossibleProperty(String arg, String source)
+    {
+        // Start property (syntax similar to System property)
+        if (arg.startsWith("-D"))
+        {
+            String[] assign = arg.substring(2).split("=",2);
+            switch (assign.length)
+            {
+                case 2:
+                    setSystemProperty(assign[0],assign[1]);
+                    setProperty(assign[0],assign[1],source);
+                    break;
+                case 1:
+                    setSystemProperty(assign[0],"");
+                    setProperty(assign[0],"",source);
+                    break;
+                default:
+                    break;
+            }
+            return;
+        }
+
+        // Is this a raw property declaration?
+        int idx = arg.indexOf('=');
+        if (idx >= 0)
+        {
+            String key = arg.substring(0,idx);
+            String value = arg.substring(idx + 1);
+
+            setProperty(key,value,source);
+            return;
+        }
+
+        // All other strings are ignored
+    }
 
     public String cleanReference(String property)
     {
@@ -96,13 +183,6 @@
             return str;
         }
 
-        if (props.isEmpty())
-        {
-            // nothing to expand
-            // this situation can occur from --add-to-startd on a new blank base directory
-            return str;
-        }
-
         Pattern pat = Pattern.compile("(?<=[^$]|^)(\\$\\{[^}]*\\})");
         Matcher mat = pat.matcher(str);
         StringBuilder expanded = new StringBuilder();
@@ -138,7 +218,7 @@
             if (value == null)
             {
                 StartLog.debug("Unable to expand: %s",property);
-                expanded.append(property);
+                expanded.append(mat.group(1));
             }
             else
             {
@@ -164,8 +244,13 @@
 
     public Prop getProp(String key)
     {
+        return getProp(key,true);
+    }
+
+    public Prop getProp(String key, boolean searchSystemProps)
+    {
         Prop prop = props.get(key);
-        if (prop == null)
+        if ((prop == null) && searchSystemProps)
         {
             // try system property
             prop = getSystemProperty(key);
@@ -221,6 +306,11 @@
         return props.values().iterator();
     }
 
+    public void reset()
+    {
+        props.clear();
+    }
+
     public void setProperty(Prop prop)
     {
         props.put(prop.key,prop);
@@ -256,4 +346,10 @@
         // write normal properties file
         props.store(stream,comments);
     }
+
+    public void setSystemProperty(String key, String value)
+    {
+        System.setProperty(key,value);
+        sysPropTracking.add(key);
+    }
 }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
index 93ca76d..cde9f56 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
@@ -21,26 +21,29 @@
 import static org.eclipse.jetty.start.UsageException.*;
 
 import java.io.File;
-import java.io.FileFilter;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.StringTokenizer;
 
 import org.eclipse.jetty.start.Props.Prop;
+import org.eclipse.jetty.start.config.ConfigSource;
+import org.eclipse.jetty.start.config.ConfigSources;
+import org.eclipse.jetty.start.config.DirConfigSource;
 
 /**
  * The Arguments required to start Jetty.
  */
 public class StartArgs
 {
-    public static final String CMD_LINE_SOURCE = "<command-line>";
     public static final String VERSION;
 
     static
@@ -67,23 +70,41 @@
 
     private static final String SERVER_MAIN = "org.eclipse.jetty.xml.XmlConfiguration";
 
-    private List<String> commandLine = new ArrayList<>();
+    /** List of enabled modules */
     private Set<String> modules = new HashSet<>();
+    /** Map of enabled modules to the source of where that activation occurred */
     private Map<String, List<String>> sources = new HashMap<>();
+    /** Map of properties to where that property was declared */
+    private Map<String, String> propertySource = new HashMap<>();
+    /** List of all active [files] sections from enabled modules */
     private List<FileArg> files = new ArrayList<>();
+    /** List of all active [lib] sectinos from enabled modules */
     private Classpath classpath;
+    /** List of all active [xml] sections from enabled modules */
+    private List<Path> xmls = new ArrayList<>();
+    /** JVM arguments, found via commmand line and in all active [exec] sections from enabled modules */
+    private List<String> jvmArgs = new ArrayList<>();
+
+    /** List of all xml references found directly on command line or start.ini */
     private List<String> xmlRefs = new ArrayList<>();
-    private List<File> xmls = new ArrayList<>();
+
     private Props properties = new Props();
     private Set<String> systemPropertyKeys = new HashSet<>();
-    private List<String> jvmArgs = new ArrayList<>();
-    private List<String> moduleStartdIni = new ArrayList<>();
-    private List<String> moduleStartIni = new ArrayList<>();
-    private Map<String, String> propertySource = new HashMap<>();
+    private List<String> rawLibs = new ArrayList<>();
+
+    // jetty.base - build out commands
+    /** --add-to-startd=[module,[module]] */
+    private List<String> addToStartdIni = new ArrayList<>();
+    /** --add-to-start=[module,[module]] */
+    private List<String> addToStartIni = new ArrayList<>();
+
+    // module inspection commands
+    /** --write-module-graph=[filename] */
     private String moduleGraphFilename;
 
+    /** Collection of all modules */
     private Modules allModules;
-    // Should the server be run?
+    /** Should the server be run? */
     private boolean run = true;
     private boolean download = false;
     private boolean help = false;
@@ -96,9 +117,8 @@
 
     private boolean exec = false;
 
-    public StartArgs(String[] commandLineArgs)
+    public StartArgs()
     {
-        commandLine.addAll(Arrays.asList(commandLineArgs));
         classpath = new Classpath();
     }
 
@@ -117,13 +137,13 @@
         System.setProperty(key,value);
     }
 
-    private void addUniqueXmlFile(String xmlRef, File xmlfile) throws IOException
+    private void addUniqueXmlFile(String xmlRef, Path xmlfile) throws IOException
     {
         if (!FS.canReadFile(xmlfile))
         {
             throw new IOException("Cannot read file: " + xmlRef);
         }
-        xmlfile = xmlfile.getCanonicalFile();
+        xmlfile = FS.toRealPath(xmlfile);
         if (!xmls.contains(xmlfile))
         {
             xmls.add(xmlfile);
@@ -141,13 +161,13 @@
             return;
         }
 
-        for (File xml : xmls)
+        for (Path xml : xmls)
         {
-            System.out.printf(" %s%n",baseHome.toShortForm(xml.getAbsolutePath()));
+            System.out.printf(" %s%n",baseHome.toShortForm(xml.toAbsolutePath()));
         }
     }
 
-    public void dumpEnvironment()
+    public void dumpEnvironment(BaseHome baseHome)
     {
         // Java Details
         System.out.println();
@@ -169,10 +189,30 @@
         System.out.println();
         System.out.println("Jetty Environment:");
         System.out.println("-----------------");
-
+        dumpProperty("jetty.version");
         dumpProperty("jetty.home");
         dumpProperty("jetty.base");
-        dumpProperty("jetty.version");
+        
+        // Jetty Configuration Environment
+        System.out.println();
+        System.out.println("Config Search Order:");
+        System.out.println("--------------------");
+        for (ConfigSource config : baseHome.getConfigSources())
+        {
+            System.out.printf(" %s",config.getId());
+            if (config instanceof DirConfigSource)
+            {
+                DirConfigSource dirsource = (DirConfigSource)config;
+                if (dirsource.isPropertyBased())
+                {
+                    System.out.printf(" -> %s",dirsource.getDir());
+                }
+            }
+            System.out.println();
+        }
+        
+        // Jetty Se
+        System.out.println();
     }
 
     public void dumpJvmArgs()
@@ -230,6 +270,30 @@
         }
     }
 
+    private void dumpProperty(String key)
+    {
+        Prop prop = properties.getProp(key);
+        if (prop == null)
+        {
+            System.out.printf(" %s (not defined)%n",key);
+        }
+        else
+        {
+            System.out.printf(" %s = %s%n",key,properties.expand(prop.value));
+            if (StartLog.isDebugEnabled())
+            {
+                System.out.printf("   origin: %s%n",prop.origin);
+                while (prop.overrides != null)
+                {
+                    prop = prop.overrides;
+                    System.out.printf("   (overrides)%n");
+                    System.out.printf("     %s = %s%n",key,properties.expand(prop.value));
+                    System.out.printf("     origin: %s%n",prop.origin);
+                }
+            }
+        }
+    }
+
     public void dumpSystemProperties()
     {
         System.out.println();
@@ -258,30 +322,6 @@
         System.out.printf(" %s = %s%n",key,System.getProperty(key));
     }
 
-    private void dumpProperty(String key)
-    {
-        Prop prop = properties.getProp(key);
-        if (prop == null)
-        {
-            System.out.printf(" %s (not defined)%n",key);
-        }
-        else
-        {
-            System.out.printf(" %s = %s%n",key,properties.expand(prop.value));
-            if (StartLog.isDebugEnabled())
-            {
-                System.out.printf("   origin: %s%n",prop.origin);
-                while (prop.overrides != null)
-                {
-                    prop = prop.overrides;
-                    System.out.printf("   (overrides)%n");
-                    System.out.printf("     %s = %s%n",key,properties.expand(prop.value));
-                    System.out.printf("     origin: %s%n",prop.origin);
-                }
-            }
-        }
-    }
-
     /**
      * Ensure that the System Properties are set (if defined as a System property, or start.config property, or start.ini property)
      * 
@@ -309,6 +349,27 @@
     }
 
     /**
+     * Expand any command line added <code>--lib</code> lib references.
+     * 
+     * @param baseHome
+     * @throws IOException
+     */
+    public void expandLibs(BaseHome baseHome) throws IOException
+    {
+        for (String rawlibref : rawLibs)
+        {
+            StartLog.debug("rawlibref = " + rawlibref);
+            String libref = properties.expand(rawlibref);
+            StartLog.debug("expanded = " + libref);
+
+            for (Path libpath : baseHome.getPaths(libref))
+            {
+                classpath.addComponent(libpath.toFile());
+            }
+        }
+    }
+
+    /**
      * Build up the Classpath and XML file references based on enabled Module list.
      * 
      * @param baseHome
@@ -322,72 +383,27 @@
             // Find and Expand Libraries
             for (String rawlibref : module.getLibs())
             {
+                StartLog.debug("rawlibref = " + rawlibref);
                 String libref = properties.expand(rawlibref);
+                StartLog.debug("expanded = " + libref);
 
-                if (libref.startsWith("regex:"))
+                for (Path libpath : baseHome.getPaths(libref))
                 {
-                    String regex = libref.substring("regex:".length());
-                    for (File libfile : baseHome.listFilesRegex(regex))
-                    {
-                        classpath.addComponent(libfile);
-                    }
-                    continue;
+                    classpath.addComponent(libpath.toFile());
                 }
+            }
 
-                libref = FS.separators(libref);
-
-                // Any globs here?
-                if (libref.contains("*"))
-                {
-                    // Glob Reference
-                    int idx = libref.lastIndexOf(File.separatorChar);
-
-                    String relativePath = "/";
-                    String filenameRef = libref;
-                    if (idx >= 0)
-                    {
-                        relativePath = libref.substring(0,idx);
-                        filenameRef = libref.substring(idx + 1);
-                    }
-
-                    StringBuilder regex = new StringBuilder();
-                    regex.append('^');
-                    for (char c : filenameRef.toCharArray())
-                    {
-                        switch (c)
-                        {
-                            case '*':
-                                regex.append(".*");
-                                break;
-                            case '.':
-                                regex.append("\\.");
-                                break;
-                            default:
-                                regex.append(c);
-                        }
-                    }
-                    regex.append('$');
-
-                    FileFilter filter = new FS.FilenameRegexFilter(regex.toString());
-
-                    for (File libfile : baseHome.listFiles(relativePath,filter))
-                    {
-                        classpath.addComponent(libfile);
-                    }
-                }
-                else
-                {
-                    // Straight Reference
-                    File libfile = baseHome.getFile(libref);
-                    classpath.addComponent(libfile);
-                }
+            for (String jvmArg : module.getJvmArgs())
+            {
+                exec = true;
+                jvmArgs.add(jvmArg);
             }
 
             // Find and Expand XML files
             for (String xmlRef : module.getXmls())
             {
                 // Straight Reference
-                File xmlfile = baseHome.getFile(xmlRef);
+                Path xmlfile = baseHome.getPath(xmlRef);
                 addUniqueXmlFile(xmlRef,xmlfile);
             }
 
@@ -400,6 +416,16 @@
         }
     }
 
+    public List<String> getAddToStartdIni()
+    {
+        return addToStartdIni;
+    }
+
+    public List<String> getAddToStartIni()
+    {
+        return addToStartIni;
+    }
+
     public Modules getAllModules()
     {
         return allModules;
@@ -410,9 +436,9 @@
         return classpath;
     }
 
-    public List<String> getCommandLine()
+    public Set<String> getEnabledModules()
     {
-        return this.commandLine;
+        return this.modules;
     }
 
     public List<FileArg> getFiles()
@@ -420,11 +446,6 @@
         return files;
     }
 
-    public Set<String> getEnabledModules()
-    {
-        return this.modules;
-    }
-
     public List<String> getJvmArgs()
     {
         return jvmArgs;
@@ -436,11 +457,11 @@
 
         if (addJavaInit)
         {
-            cmd.addArg(CommandLineBuilder.findJavaBin());
+            cmd.addRawArg(CommandLineBuilder.findJavaBin());
 
             for (String x : jvmArgs)
             {
-                cmd.addArg(x);
+                cmd.addRawArg(x);
             }
 
             cmd.addRawArg("-Djetty.home=" + baseHome.getHome());
@@ -453,7 +474,7 @@
                 cmd.addEqualsArg("-D" + propKey,value);
             }
 
-            cmd.addArg("-cp");
+            cmd.addRawArg("-cp");
             cmd.addRawArg(classpath.toString());
             cmd.addRawArg(getMainClassname());
         }
@@ -475,12 +496,12 @@
             {
                 properties.store(out,"start.jar properties");
             }
-            cmd.addArg(prop_file.getAbsolutePath());
+            cmd.addRawArg(prop_file.getAbsolutePath());
         }
 
-        for (File xml : xmls)
+        for (Path xml : xmls)
         {
-            cmd.addRawArg(xml.getAbsolutePath());
+            cmd.addRawArg(xml.toAbsolutePath().toString());
         }
 
         return cmd;
@@ -497,16 +518,6 @@
         return moduleGraphFilename;
     }
 
-    public List<String> getModuleStartdIni()
-    {
-        return moduleStartdIni;
-    }
-
-    public List<String> getModuleStartIni()
-    {
-        return moduleStartIni;
-    }
-
     public Props getProperties()
     {
         return properties;
@@ -517,40 +528,7 @@
         return sources.get(module);
     }
 
-    private String getValue(String arg)
-    {
-        int idx = arg.indexOf('=');
-        if (idx == (-1))
-        {
-            throw new UsageException(ERR_BAD_ARG,"Argument is missing a required value: %s",arg);
-        }
-        String value = arg.substring(idx + 1).trim();
-        if (value.length() <= 0)
-        {
-            throw new UsageException(ERR_BAD_ARG,"Argument is missing a required value: %s",arg);
-        }
-        return value;
-    }
-
-    private List<String> getValues(String arg)
-    {
-        String v = getValue(arg);
-        ArrayList<String> l = new ArrayList<>();
-        for (String s : v.split(","))
-        {
-            if (s != null)
-            {
-                s = s.trim();
-                if (s.length() > 0)
-                {
-                    l.add(s);
-                }
-            }
-        }
-        return l;
-    }
-
-    public List<File> getXmlFiles()
+    public List<Path> getXmlFiles()
     {
         return xmls;
     }
@@ -610,31 +588,6 @@
         return listModules;
     }
 
-    private void setProperty(String key, String value, String source)
-    {
-        // Special / Prevent override from start.ini's
-        if (key.equals("jetty.home"))
-        {
-            properties.setProperty("jetty.home",System.getProperty("jetty.home"),source);
-            return;
-        }
-
-        // Special / Prevent override from start.ini's
-        if (key.equals("jetty.base"))
-        {
-            properties.setProperty("jetty.base",System.getProperty("jetty.base"),source);
-            return;
-        }
-
-        // Normal
-        properties.setProperty(key,value,source);
-    }
-
-    public void setRun(boolean run)
-    {
-        this.run = run;
-    }
-
     public boolean isRun()
     {
         return run;
@@ -650,20 +603,16 @@
         return version;
     }
 
-    public void parse(BaseHome baseHome, TextFile file)
+    public void parse(ConfigSources sources)
     {
-        String source;
-        try
+        ListIterator<ConfigSource> iter = sources.reverseListIterator();
+        while (iter.hasPrevious())
         {
-            source = baseHome.toShortForm(file.getFile());
-        }
-        catch (Exception e)
-        {
-            throw new UsageException(ERR_BAD_ARG,"Bad file: %s",file);
-        }
-        for (String line : file)
-        {
-            parse(line,source);
+            ConfigSource source = iter.previous();
+            for (String arg : source.getArgs())
+            {
+                parse(arg,source.getId());
+            }
         }
     }
 
@@ -688,28 +637,25 @@
 
         if ("--help".equals(arg) || "-?".equals(arg))
         {
-            if (!CMD_LINE_SOURCE.equals(source))
-            {
-                throw new UsageException(ERR_BAD_ARG,"%s not allowed in %s",arg,source);
-            }
-
             help = true;
             run = false;
             return;
         }
 
-        if ("--debug".equals(arg))
+        if ("--debug".equals(arg) || arg.startsWith("--start-log-file"))
         {
             // valid, but handled in StartLog instead
             return;
         }
 
+        if (arg.startsWith("--extra-start-dir="))
+        {
+            // valid, but handled in ConfigSources instead
+            return;
+        }
+
         if ("--stop".equals(arg))
         {
-            if (!CMD_LINE_SOURCE.equals(source))
-            {
-                throw new UsageException(ERR_BAD_ARG,"%s not allowed in %s",arg,source);
-            }
             stopCommand = true;
             run = false;
             return;
@@ -717,7 +663,7 @@
 
         if (arg.startsWith("--download="))
         {
-            addFile(getValue(arg));
+            addFile(Props.getValue(arg));
             run = false;
             download = true;
             return;
@@ -746,15 +692,12 @@
 
         if ("--dry-run".equals(arg) || "--exec-print".equals(arg))
         {
-            if (!CMD_LINE_SOURCE.equals(source))
-            {
-                throw new UsageException(ERR_BAD_ARG,"%s not allowed in %s",arg,source);
-            }
             dryRun = true;
             run = false;
             return;
         }
 
+        // Enable forked execution of Jetty server
         if ("--exec".equals(arg))
         {
             exec = true;
@@ -762,11 +705,18 @@
         }
 
         // Arbitrary Libraries
-
         if (arg.startsWith("--lib="))
         {
-            String cp = getValue(arg);
-            classpath.addClasspath(cp);
+            String cp = Props.getValue(arg);
+
+            if (cp != null)
+            {
+                StringTokenizer t = new StringTokenizer(cp,File.pathSeparator);
+                while (t.hasMoreTokens())
+                {
+                    rawLibs.add(t.nextToken());
+                }
+            }
             return;
         }
 
@@ -778,31 +728,28 @@
             return;
         }
 
-        if (arg.startsWith("--add-to-startd"))
+        // jetty.base build-out : add to ${jetty.base}/start.d/
+        if (arg.startsWith("--add-to-startd="))
         {
-            if (!CMD_LINE_SOURCE.equals(source))
-            {
-                throw new UsageException(ERR_BAD_ARG,"%s not allowed in %s",arg,source);
-            }
-            moduleStartdIni.addAll(getValues(arg));
+            addToStartdIni.addAll(Props.getValues(arg));
             run = false;
+            download = true;
             return;
         }
 
-        if (arg.startsWith("--add-to-start"))
+        // jetty.base build-out : add to ${jetty.base}/start.ini
+        if (arg.startsWith("--add-to-start="))
         {
-            if (!CMD_LINE_SOURCE.equals(source))
-            {
-                throw new UsageException(ERR_BAD_ARG,"%s not allowed in %s",arg,source);
-            }
-            moduleStartIni.addAll(getValues(arg));
+            addToStartIni.addAll(Props.getValues(arg));
             run = false;
+            download = true;
             return;
         }
 
+        // Enable a module
         if (arg.startsWith("--module="))
         {
-            for (String moduleName : getValues(arg))
+            for (String moduleName : Props.getValues(arg))
             {
                 modules.add(moduleName);
                 List<String> list = sources.get(moduleName);
@@ -816,9 +763,10 @@
             return;
         }
 
+        // Create graphviz output of module graph
         if (arg.startsWith("--write-module-graph="))
         {
-            this.moduleGraphFilename = getValue(arg);
+            this.moduleGraphFilename = Props.getValue(arg);
             run = false;
             return;
         }
@@ -862,14 +810,11 @@
             String key = arg.substring(0,idx);
             String value = arg.substring(idx + 1);
 
-            if (source != CMD_LINE_SOURCE)
+            if (propertySource.containsKey(key))
             {
-                if (propertySource.containsKey(key))
-                {
-                    throw new UsageException(ERR_BAD_ARG,"Property %s in %s already set in %s",key,source,propertySource.get(key));
-                }
-                propertySource.put(key,source);
+                StartLog.warn("Property %s in %s already set in %s",key,source,propertySource.get(key));
             }
+            propertySource.put(key,source);
 
             if ("OPTION".equals(key) || "OPTIONS".equals(key))
             {
@@ -901,26 +846,16 @@
         throw new UsageException(ERR_BAD_ARG,"Unrecognized argument: \"%s\" in %s",arg,source);
     }
 
-    public StartArgs parseCommandLine()
-    {
-        for (String line : commandLine)
-        {
-            parse(line,StartArgs.CMD_LINE_SOURCE);
-        }
-
-        return this;
-    }
-
     public void resolveExtraXmls(BaseHome baseHome) throws IOException
     {
         // Find and Expand XML files
         for (String xmlRef : xmlRefs)
         {
             // Straight Reference
-            File xmlfile = baseHome.getFile(xmlRef);
-            if (!xmlfile.exists())
+            Path xmlfile = baseHome.getPath(xmlRef);
+            if (!FS.exists(xmlfile))
             {
-                xmlfile = baseHome.getFile("etc/" + xmlRef);
+                xmlfile = baseHome.getPath("etc/" + xmlRef);
             }
             addUniqueXmlFile(xmlRef,xmlfile);
         }
@@ -931,13 +866,36 @@
         this.allModules = allModules;
     }
 
+    private void setProperty(String key, String value, String source)
+    {
+        // Special / Prevent override from start.ini's
+        if (key.equals("jetty.home"))
+        {
+            properties.setProperty("jetty.home",System.getProperty("jetty.home"),source);
+            return;
+        }
+
+        // Special / Prevent override from start.ini's
+        if (key.equals("jetty.base"))
+        {
+            properties.setProperty("jetty.base",System.getProperty("jetty.base"),source);
+            return;
+        }
+
+        // Normal
+        properties.setProperty(key,value,source);
+    }
+
+    public void setRun(boolean run)
+    {
+        this.run = run;
+    }
+
     @Override
     public String toString()
     {
         StringBuilder builder = new StringBuilder();
-        builder.append("StartArgs [commandLine=");
-        builder.append(commandLine);
-        builder.append(", enabledModules=");
+        builder.append("StartArgs [enabledModules=");
         builder.append(modules);
         builder.append(", xmlRefs=");
         builder.append(xmlRefs);
@@ -948,5 +906,4 @@
         builder.append("]");
         return builder.toString();
     }
-
 }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java
index c2635ae..524f958 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java
@@ -18,16 +18,17 @@
 
 package org.eclipse.jetty.start;
 
-import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.nio.file.Path;
 
 /**
  * Simple Start .INI handler
  */
 public class StartIni extends TextFile
 {
-    public StartIni(File file) throws FileNotFoundException, IOException
+    private Path basedir;
+
+    public StartIni(Path file) throws IOException
     {
         super(file);
     }
@@ -41,12 +42,33 @@
             String value = line.substring(idx + 1);
             for (String part : value.split(","))
             {
-                super.addUniqueLine("--module=" + part);
+                super.addUniqueLine("--module=" + expandBaseDir(part));
             }
         }
         else
         {
-            super.addUniqueLine(line);
+            super.addUniqueLine(expandBaseDir(line));
         }
     }
+
+    private String expandBaseDir(String line)
+    {
+        if (line == null)
+        {
+            return line;
+        }
+
+        return line.replace("${start.basedir}",basedir.toString());
+    }
+
+    @Override
+    public void init()
+    {
+        basedir = getFile().getParent().toAbsolutePath();
+    }
+
+    public Path getBaseDir()
+    {
+        return basedir;
+    }
 }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartLog.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartLog.java
index c411d0d..1f4105d 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartLog.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartLog.java
@@ -18,13 +18,15 @@
 
 package org.eclipse.jetty.start;
 
-import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
 import java.util.Date;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+
+import org.eclipse.jetty.start.config.CommandLineConfigSource;
 
 /**
  * Centralized Place for logging.
@@ -62,7 +64,7 @@
     {
         System.err.printf("INFO: " + format + "%n",args);
     }
-    
+
     public static void warn(String format, Object... args)
     {
         System.err.printf("WARNING: " + format + "%n",args);
@@ -72,7 +74,7 @@
     {
         t.printStackTrace(System.err);
     }
-    
+
     public static boolean isDebugEnabled()
     {
         return INSTANCE.debug;
@@ -80,17 +82,17 @@
 
     private boolean debug = false;
 
-    public void initialize(BaseHome baseHome, StartArgs args) throws IOException
+    public void initialize(BaseHome baseHome, CommandLineConfigSource cmdLineSource) throws IOException
     {
-        // Debug with boolean
-        Pattern debugBoolPat = Pattern.compile("(-D)?debug=(.*)");
-        // Log file name
-        Pattern logFilePat = Pattern.compile("(-D)?start-log-file=(.*)");
+        String dbgProp = cmdLineSource.getProperty("debug");
+        if (dbgProp != null)
+        {
+            debug = Boolean.parseBoolean(dbgProp);
+        }
 
-        // TODO: support backward compatible --daemon argument ??
+        String logFileName = cmdLineSource.getProperty("start-log-file");
 
-        Matcher matcher;
-        for (String arg : args.getCommandLine())
+        for (String arg : cmdLineSource.getArgs())
         {
             if ("--debug".equals(arg))
             {
@@ -98,55 +100,55 @@
                 continue;
             }
 
-            matcher = debugBoolPat.matcher(arg);
-            if (matcher.matches())
+            if (arg.startsWith("--start-log-file"))
             {
-                debug = Boolean.parseBoolean(matcher.group(2));
+                logFileName = Props.getValue(arg);
                 continue;
             }
+        }
 
-            matcher = logFilePat.matcher(arg);
-            if (matcher.matches())
-            {
-                String filename = matcher.group(2);
-                File logfile = baseHome.getBaseFile(filename);
-                initLogFile(logfile);
-            }
+        if (logFileName != null)
+        {
+            Path logfile = baseHome.getBasePath(logFileName);
+            initLogFile(logfile);
         }
     }
 
-    public void initLogFile(File logfile) throws IOException
+    public void initLogFile(Path logfile) throws IOException
     {
         if (logfile != null)
         {
-            File logDir = logfile.getParentFile();
-            if (!logDir.exists() || !logDir.canWrite())
+            try
             {
-                String err = String.format("Cannot write %s to directory %s [directory doesn't exist or is read-only]",logfile.getName(),
-                        logDir.getAbsolutePath());
-                throw new UsageException(UsageException.ERR_LOGGING,new IOException(err));
+                Path logDir = logfile.getParent();
+                FS.ensureDirectoryWritable(logDir);
+
+                Path startLog = logfile;
+
+                if (!FS.exists(startLog) && !FS.createNewFile(startLog))
+                {
+                    // Output about error is lost in majority of cases.
+                    throw new UsageException(UsageException.ERR_LOGGING,new IOException("Unable to create: " + startLog.toAbsolutePath()));
+                }
+
+                if (!FS.canWrite(startLog))
+                {
+                    // Output about error is lost in majority of cases.
+                    throw new UsageException(UsageException.ERR_LOGGING,new IOException("Unable to write to: " + startLog.toAbsolutePath()));
+                }
+
+                System.out.println("Logging to " + logfile);
+
+                OutputStream out = Files.newOutputStream(startLog,StandardOpenOption.CREATE,StandardOpenOption.APPEND);
+                PrintStream logger = new PrintStream(out);
+                System.setOut(logger);
+                System.setErr(logger);
+                System.out.println("Establishing " + logfile + " on " + new Date());
             }
-
-            File startLog = logfile;
-
-            if (!startLog.exists() && !startLog.createNewFile())
+            catch (IOException e)
             {
-                // Output about error is lost in majority of cases.
-                throw new UsageException(UsageException.ERR_LOGGING,new IOException("Unable to create: " + startLog.getAbsolutePath()));
+                throw new UsageException(UsageException.ERR_LOGGING,e);
             }
-
-            if (!startLog.canWrite())
-            {
-                // Output about error is lost in majority of cases.
-                throw new UsageException(UsageException.ERR_LOGGING,new IOException("Unable to write to: " + startLog.getAbsolutePath()));
-            }
-            
-            System.out.println("Logging to " + logfile);
-            
-            PrintStream logger = new PrintStream(new FileOutputStream(startLog,false));
-            System.setOut(logger);
-            System.setErr(logger);
-            System.out.println("Establishing " + logfile + " on " + new Date());
         }
     }
 
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/TextFile.java b/jetty-start/src/main/java/org/eclipse/jetty/start/TextFile.java
index fccc762..862422f 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/TextFile.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/TextFile.java
@@ -19,10 +19,11 @@
 package org.eclipse.jetty.start;
 
 import java.io.BufferedReader;
-import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -36,40 +37,37 @@
  */
 public class TextFile implements Iterable<String>
 {
-    private final File file;
+    private final Path file;
     private final List<String> lines = new ArrayList<>();
 
-    public TextFile(File file) throws FileNotFoundException, IOException
+    public TextFile(Path file) throws FileNotFoundException, IOException
     {
         this.file = file;
         init();
         
         if (!FS.canReadFile(file))
         {
-            StartLog.debug("Skipping read of missing file: %s",file.getAbsolutePath());
+            StartLog.debug("Skipping read of missing file: %s",file.toAbsolutePath());
             return;
         }
 
-        try (FileReader reader = new FileReader(file))
+        try (BufferedReader buf = Files.newBufferedReader(file,StandardCharsets.UTF_8))
         {
-            try (BufferedReader buf = new BufferedReader(reader))
+            String line;
+            while ((line = buf.readLine()) != null)
             {
-                String line;
-                while ((line = buf.readLine()) != null)
+                if (line.length() == 0)
                 {
-                    if (line.length() == 0)
-                    {
-                        continue;
-                    }
-
-                    if (line.charAt(0) == '#')
-                    {
-                        continue;
-                    }
-
-                    // TODO - bad form calling derived method from base class constructor
-                    process(line.trim());
+                    continue;
                 }
+
+                if (line.charAt(0) == '#')
+                {
+                    continue;
+                }
+
+                // TODO - bad form calling derived method from base class constructor
+                process(line.trim());
             }
         }
     }
@@ -84,7 +82,7 @@
         lines.add(line);
     }
 
-    public File getFile()
+    public Path getFile()
     {
         return file;
     }
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/config/CommandLineConfigSource.java b/jetty-start/src/main/java/org/eclipse/jetty/start/config/CommandLineConfigSource.java
new file mode 100644
index 0000000..61f9f64
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/config/CommandLineConfigSource.java
@@ -0,0 +1,245 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.config;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.start.BaseHome;
+import org.eclipse.jetty.start.FS;
+import org.eclipse.jetty.start.Props;
+import org.eclipse.jetty.start.Props.Prop;
+import org.eclipse.jetty.start.UsageException;
+
+/**
+ * Configuration Source representing the Command Line arguments.
+ */
+public class CommandLineConfigSource implements ConfigSource
+{
+    public static final String ORIGIN_INTERNAL_FALLBACK = "<internal-fallback>";
+    public static final String ORIGIN_CMD_LINE = "<command-line>";
+
+    private final List<String> args;
+    private final Props props;
+    private final Path homePath;
+    private final Path basePath;
+
+    public CommandLineConfigSource(String rawargs[])
+    {
+        this.args = Arrays.asList(rawargs);
+        this.props = new Props();
+        for (String arg : args)
+        {
+            this.props.addPossibleProperty(arg,ORIGIN_CMD_LINE);
+        }
+
+        // Setup ${jetty.base} and ${jetty.home}
+        this.homePath = findJettyHomePath().toAbsolutePath();
+        this.basePath = findJettyBasePath().toAbsolutePath();
+
+        // Update System Properties
+        setSystemProperty(BaseHome.JETTY_HOME,homePath.toAbsolutePath().toString());
+        setSystemProperty(BaseHome.JETTY_BASE,basePath.toAbsolutePath().toString());
+    }
+
+    private final Path findJettyBasePath()
+    {
+        // If a jetty property is defined, use it
+        Prop prop = this.props.getProp(BaseHome.JETTY_BASE,false);
+        if (prop != null && !isEmpty(prop.value))
+        {
+            return FS.toPath(prop.value);
+        }
+
+        // If a system property is defined, use it
+        String val = System.getProperty(BaseHome.JETTY_BASE);
+        if (!isEmpty(val))
+        {
+            return FS.toPath(val);
+        }
+
+        // Lastly, fall back to base == ${user.dir}
+        Path base = FS.toPath(this.props.getString("user.dir","."));
+        setProperty(BaseHome.JETTY_BASE,base.toString(),ORIGIN_INTERNAL_FALLBACK);
+        return base;
+    }
+
+    private final Path findJettyHomePath()
+    {
+        // If a jetty property is defined, use it
+        Prop prop = this.props.getProp(BaseHome.JETTY_HOME,false);
+        if (prop != null && !isEmpty(prop.value))
+        {
+            return FS.toPath(prop.value);
+        }
+
+        // If a system property is defined, use it
+        String val = System.getProperty(BaseHome.JETTY_HOME);
+        if (!isEmpty(val))
+        {
+            return FS.toPath(val);
+        }
+
+        // Attempt to find path relative to content in jetty's start.jar
+        // based on lookup for the Main class (from jetty's start.jar)
+        String classRef = "org/eclipse/jetty/start/Main.class";
+        URL jarfile = this.getClass().getClassLoader().getResource(classRef);
+        if (jarfile != null)
+        {
+            Matcher m = Pattern.compile("jar:(file:.*)!/" + classRef).matcher(jarfile.toString());
+            if (m.matches())
+            {
+                // ${jetty.home} is relative to found BaseHome class
+                try
+                {
+                    return new File(new URI(m.group(1))).getParentFile().toPath();
+                }
+                catch (URISyntaxException e)
+                {
+                    throw new UsageException(UsageException.ERR_UNKNOWN,e);
+                }
+            }
+        }
+
+        // Lastly, fall back to ${user.dir} default
+        Path home = FS.toPath(System.getProperty("user.dir","."));
+        setProperty(BaseHome.JETTY_HOME,home.toString(),ORIGIN_INTERNAL_FALLBACK);
+        return home;
+    }
+
+    private boolean isEmpty(String value)
+    {
+        if (value == null)
+        {
+            return true;
+        }
+        int len = value.length();
+        for (int i = 0; i < len; i++)
+        {
+            int c = value.codePointAt(i);
+            if (!Character.isWhitespace(c))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+        {
+            return true;
+        }
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        CommandLineConfigSource other = (CommandLineConfigSource)obj;
+        if (args == null)
+        {
+            if (other.args != null)
+            {
+                return false;
+            }
+        }
+        else if (!args.equals(other.args))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public List<String> getArgs()
+    {
+        return args;
+    }
+
+    public Path getBasePath()
+    {
+        return basePath;
+    }
+
+    public Path getHomePath()
+    {
+        return homePath;
+    }
+
+    @Override
+    public String getId()
+    {
+        return ORIGIN_CMD_LINE;
+    }
+
+    @Override
+    public String getProperty(String key)
+    {
+        return props.getString(key);
+    }
+
+    @Override
+    public Props getProps()
+    {
+        return props;
+    }
+
+    @Override
+    public int getWeight()
+    {
+        return -1; // default value for command line
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = (prime * result) + ((args == null)?0:args.hashCode());
+        return result;
+    }
+
+    public void setProperty(String key, String value, String origin)
+    {
+        this.props.setProperty(key,value,origin);
+    }
+
+    public void setSystemProperty(String key, String value)
+    {
+        this.props.setSystemProperty(key,value);
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s[%s,args.length=%d]",this.getClass().getSimpleName(),getId(),getArgs().size());
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/config/ConfigSource.java b/jetty-start/src/main/java/org/eclipse/jetty/start/config/ConfigSource.java
new file mode 100644
index 0000000..499013f
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/config/ConfigSource.java
@@ -0,0 +1,76 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.config;
+
+import java.util.List;
+
+import org.eclipse.jetty.start.Props;
+
+/**
+ * A Configuration Source
+ */
+public interface ConfigSource
+{
+    /**
+     * The identifier for this source.
+     * <p>
+     * Used in end-user display of the source.
+     * 
+     * @return the configuration source identifier.
+     */
+    public String getId();
+
+    /**
+     * The weight of this source, used for proper ordering of the config source search order.
+     * <p>
+     * Recommended Weights:
+     * <pre>
+     *           -1 = the command line
+     *            0 = the ${jetty.base} source
+     *       [1..n] = extra-start-dir entries from command line
+     *     [n+1..n] = extra-start-dir entries from start.ini (or start.d/*.ini) 
+     *      9999999 = the ${jetty.home} source
+     * </pre>
+     * 
+     * @return the weight of the config source. (lower value is more important)
+     */
+    public int getWeight();
+
+    /**
+     * The list of Arguments for this ConfigSource
+     * 
+     * @return the list of Arguments for this ConfigSource
+     */
+    public List<String> getArgs();
+
+    /**
+     * The properties for this ConfigSource
+     * 
+     * @return the properties for this ConfigSource
+     */
+    public Props getProps();
+    
+    /**
+     * Return the value of the specified property.
+     * 
+     * @param key the key to lookup
+     * @return the value of the property, or null if not found
+     */
+    public String getProperty(String key);
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/config/ConfigSources.java b/jetty-start/src/main/java/org/eclipse/jetty/start/config/ConfigSources.java
new file mode 100644
index 0000000..5f5843e
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/config/ConfigSources.java
@@ -0,0 +1,163 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.config;
+
+import static org.eclipse.jetty.start.UsageException.*;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jetty.start.FS;
+import org.eclipse.jetty.start.Props;
+import org.eclipse.jetty.start.Props.Prop;
+import org.eclipse.jetty.start.UsageException;
+
+/**
+ * Weighted List of ConfigSources.
+ * <p>
+ */
+public class ConfigSources implements Iterable<ConfigSource>
+{
+    private static class WeightedConfigSourceComparator implements Comparator<ConfigSource>
+    {
+        @Override
+        public int compare(ConfigSource o1, ConfigSource o2)
+        {
+            return o1.getWeight() - o2.getWeight();
+        }
+    }
+
+    private LinkedList<ConfigSource> sources = new LinkedList<>();
+    private Props props = new Props();
+    private AtomicInteger xtraSourceWeight = new AtomicInteger(1);
+
+    public void add(ConfigSource source) throws IOException
+    {
+        if (sources.contains(source))
+        {
+            // TODO: needs a better/more clear error message
+            throw new UsageException(ERR_BAD_ARG,"Duplicate Configuration Source Reference: " + source);
+        }
+        sources.add(source);
+
+        Collections.sort(sources,new WeightedConfigSourceComparator());
+
+        updateProps();
+
+        // look for --extra-start-dir entries
+        for (String arg : source.getArgs())
+        {
+            if (arg.startsWith("--extra-start-dir"))
+            {
+                String ref = getValue(arg);
+                String dirName = props.expand(ref);
+                Path dir = FS.toPath(dirName);
+                DirConfigSource dirsource = new DirConfigSource(ref,dir,xtraSourceWeight.incrementAndGet(),true);
+                add(dirsource);
+            }
+        }
+    }
+
+    public CommandLineConfigSource getCommandLineSource()
+    {
+        for (ConfigSource source : sources)
+        {
+            if (source instanceof CommandLineConfigSource)
+            {
+                return (CommandLineConfigSource)source;
+            }
+        }
+        return null;
+    }
+
+    public Prop getProp(String key)
+    {
+        return props.getProp(key);
+    }
+
+    public Props getProps()
+    {
+        return props;
+    }
+
+    private String getValue(String arg)
+    {
+        int idx = arg.indexOf('=');
+        if (idx == (-1))
+        {
+            throw new UsageException(ERR_BAD_ARG,"Argument is missing a required value: %s",arg);
+        }
+        String value = arg.substring(idx + 1).trim();
+        if (value.length() <= 0)
+        {
+            throw new UsageException(ERR_BAD_ARG,"Argument is missing a required value: %s",arg);
+        }
+        return value;
+    }
+
+    @Override
+    public Iterator<ConfigSource> iterator()
+    {
+        return sources.iterator();
+    }
+
+    public ListIterator<ConfigSource> reverseListIterator()
+    {
+        return sources.listIterator(sources.size());
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append(this.getClass().getSimpleName());
+        str.append('[');
+        boolean delim = false;
+        for (ConfigSource source : sources)
+        {
+            if (delim)
+            {
+                str.append(',');
+            }
+            str.append(source.getId());
+            delim = true;
+        }
+        str.append(']');
+        return str.toString();
+    }
+
+    private void updateProps()
+    {
+        props.reset();
+
+        // add all properties from config sources (in reverse order)
+        ListIterator<ConfigSource> iter = sources.listIterator(sources.size());
+        while (iter.hasPrevious())
+        {
+            ConfigSource source = iter.previous();
+            props.addAll(source.getProps());
+        }
+    }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/config/DirConfigSource.java b/jetty-start/src/main/java/org/eclipse/jetty/start/config/DirConfigSource.java
new file mode 100644
index 0000000..b3a818c
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/config/DirConfigSource.java
@@ -0,0 +1,255 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.config;
+
+import static org.eclipse.jetty.start.UsageException.*;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jetty.start.FS;
+import org.eclipse.jetty.start.NaturalSort;
+import org.eclipse.jetty.start.PathMatchers;
+import org.eclipse.jetty.start.Props;
+import org.eclipse.jetty.start.UsageException;
+import org.eclipse.jetty.start.Props.Prop;
+import org.eclipse.jetty.start.StartIni;
+import org.eclipse.jetty.start.StartLog;
+
+/**
+ * A Directory based {@link ConfigSource}.
+ * <p>
+ * Such as <code>${jetty.base}</code> or and <code>--extra-start-dir=[path]</code> sources.
+ */
+public class DirConfigSource implements ConfigSource
+{
+    private static final List<String> BANNED_ARGS;
+
+    static
+    {
+        // Arguments that are not allowed to be in start.ini or start.d/{name}.ini files
+        BANNED_ARGS = new ArrayList<>();
+        BANNED_ARGS.add("--help");
+        BANNED_ARGS.add("-?");
+        BANNED_ARGS.add("--stop");
+        BANNED_ARGS.add("--dry-run");
+        BANNED_ARGS.add("--exec-print");
+        BANNED_ARGS.add("--list-config");
+        BANNED_ARGS.add("--list-classpath");
+        BANNED_ARGS.add("--list-modules");
+        BANNED_ARGS.add("--write-module-graph");
+        BANNED_ARGS.add("--version");
+        BANNED_ARGS.add("-v");
+        BANNED_ARGS.add("--download");
+        BANNED_ARGS.add("--create-files");
+        BANNED_ARGS.add("--add-to-startd");
+        BANNED_ARGS.add("--add-to-start");
+    }
+
+    private final String id;
+    private final Path dir;
+    private final int weight;
+    private final List<String> args;
+    private final Props props;
+
+    /**
+     * Create DirConfigSource with specified identifier and directory.
+     * 
+     * @param id
+     *            the identifier for this {@link ConfigSource}
+     * @param dir
+     *            the directory for this {@link ConfigSource}
+     * @param weight
+     *            the configuration weight (used for search order)
+     * @param canHaveArgs
+     *            true if this directory can have start.ini or start.d entries. (false for directories like ${jetty.home}, for example)
+     * @throws IOException
+     *             if unable to load the configuration args
+     */
+    public DirConfigSource(String id, Path dir, int weight, boolean canHaveArgs) throws IOException
+    {
+        this.id = id;
+        this.dir = dir.toAbsolutePath();
+        this.weight = weight;
+        this.props = new Props();
+
+        this.args = new ArrayList<>();
+
+        if (canHaveArgs)
+        {
+            Path iniFile = dir.resolve("start.ini");
+            if (FS.canReadFile(iniFile))
+            {
+                StartIni ini = new StartIni(iniFile);
+                args.addAll(ini.getLines());
+                parseAllArgs(ini.getLines(),iniFile.toString());
+            }
+
+            Path startDdir = dir.resolve("start.d");
+
+            if (FS.canReadDirectory(startDdir))
+            {
+                DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>()
+                {
+                    PathMatcher iniMatcher = PathMatchers.getMatcher("glob:**/start.d/*.ini");
+
+                    @Override
+                    public boolean accept(Path entry) throws IOException
+                    {
+                        return iniMatcher.matches(entry);
+                    }
+                };
+
+                List<Path> paths = new ArrayList<>();
+
+                for (Path diniFile : Files.newDirectoryStream(startDdir,filter))
+                {
+                    if (FS.canReadFile(diniFile))
+                    {
+                        paths.add(diniFile);
+                    }
+                }
+
+                Collections.sort(paths,new NaturalSort.Paths());
+
+                for (Path diniFile : paths)
+                {
+                    StartLog.debug("Reading %s/start.d/%s - %s",id,diniFile.getFileName(),diniFile);
+                    StartIni ini = new StartIni(diniFile);
+                    args.addAll(ini.getLines());
+                    parseAllArgs(ini.getLines(),diniFile.toString());
+                }
+            }
+        }
+    }
+
+    private void parseAllArgs(List<String> lines, String origin)
+    {
+        for (String line : lines)
+        {
+            String arg = line;
+            int idx = line.indexOf('=');
+            if (idx > 0)
+            {
+                arg = line.substring(0,idx);
+            }
+            if (BANNED_ARGS.contains(arg))
+            {
+                throw new UsageException(ERR_BAD_ARG,"%s not allowed in %s",arg,origin);
+            }
+            this.props.addPossibleProperty(line,origin);
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+        {
+            return true;
+        }
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        DirConfigSource other = (DirConfigSource)obj;
+        if (dir == null)
+        {
+            if (other.dir != null)
+            {
+                return false;
+            }
+        }
+        else if (!dir.equals(other.dir))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public List<String> getArgs()
+    {
+        return args;
+    }
+
+    public Path getDir()
+    {
+        return dir;
+    }
+
+    @Override
+    public String getId()
+    {
+        return id;
+    }
+
+    @Override
+    public String getProperty(String key)
+    {
+        Prop prop = props.getProp(key,false);
+        if (prop == null)
+        {
+            return null;
+        }
+        return prop.value;
+    }
+
+    @Override
+    public Props getProps()
+    {
+        return props;
+    }
+
+    @Override
+    public int getWeight()
+    {
+        return weight;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = (prime * result) + ((dir == null)?0:dir.hashCode());
+        return result;
+    }
+
+    public boolean isPropertyBased()
+    {
+        return id.contains("${");
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("%s[%s,%s,args.length=%d]",this.getClass().getSimpleName(),id,dir,getArgs().size());
+    }
+}
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java b/jetty-start/src/main/java/org/eclipse/jetty/start/config/JettyBaseConfigSource.java
similarity index 61%
copy from jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
copy to jetty-start/src/main/java/org/eclipse/jetty/start/config/JettyBaseConfigSource.java
index 50a1e5b..ebac601 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/config/JettyBaseConfigSource.java
@@ -16,21 +16,21 @@
 //  ========================================================================
 //
 
-package org.eclipse.jetty.spdy.client;
+package org.eclipse.jetty.start.config;
 
-import org.eclipse.jetty.io.ClientConnectionFactory;
+import java.io.IOException;
+import java.nio.file.Path;
 
-public abstract class NegotiatingClientConnectionFactory implements ClientConnectionFactory
+/**
+ * ${jetty.base} specific ConfigSource
+ */
+public class JettyBaseConfigSource extends DirConfigSource
 {
-    private final ClientConnectionFactory connectionFactory;
-
-    protected NegotiatingClientConnectionFactory(ClientConnectionFactory connectionFactory)
+    // Standard weight for ${jetty.base}, so that it comes after command line, and before everything else 
+    private final static int WEIGHT = 0;
+    
+    public JettyBaseConfigSource(Path dir) throws IOException
     {
-        this.connectionFactory = connectionFactory;
-    }
-
-    public ClientConnectionFactory getClientConnectionFactory()
-    {
-        return connectionFactory;
+        super("${jetty.base}",dir,WEIGHT,true);
     }
 }
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java b/jetty-start/src/main/java/org/eclipse/jetty/start/config/JettyHomeConfigSource.java
similarity index 61%
copy from jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
copy to jetty-start/src/main/java/org/eclipse/jetty/start/config/JettyHomeConfigSource.java
index 50a1e5b..8ebe0bb 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NegotiatingClientConnectionFactory.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/config/JettyHomeConfigSource.java
@@ -16,21 +16,21 @@
 //  ========================================================================
 //
 
-package org.eclipse.jetty.spdy.client;
+package org.eclipse.jetty.start.config;
 
-import org.eclipse.jetty.io.ClientConnectionFactory;
+import java.io.IOException;
+import java.nio.file.Path;
 
-public abstract class NegotiatingClientConnectionFactory implements ClientConnectionFactory
+/**
+ * ${jetty.home} specific ConfigSource
+ */
+public class JettyHomeConfigSource extends DirConfigSource
 {
-    private final ClientConnectionFactory connectionFactory;
+    // Standard weight for ${jetty.home}, so that it comes after everything else
+    private final static int WEIGHT = 9999999;
 
-    protected NegotiatingClientConnectionFactory(ClientConnectionFactory connectionFactory)
+    public JettyHomeConfigSource(Path dir) throws IOException
     {
-        this.connectionFactory = connectionFactory;
-    }
-
-    public ClientConnectionFactory getClientConnectionFactory()
-    {
-        return connectionFactory;
+        super("${jetty.home}",dir,WEIGHT,false);
     }
 }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java
index ef3ac68..861972c 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java
@@ -22,9 +22,13 @@
 

 import java.io.File;

 import java.io.IOException;

+import java.nio.file.Path;

 import java.util.ArrayList;

 import java.util.List;

 

+import org.eclipse.jetty.start.config.ConfigSources;

+import org.eclipse.jetty.start.config.JettyBaseConfigSource;

+import org.eclipse.jetty.start.config.JettyHomeConfigSource;

 import org.eclipse.jetty.toolchain.test.IO;

 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;

 import org.junit.Assert;

@@ -32,7 +36,55 @@
 

 public class BaseHomeTest

 {

-    private void assertFileList(BaseHome hb, String message, List<String> expected, List<File> files)

+    public static void assertPathList(BaseHome hb, String message, List<String> expected, PathFinder finder)

+    {

+        List<String> actual = new ArrayList<>();

+        for (Path path : finder.getHits())

+        {

+            actual.add(hb.toShortForm(path.toFile()));

+        }

+

+        if (actual.size() != expected.size())

+        {

+            System.out.printf("Actual Path(s): %,d hits%n",actual.size());

+            for (String path : actual)

+            {

+                System.out.printf(" %s%n",path);

+            }

+            System.out.printf("Expected Path(s): %,d entries%n",expected.size());

+            for (String path : expected)

+            {

+                System.out.printf(" %s%n",path);

+            }

+        }

+        Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));

+    }

+    

+    public static void assertPathList(BaseHome hb, String message, List<String> expected, List<Path> paths)

+    {

+        List<String> actual = new ArrayList<>();

+        for (Path path : paths)

+        {

+            actual.add(hb.toShortForm(path.toFile()));

+        }

+

+        if (actual.size() != expected.size())

+        {

+            System.out.printf("Actual Path(s): %,d hits%n",actual.size());

+            for (String path : actual)

+            {

+                System.out.printf(" %s%n",path);

+            }

+            System.out.printf("Expected Path(s): %,d entries%n",expected.size());

+            for (String path : expected)

+            {

+                System.out.printf(" %s%n",path);

+            }

+        }

+        Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));

+    }

+

+    public static void assertFileList(BaseHome hb, String message, List<String> expected, List<File> files)

     {

         List<String> actual = new ArrayList<>();

         for (File file : files)

@@ -42,39 +94,34 @@
         Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray()));

     }

 

-    private void toOsSeparators(List<String> expected)

-    {

-        for (int i = 0; i < expected.size(); i++)

-        {

-            String fixed = FS.separators(expected.get(i));

-            expected.set(i,fixed);

-        }

-    }

-

     @Test

-    public void testGetFile_OnlyHome() throws IOException

+    public void testGetPath_OnlyHome() throws IOException

     {

         File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");

-        File baseDir = null;

+        

+        ConfigSources config = new ConfigSources();

+        config.add(new JettyHomeConfigSource(homeDir.toPath()));

 

-        BaseHome hb = new BaseHome(homeDir,baseDir);

-        File startIni = hb.getFile("/start.ini");

+        BaseHome hb = new BaseHome(config);

+        Path startIni = hb.getPath("start.ini");

 

         String ref = hb.toShortForm(startIni);

         Assert.assertThat("Reference",ref,startsWith("${jetty.home}"));

 

-        String contents = IO.readToString(startIni);

+        String contents = IO.readToString(startIni.toFile());

         Assert.assertThat("Contents",contents,containsString("Home Ini"));

     }

 

     @Test

-    public void testListFiles_OnlyHome() throws IOException

+    public void testGetPaths_OnlyHome() throws IOException

     {

         File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");

-        File baseDir = null;

 

-        BaseHome hb = new BaseHome(homeDir,baseDir);

-        List<File> files = hb.listFiles("/start.d");

+        ConfigSources config = new ConfigSources();

+        config.add(new JettyHomeConfigSource(homeDir.toPath()));

+

+        BaseHome hb = new BaseHome(config);

+        List<Path> paths = hb.getPaths("start.d/*");

 

         List<String> expected = new ArrayList<>();

         expected.add("${jetty.home}/start.d/jmx.ini");

@@ -82,19 +129,21 @@
         expected.add("${jetty.home}/start.d/jsp.ini");

         expected.add("${jetty.home}/start.d/logging.ini");

         expected.add("${jetty.home}/start.d/ssl.ini");

-        toOsSeparators(expected);

+        FSTest.toOsSeparators(expected);

 

-        assertFileList(hb,"Files found",expected,files);

+        assertPathList(hb,"Paths found",expected,paths);

     }

 

     @Test

-    public void testListFiles_Filtered_OnlyHome() throws IOException

+    public void testGetPaths_OnlyHome_InisOnly() throws IOException

     {

         File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");

-        File baseDir = null;

 

-        BaseHome hb = new BaseHome(homeDir,baseDir);

-        List<File> files = hb.listFiles("/start.d",new FS.IniFilter());

+        ConfigSources config = new ConfigSources();

+        config.add(new JettyHomeConfigSource(homeDir.toPath()));

+

+        BaseHome hb = new BaseHome(config);

+        List<Path> paths = hb.getPaths("start.d/*.ini");

 

         List<String> expected = new ArrayList<>();

         expected.add("${jetty.home}/start.d/jmx.ini");

@@ -102,19 +151,23 @@
         expected.add("${jetty.home}/start.d/jsp.ini");

         expected.add("${jetty.home}/start.d/logging.ini");

         expected.add("${jetty.home}/start.d/ssl.ini");

-        toOsSeparators(expected);

+        FSTest.toOsSeparators(expected);

 

-        assertFileList(hb,"Files found",expected,files);

+        assertPathList(hb,"Paths found",expected,paths);

     }

 

     @Test

-    public void testListFiles_Both() throws IOException

+    public void testGetPaths_Both() throws IOException

     {

         File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");

         File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base");

 

-        BaseHome hb = new BaseHome(homeDir,baseDir);

-        List<File> files = hb.listFiles("/start.d");

+        ConfigSources config = new ConfigSources();

+        config.add(new JettyBaseConfigSource(baseDir.toPath()));

+        config.add(new JettyHomeConfigSource(homeDir.toPath()));

+

+        BaseHome hb = new BaseHome(config);

+        List<Path> paths = hb.getPaths("start.d/*.ini");

 

         List<String> expected = new ArrayList<>();

         expected.add("${jetty.base}/start.d/jmx.ini");

@@ -123,9 +176,9 @@
         expected.add("${jetty.base}/start.d/logging.ini");

         expected.add("${jetty.home}/start.d/ssl.ini");

         expected.add("${jetty.base}/start.d/myapp.ini");

-        toOsSeparators(expected);

+        FSTest.toOsSeparators(expected);

 

-        assertFileList(hb,"Files found",expected,files);

+        assertPathList(hb,"Paths found",expected,paths);

     }

 

     @Test

@@ -137,18 +190,22 @@
     }

 

     @Test

-    public void testGetFile_Both() throws IOException

+    public void testGetPath_Both() throws IOException

     {

         File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");

         File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base");

 

-        BaseHome hb = new BaseHome(homeDir,baseDir);

-        File startIni = hb.getFile("/start.ini");

+        ConfigSources config = new ConfigSources();

+        config.add(new JettyBaseConfigSource(baseDir.toPath()));

+        config.add(new JettyHomeConfigSource(homeDir.toPath()));

+

+        BaseHome hb = new BaseHome(config);

+        Path startIni = hb.getPath("start.ini");

 

         String ref = hb.toShortForm(startIni);

         Assert.assertThat("Reference",ref,startsWith("${jetty.base}"));

 

-        String contents = IO.readToString(startIni);

+        String contents = IO.readToString(startIni.toFile());

         Assert.assertThat("Contents",contents,containsString("Base Ini"));

     }

 }

diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java
index f3ba3a8..ced1889 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java
@@ -25,6 +25,7 @@
 import java.io.IOException;

 import java.io.PrintWriter;

 import java.io.StringWriter;

+import java.nio.file.Path;

 import java.util.ArrayList;

 import java.util.Collection;

 import java.util.HashSet;

@@ -52,7 +53,7 @@
     {

         File testResourcesDir = MavenTestingUtils.getTestResourcesDir();

         File file = MavenTestingUtils.getTestResourceFile(filename);

-        TextFile textFile = new TextFile(file);

+        TextFile textFile = new TextFile(file.toPath());

 

         // Validate XMLs (order is important)

         List<String> expectedXmls = new ArrayList<>();

@@ -64,7 +65,7 @@
             }

         }

         List<String> actualXmls = new ArrayList<>();

-        for (File xml : args.getXmlFiles())

+        for (Path xml : args.getXmlFiles())

         {

             actualXmls.add(shorten(baseHome,xml,testResourcesDir));

         }

@@ -82,7 +83,7 @@
         List<String> actualLibs = new ArrayList<>();

         for (File path : args.getClasspath())

         {

-            actualLibs.add(shorten(baseHome,path,testResourcesDir));

+            actualLibs.add(shorten(baseHome,path.toPath(),testResourcesDir));

         }

         assertContainsUnordered("Libs",expectedLibs,actualLibs);

 

@@ -99,7 +100,8 @@
         for (Prop prop : args.getProperties())

         {

             String name = prop.key;

-            if ("jetty.home".equals(name) || "jetty.base".equals(name) || prop.origin.equals(Props.ORIGIN_SYSPROP))

+            if ("jetty.home".equals(name) || "jetty.base".equals(name) ||

+                "user.dir".equals(name) || prop.origin.equals(Props.ORIGIN_SYSPROP))

             {

                 // strip these out from assertion, to make assertions easier.

                 continue;

@@ -147,7 +149,7 @@
         assertContainsUnordered("Files/Dirs",expectedFiles,actualFiles);

     }

 

-    private static String shorten(BaseHome baseHome, File path, File testResourcesDir)

+    private static String shorten(BaseHome baseHome, Path path, File testResourcesDir)

     {

         String value = baseHome.toShortForm(path);

         if (value.startsWith(testResourcesDir.getAbsolutePath()))

@@ -206,7 +208,7 @@
         }

     }

 

-    private static void assertOrdered(String msg, List<String> expectedList, List<String> actualList)

+    public static void assertOrdered(String msg, List<String> expectedList, List<String> actualList)

     {

         // same size?

         boolean mismatch = expectedList.size() != actualList.size();

diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ExtraStartTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ExtraStartTest.java
new file mode 100644
index 0000000..0b41045
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ExtraStartTest.java
@@ -0,0 +1,559 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import static org.hamcrest.Matchers.*;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.start.Props.Prop;
+import org.eclipse.jetty.start.config.ConfigSource;
+import org.eclipse.jetty.start.config.ConfigSources;
+import org.eclipse.jetty.start.config.DirConfigSource;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class ExtraStartTest
+{
+    private static class MainResult
+    {
+        private Main main;
+        private StartArgs args;
+
+        public void assertSearchOrder(List<String> expectedSearchOrder)
+        {
+            ConfigSources sources = main.getBaseHome().getConfigSources();
+            List<String> actualOrder = new ArrayList<>();
+            for (ConfigSource source : sources)
+            {
+                if (source instanceof DirConfigSource)
+                {
+                    actualOrder.add(source.getId());
+                }
+            }
+            ConfigurationAssert.assertOrdered("Search Order",expectedSearchOrder,actualOrder);
+        }
+
+        public void assertProperty(String key, String expectedValue)
+        {
+            Prop prop = args.getProperties().getProp(key);
+            String prefix = "Prop[" + key + "]";
+            Assert.assertThat(prefix + " should have a value",prop,notNullValue());
+            Assert.assertThat(prefix + " value",prop.value,is(expectedValue));
+        }
+    }
+
+    @Rule
+    public TestingDir testdir = new TestingDir();
+
+    private MainResult runMain(File baseDir, File homeDir, String... cmdLineArgs) throws Exception
+    {
+        MainResult ret = new MainResult();
+        ret.main = new Main();
+        List<String> cmdLine = new ArrayList<>();
+        cmdLine.add("jetty.home=" + homeDir.getAbsolutePath());
+        cmdLine.add("jetty.base=" + baseDir.getAbsolutePath());
+        // cmdLine.add("--debug");
+        for (String arg : cmdLineArgs)
+        {
+            cmdLine.add(arg);
+        }
+        ret.args = ret.main.processCommandLine(cmdLine);
+        return ret;
+    }
+
+    @Test
+    public void testNoExtras() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1");
+
+        // Simple command line - no reference to extra-start-dirs
+        MainResult result = runMain(base,home);
+
+        List<String> expectedSearchOrder = new ArrayList<>();
+        expectedSearchOrder.add("${jetty.base}");
+        expectedSearchOrder.add("${jetty.home}");
+        result.assertSearchOrder(expectedSearchOrder);
+
+        result.assertProperty("jetty.host","127.0.0.1");
+    }
+
+    @Test
+    public void testCommandLine_1Extra() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1");
+
+        // Simple command line reference to extra-start-dir
+        MainResult result = runMain(base,home,
+        // direct reference via path
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        List<String> expectedSearchOrder = new ArrayList<>();
+        expectedSearchOrder.add("${jetty.base}");
+        expectedSearchOrder.add(common.getAbsolutePath());
+        expectedSearchOrder.add("${jetty.home}");
+        result.assertSearchOrder(expectedSearchOrder);
+
+        result.assertProperty("jetty.host","127.0.0.1");
+        result.assertProperty("jetty.port","8080"); // from 'common'
+    }
+
+    @Test
+    public void testCommandLine_1Extra_FromSimpleProp() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1");
+
+        // Simple command line reference to extra-start-dir via property (also on command line)
+        MainResult result = runMain(base,home,
+        // property
+                "my.common=" + common.getAbsolutePath(),
+                // reference via property
+                "--extra-start-dir=${my.common}");
+
+        List<String> expectedSearchOrder = new ArrayList<>();
+        expectedSearchOrder.add("${jetty.base}");
+        expectedSearchOrder.add("${my.common}"); // should see property use
+        expectedSearchOrder.add("${jetty.home}");
+        result.assertSearchOrder(expectedSearchOrder);
+
+        result.assertProperty("jetty.host","127.0.0.1");
+        result.assertProperty("jetty.port","8080"); // from 'common'
+    }
+
+    @Test
+    public void testCommandLine_1Extra_FromPropPrefix() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create opt
+        File opt = testdir.getFile("opt");
+        FS.ensureEmpty(opt);
+
+        // Create common
+        File common = new File(opt,"common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1");
+
+        String dirRef = "${my.opt}" + File.separator + "common";
+
+        // Simple command line reference to extra-start-dir via property (also on command line)
+        MainResult result = runMain(base,home,
+        // property to 'opt' dir
+                "my.opt=" + opt.getAbsolutePath(),
+                // reference via property prefix
+                "--extra-start-dir=" + dirRef);
+
+        List<String> expectedSearchOrder = new ArrayList<>();
+        expectedSearchOrder.add("${jetty.base}");
+        expectedSearchOrder.add(dirRef); // should use property
+        expectedSearchOrder.add("${jetty.home}");
+        result.assertSearchOrder(expectedSearchOrder);
+
+        result.assertProperty("jetty.host","127.0.0.1");
+        result.assertProperty("jetty.port","8080"); // from 'common'
+    }
+
+    @Test
+    public void testCommandLine_1Extra_FromCompoundProp() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create opt
+        File opt = testdir.getFile("opt");
+        FS.ensureEmpty(opt);
+
+        // Create common
+        File common = new File(opt,"common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1");
+
+        String dirRef = "${my.opt}" + File.separator + "${my.dir}";
+
+        // Simple command line reference to extra-start-dir via property (also on command line)
+        MainResult result = runMain(base,home,
+        // property to 'opt' dir
+                "my.opt=" + opt.getAbsolutePath(),
+                // property to commmon dir name
+                "my.dir=common",
+                // reference via property prefix
+                "--extra-start-dir=" + dirRef);
+
+        List<String> expectedSearchOrder = new ArrayList<>();
+        expectedSearchOrder.add("${jetty.base}");
+        expectedSearchOrder.add(dirRef); // should use property
+        expectedSearchOrder.add("${jetty.home}");
+        result.assertSearchOrder(expectedSearchOrder);
+
+        result.assertProperty("jetty.host","127.0.0.1");
+        result.assertProperty("jetty.port","8080"); // from 'common'
+    }
+
+    @Test
+    public void testRefCommon() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        MainResult result = runMain(base,home);
+
+        List<String> expectedSearchOrder = new ArrayList<>();
+        expectedSearchOrder.add("${jetty.base}");
+        expectedSearchOrder.add(common.getAbsolutePath());
+        expectedSearchOrder.add("${jetty.home}");
+        result.assertSearchOrder(expectedSearchOrder);
+
+        result.assertProperty("jetty.host","127.0.0.1");
+        result.assertProperty("jetty.port","8080"); // from 'common'
+    }
+
+    @Test
+    public void testRefCommonAndCorp() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath(), //
+                "--extra-start-dir=" + corp.getAbsolutePath());
+
+        MainResult result = runMain(base,home);
+
+        List<String> expectedSearchOrder = new ArrayList<>();
+        expectedSearchOrder.add("${jetty.base}");
+        expectedSearchOrder.add(common.getAbsolutePath());
+        expectedSearchOrder.add(corp.getAbsolutePath());
+        expectedSearchOrder.add("${jetty.home}");
+        result.assertSearchOrder(expectedSearchOrder);
+
+        result.assertProperty("jetty.host","127.0.0.1");
+        result.assertProperty("jetty.port","8080"); // from 'common'
+    }
+
+    @Test
+    public void testRefCommonRefCorp() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+        TestEnv.makeFile(corp,"start.ini","jetty.port=9090");
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini", //
+                "--extra-start-dir=" + corp.getAbsolutePath(), //
+                "jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        MainResult result = runMain(base,home);
+
+        List<String> expectedSearchOrder = new ArrayList<>();
+        expectedSearchOrder.add("${jetty.base}");
+        expectedSearchOrder.add(common.getAbsolutePath());
+        expectedSearchOrder.add(corp.getAbsolutePath());
+        expectedSearchOrder.add("${jetty.home}");
+        result.assertSearchOrder(expectedSearchOrder);
+
+        result.assertProperty("jetty.host","127.0.0.1");
+        result.assertProperty("jetty.port","8080"); // from 'common'
+    }
+
+    @Test
+    public void testRefCommonRefCorp_FromSimpleProps() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+        TestEnv.makeFile(corp,"start.ini", //
+                "jetty.port=9090");
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini", //
+                "my.corp=" + corp.getAbsolutePath(), //
+                "--extra-start-dir=${my.corp}", //
+                "jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "my.common=" + common.getAbsolutePath(), //
+                "--extra-start-dir=${my.common}");
+
+        MainResult result = runMain(base,home);
+
+        List<String> expectedSearchOrder = new ArrayList<>();
+        expectedSearchOrder.add("${jetty.base}");
+        expectedSearchOrder.add("${my.common}");
+        expectedSearchOrder.add("${my.corp}");
+        expectedSearchOrder.add("${jetty.home}");
+        result.assertSearchOrder(expectedSearchOrder);
+
+        result.assertProperty("jetty.host","127.0.0.1");
+        result.assertProperty("jetty.port","8080"); // from 'common'
+    }
+
+    @Test
+    public void testRefCommonRefCorp_CmdLineRef() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create devops
+        File devops = testdir.getFile("devops");
+        FS.ensureEmpty(devops);
+        TestEnv.makeFile(devops,"start.ini", //
+                "--module=logging", //
+                "jetty.port=2222");
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+        TestEnv.makeFile(corp,"start.ini", //
+                "jetty.port=9090");
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini", //
+                "--extra-start-dir=" + corp.getAbsolutePath(), //
+                "jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        MainResult result = runMain(base,home,
+        // command line provided extra-start-dir ref
+                "--extra-start-dir=" + devops.getAbsolutePath());
+
+        List<String> expectedSearchOrder = new ArrayList<>();
+        expectedSearchOrder.add("${jetty.base}");
+        expectedSearchOrder.add(devops.getAbsolutePath());
+        expectedSearchOrder.add(common.getAbsolutePath());
+        expectedSearchOrder.add(corp.getAbsolutePath());
+        expectedSearchOrder.add("${jetty.home}");
+        result.assertSearchOrder(expectedSearchOrder);
+
+        result.assertProperty("jetty.host","127.0.0.1");
+        result.assertProperty("jetty.port","2222"); // from 'devops'
+    }
+
+    @Test
+    public void testRefCommonRefCorp_CmdLineProp() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+        TestEnv.makeFile(corp,"start.ini", //
+                "jetty.port=9090");
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini", //
+                "--extra-start-dir=" + corp.getAbsolutePath(), //
+                "jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        MainResult result = runMain(base,home,
+        // command line property should override all others
+                "jetty.port=7070");
+
+        List<String> expectedSearchOrder = new ArrayList<>();
+        expectedSearchOrder.add("${jetty.base}");
+        expectedSearchOrder.add(common.getAbsolutePath());
+        expectedSearchOrder.add(corp.getAbsolutePath());
+        expectedSearchOrder.add("${jetty.home}");
+        result.assertSearchOrder(expectedSearchOrder);
+
+        result.assertProperty("jetty.host","127.0.0.1");
+        result.assertProperty("jetty.port","7070"); // from command line
+    }
+
+    @Test
+    public void testBadDoubleRef() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+        TestEnv.makeFile(corp,"start.ini",
+        // standard property
+                "jetty.port=9090",
+                // INTENTIONAL BAD Reference (duplicate)
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        // Populate common
+        TestEnv.makeFile(common,"start.ini",
+        // standard property
+                "jetty.port=8080",
+                // reference to corp
+                "--extra-start-dir=" + corp.getAbsolutePath());
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        try
+        {
+            runMain(base,home);
+            Assert.fail("Should have thrown a UsageException");
+        }
+        catch (UsageException e)
+        {
+            Assert.assertThat("UsageException",e.getMessage(),containsString("Duplicate"));
+        }
+    }
+}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/FSTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/FSTest.java
new file mode 100644
index 0000000..4061851
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/FSTest.java
@@ -0,0 +1,62 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import java.io.File;
+import java.util.List;
+
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class FSTest
+{
+    @Test
+    public void testCanReadDirectory()
+    {
+        File targetDir = MavenTestingUtils.getTargetDir();
+        Assert.assertTrue("Can read dir: " + targetDir,FS.canReadDirectory(targetDir.toPath()));
+    }
+
+    @Test
+    public void testCanReadDirectory_NotDir()
+    {
+        File bogusFile = MavenTestingUtils.getTestResourceFile("bogus.xml");
+        Assert.assertFalse("Can read dir: " + bogusFile,FS.canReadDirectory(bogusFile.toPath()));
+    }
+
+    @Test
+    public void testCanReadFile()
+    {
+        File pom = MavenTestingUtils.getProjectFile("pom.xml");
+        Assert.assertTrue("Can read file: " + pom,FS.canReadFile(pom.toPath()));
+    }
+    
+    /**
+     * Utility method used by other test cases
+     */
+    public static void toOsSeparators(List<String> expected)
+    {
+        for (int i = 0; i < expected.size(); i++)
+        {
+            String fixed = FS.separators(expected.get(i));
+            expected.set(i,fixed);
+        }
+    }
+}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
index 222f760..a8003c3 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
@@ -18,28 +18,34 @@
 
 package org.eclipse.jetty.start;
 
+import static org.hamcrest.Matchers.*;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 
 public class MainTest
 {
-    private void addUseCasesHome(List<String> cmdLineArgs)
+    @Before
+    public void clearSystemProperties()
     {
-        File testJettyHome = MavenTestingUtils.getTestResourceDir("usecases/home");
-        cmdLineArgs.add("jetty.home=" + testJettyHome);
+        System.setProperty("jetty.home","");
+        System.setProperty("jetty.base","");
     }
-
+    
     @Test
     public void testBasicProcessing() throws Exception
     {
         List<String> cmdLineArgs = new ArrayList<>();
-        addUseCasesHome(cmdLineArgs);
+        File testJettyHome = MavenTestingUtils.getTestResourceDir("usecases/home").getAbsoluteFile();
+        cmdLineArgs.add("user.dir=" + testJettyHome);
+        cmdLineArgs.add("jetty.home=" + testJettyHome);
         cmdLineArgs.add("jetty.port=9090");
 
         Main main = new Main();
@@ -74,7 +80,8 @@
     public void testListConfig() throws Exception
     {
         List<String> cmdLineArgs = new ArrayList<>();
-        addUseCasesHome(cmdLineArgs);
+        File testJettyHome = MavenTestingUtils.getTestResourceDir("usecases/home");
+        cmdLineArgs.add("jetty.home=" + testJettyHome);
         cmdLineArgs.add("jetty.port=9090");
         cmdLineArgs.add("--list-config");
         // cmdLineArgs.add("--debug");
@@ -97,8 +104,10 @@
     {
         List<String> cmdLineArgs = new ArrayList<>();
 
-        addUseCasesHome(cmdLineArgs);
-
+        File homePath = MavenTestingUtils.getTestResourceDir("usecases/home").getAbsoluteFile();
+        cmdLineArgs.add("jetty.home=" + homePath);
+        cmdLineArgs.add("user.dir=" + homePath);
+        
         // JVM args
         cmdLineArgs.add("--exec");
         cmdLineArgs.add("-Xms1024m");
@@ -118,7 +127,9 @@
 
         StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
         BaseHome baseHome = main.getBaseHome();
-        System.err.println(args);
+        
+        Assert.assertThat("jetty.home", baseHome.getHome(), is(homePath.getAbsolutePath()));
+        Assert.assertThat("jetty.base", baseHome.getBase(), is(homePath.getAbsolutePath()));
 
         ConfigurationAssert.assertConfiguration(baseHome,args,"assert-home-with-jvm.txt");
     }
@@ -128,14 +139,17 @@
     {
         List<String> cmdLineArgs = new ArrayList<>();
 
-        File homePath = MavenTestingUtils.getTestResourceDir("jetty home with spaces");
+        File homePath = MavenTestingUtils.getTestResourceDir("jetty home with spaces").getAbsoluteFile();
+        cmdLineArgs.add("user.dir=" + homePath);
         cmdLineArgs.add("jetty.home=" + homePath);
 
         Main main = new Main();
         StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
         BaseHome baseHome = main.getBaseHome();
-        System.err.println(args);
-
+        
+        Assert.assertThat("jetty.home", baseHome.getHome(), is(homePath.getAbsolutePath()));
+        Assert.assertThat("jetty.base", baseHome.getBase(), is(homePath.getAbsolutePath()));
+        
         ConfigurationAssert.assertConfiguration(baseHome,args,"assert-home-with-spaces.txt");
     }
 }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java
index 92ad503..82db04c 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java
@@ -18,13 +18,16 @@
 
 package org.eclipse.jetty.start;
 
-import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.*;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
+import java.nio.file.Path;
 
+import org.eclipse.jetty.start.config.CommandLineConfigSource;
+import org.eclipse.jetty.start.config.ConfigSources;
+import org.eclipse.jetty.start.config.JettyBaseConfigSource;
+import org.eclipse.jetty.start.config.JettyHomeConfigSource;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
 import org.eclipse.jetty.toolchain.test.TestingDir;
 import org.junit.Assert;
@@ -33,30 +36,39 @@
 
 public class ModuleGraphWriterTest
 {
-    @SuppressWarnings("unused")
-    private final static List<String> TEST_SOURCE = Collections.singletonList("<test>");
-    
-    private StartArgs DEFAULT_ARGS = new StartArgs(new String[]{"jetty.version=TEST"}).parseCommandLine();
-
     @Rule
     public TestingDir testdir = new TestingDir();
 
     @Test
     public void testGenerate_NothingEnabled() throws IOException
     {
+        // Test Env
         File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
         File baseDir = testdir.getEmptyDir();
-        BaseHome basehome = new BaseHome(homeDir,baseDir);
+        String cmdLine[] = new String[] {"jetty.version=TEST"};
+        
+        // Configuration
+        CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
+        ConfigSources config = new ConfigSources();
+        config.add(cmdLineSource);
+        config.add(new JettyHomeConfigSource(homeDir.toPath()));
+        config.add(new JettyBaseConfigSource(baseDir.toPath()));
+        
+        // Initialize
+        BaseHome basehome = new BaseHome(config);
+        
+        StartArgs args = new StartArgs();
+        args.parse(config);
 
         Modules modules = new Modules();
-        modules.registerAll(basehome, DEFAULT_ARGS);
+        modules.registerAll(basehome, args);
         modules.buildGraph();
 
-        File outputFile = new File(baseDir,"graph.dot");
+        Path outputFile = basehome.getBasePath("graph.dot");
 
         ModuleGraphWriter writer = new ModuleGraphWriter();
         writer.write(modules,outputFile);
 
-        Assert.assertThat("Output File Exists",outputFile.exists(),is(true));
+        Assert.assertThat("Output File Exists",FS.exists(outputFile),is(true));
     }
 }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleTest.java
index 31b2e4d..ced2d38 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleTest.java
@@ -18,36 +18,53 @@
 
 package org.eclipse.jetty.start;
 
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.*;
 
 import java.io.File;
 import java.io.IOException;
 
+import org.eclipse.jetty.start.config.CommandLineConfigSource;
+import org.eclipse.jetty.start.config.ConfigSources;
+import org.eclipse.jetty.start.config.JettyBaseConfigSource;
+import org.eclipse.jetty.start.config.JettyHomeConfigSource;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.TestingDir;
 import org.junit.Assert;
+import org.junit.Rule;
 import org.junit.Test;
 
 public class ModuleTest
 {
-    private Module loadTestHomeModule(String moduleFileName) throws IOException
-    {
-        File file = MavenTestingUtils.getTestResourceFile("usecases/home/modules/" + moduleFileName);
-        return new Module(new BaseHome(),file);
-    }
-
+    @Rule
+    public TestingDir testdir = new TestingDir();
+    
     @Test
     public void testLoadWebSocket() throws IOException
     {
-        Module Module = loadTestHomeModule("websocket.mod");
-
-        Assert.assertThat("Module Name",Module.getName(),is("websocket"));
-        Assert.assertThat("Module Parents Size",Module.getParentNames().size(),is(2));
-        Assert.assertThat("Module Parents",Module.getParentNames(),containsInAnyOrder("annotations","server"));
-        Assert.assertThat("Module Xmls Size",Module.getXmls().size(),is(1));
-        Assert.assertThat("Module Xmls",Module.getXmls(),contains("etc/jetty-websockets.xml"));
-        Assert.assertThat("Module Options Size",Module.getLibs().size(),is(1));
-        Assert.assertThat("Module Options",Module.getLibs(),contains("lib/websocket/*.jar"));
+        // Test Env
+        File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
+        File baseDir = testdir.getEmptyDir();
+        String cmdLine[] = new String[] {"jetty.version=TEST"};
+        
+        // Configuration
+        CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
+        ConfigSources config = new ConfigSources();
+        config.add(cmdLineSource);
+        config.add(new JettyHomeConfigSource(homeDir.toPath()));
+        config.add(new JettyBaseConfigSource(baseDir.toPath()));
+        
+        // Initialize
+        BaseHome basehome = new BaseHome(config);
+        
+        File file = MavenTestingUtils.getTestResourceFile("usecases/home/modules/websocket.mod");
+        Module module = new Module(basehome,file.toPath());
+        
+        Assert.assertThat("Module Name",module.getName(),is("websocket"));
+        Assert.assertThat("Module Parents Size",module.getParentNames().size(),is(2));
+        Assert.assertThat("Module Parents",module.getParentNames(),containsInAnyOrder("annotations","server"));
+        Assert.assertThat("Module Xmls Size",module.getXmls().size(),is(1));
+        Assert.assertThat("Module Xmls",module.getXmls(),contains("etc/jetty-websockets.xml"));
+        Assert.assertThat("Module Options Size",module.getLibs().size(),is(1));
+        Assert.assertThat("Module Options",module.getLibs(),contains("lib/websocket/*.jar"));
     }
 }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java
index 27d5726..4b2fd23 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java
@@ -27,50 +27,121 @@
 import java.util.Collections;
 import java.util.List;
 
+import org.eclipse.jetty.start.config.CommandLineConfigSource;
+import org.eclipse.jetty.start.config.ConfigSources;
+import org.eclipse.jetty.start.config.JettyBaseConfigSource;
+import org.eclipse.jetty.start.config.JettyHomeConfigSource;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.TestingDir;
 import org.junit.Assert;
+import org.junit.Rule;
 import org.junit.Test;
 
 public class ModulesTest
 {
     private final static List<String> TEST_SOURCE = Collections.singletonList("<test>");
-    private StartArgs DEFAULT_ARGS = new StartArgs(new String[] { "jetty.version=TEST" }).parseCommandLine();
-    
+
+    @Rule
+    public TestingDir testdir = new TestingDir();
+
     @Test
     public void testLoadAllModules() throws IOException
     {
+        // Test Env
         File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
-        BaseHome basehome = new BaseHome(homeDir,homeDir);
+        File baseDir = testdir.getEmptyDir();
+        String cmdLine[] = new String[] {"jetty.version=TEST"};
+        
+        // Configuration
+        CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
+        ConfigSources config = new ConfigSources();
+        config.add(cmdLineSource);
+        config.add(new JettyHomeConfigSource(homeDir.toPath()));
+        config.add(new JettyBaseConfigSource(baseDir.toPath()));
+        
+        // Initialize
+        BaseHome basehome = new BaseHome(config);
+        
+        StartArgs args = new StartArgs();
+        args.parse(config);
 
+        // Test Modules
         Modules modules = new Modules();
-        modules.registerAll(basehome, DEFAULT_ARGS);
-        Assert.assertThat("Module count",modules.count(),is(30));
+        modules.registerAll(basehome,args);
+
+        List<String> moduleNames = new ArrayList<>();
+        for (Module mod : modules)
+        {
+            // skip npn-boot in this test (as its behavior is jdk specific)
+            if (mod.getName().equals("npn-boot"))
+            {
+                continue;
+            }
+            moduleNames.add(mod.getName());
+        }
+
+        String expected[] = { "jmx", "client", "stats", "spdy", "deploy", "debug", "security", "npn", "ext", "websocket", "rewrite", "ipaccess", "xinetd",
+                "proxy", "webapp", "jndi", "lowresources", "https", "plus", "requestlog", "jsp", "monitor", "xml", "servlet", "jaas", "http", "base", "server",
+                "annotations" };
+
+        Assert.assertThat("Module count: " + moduleNames,moduleNames.size(),is(expected.length));
     }
-    
+
     @Test
     public void testEnableRegexSimple() throws IOException
     {
+        // Test Env
         File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
-        BaseHome basehome = new BaseHome(homeDir,homeDir);
+        File baseDir = testdir.getEmptyDir();
+        String cmdLine[] = new String[] {"jetty.version=TEST"};
+        
+        // Configuration
+        CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
+        ConfigSources config = new ConfigSources();
+        config.add(cmdLineSource);
+        config.add(new JettyHomeConfigSource(homeDir.toPath()));
+        config.add(new JettyBaseConfigSource(baseDir.toPath()));
+        
+        // Initialize
+        BaseHome basehome = new BaseHome(config);
+        
+        StartArgs args = new StartArgs();
+        args.parse(config);
 
+        // Test Modules
         Modules modules = new Modules();
-        modules.registerAll(basehome, DEFAULT_ARGS);
+        modules.registerAll(basehome,args);
         modules.enable("[sj]{1}.*",TEST_SOURCE);
-        
+
         String expected[] = { "jmx", "stats", "spdy", "security", "jndi", "jsp", "servlet", "jaas", "server" };
-        
+
         Assert.assertThat("Enabled Module count",modules.resolveEnabled().size(),is(expected.length));
     }
 
     @Test
     public void testResolve_ServerHttp() throws IOException
     {
+        // Test Env
         File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
-        BaseHome basehome = new BaseHome(homeDir,homeDir);
+        File baseDir = testdir.getEmptyDir();
+        String cmdLine[] = new String[] {"jetty.version=TEST"};
+        
+        // Configuration
+        CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
+        ConfigSources config = new ConfigSources();
+        config.add(cmdLineSource);
+        config.add(new JettyHomeConfigSource(homeDir.toPath()));
+        config.add(new JettyBaseConfigSource(baseDir.toPath()));
+        
+        // Initialize
+        BaseHome basehome = new BaseHome(config);
+        
+        StartArgs args = new StartArgs();
+        args.parse(config);
 
-        // Register modules
+        // Test Modules
         Modules modules = new Modules();
-        modules.registerAll(basehome, DEFAULT_ARGS);
+        modules.registerAll(basehome,args);
         modules.buildGraph();
 
         // Enable 2 modules
@@ -108,12 +179,12 @@
 
         List<String> actualLibs = modules.normalizeLibs(active);
         Assert.assertThat("Resolved Libs: " + actualLibs,actualLibs,contains(expectedLibs.toArray()));
-        
+
         // Assert XML List
         List<String> expectedXmls = new ArrayList<>();
         expectedXmls.add("etc/jetty.xml");
         expectedXmls.add("etc/jetty-http.xml");
-        
+
         List<String> actualXmls = modules.normalizeXmls(active);
         Assert.assertThat("Resolved XMLs: " + actualXmls,actualXmls,contains(expectedXmls.toArray()));
     }
@@ -121,12 +192,27 @@
     @Test
     public void testResolve_WebSocket() throws IOException
     {
+        // Test Env
         File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
-        BaseHome basehome = new BaseHome(homeDir,homeDir);
+        File baseDir = testdir.getEmptyDir();
+        String cmdLine[] = new String[] {"jetty.version=TEST"};
+        
+        // Configuration
+        CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
+        ConfigSources config = new ConfigSources();
+        config.add(cmdLineSource);
+        config.add(new JettyHomeConfigSource(homeDir.toPath()));
+        config.add(new JettyBaseConfigSource(baseDir.toPath()));
+        
+        // Initialize
+        BaseHome basehome = new BaseHome(config);
+        
+        StartArgs args = new StartArgs();
+        args.parse(config);
 
-        // Register modules
+        // Test Modules
         Modules modules = new Modules();
-        modules.registerAll(basehome, DEFAULT_ARGS);
+        modules.registerAll(basehome,args);
         modules.buildGraph();
         // modules.dump();
 
@@ -136,7 +222,7 @@
 
         // Collect active module list
         List<Module> active = modules.resolveEnabled();
-        
+
         // Assert names are correct, and in the right order
         List<String> expectedNames = new ArrayList<>();
         expectedNames.add("base");
@@ -174,10 +260,10 @@
         expectedLibs.add("lib/jetty-annotations-${jetty.version}.jar");
         expectedLibs.add("lib/annotations/*.jar");
         expectedLibs.add("lib/websocket/*.jar");
-        
+
         List<String> actualLibs = modules.normalizeLibs(active);
         Assert.assertThat("Resolved Libs: " + actualLibs,actualLibs,contains(expectedLibs.toArray()));
-        
+
         // Assert XML List
         List<String> expectedXmls = new ArrayList<>();
         expectedXmls.add("etc/jetty.xml");
@@ -185,7 +271,7 @@
         expectedXmls.add("etc/jetty-plus.xml");
         expectedXmls.add("etc/jetty-annotations.xml");
         expectedXmls.add("etc/jetty-websockets.xml");
-        
+
         List<String> actualXmls = modules.normalizeXmls(active);
         Assert.assertThat("Resolved XMLs: " + actualXmls,actualXmls,contains(expectedXmls.toArray()));
     }
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/PathFinderTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/PathFinderTest.java
new file mode 100644
index 0000000..18b05cd
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/PathFinderTest.java
@@ -0,0 +1,88 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.junit.Test;
+
+public class PathFinderTest
+{
+    @Test
+    public void testFindInis() throws IOException
+    {
+        File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home");
+        Path homePath = homeDir.toPath().toAbsolutePath();
+
+        PathFinder finder = new PathFinder();
+        finder.setFileMatcher("glob:**/*.ini");
+        finder.setBase(homePath);
+
+        Files.walkFileTree(homePath,EnumSet.of(FileVisitOption.FOLLOW_LINKS),30,finder);
+
+        List<String> expected = new ArrayList<>();
+        expected.add("${jetty.home}/start.d/jmx.ini");
+        expected.add("${jetty.home}/start.d/jndi.ini");
+        expected.add("${jetty.home}/start.d/jsp.ini");
+        expected.add("${jetty.home}/start.d/logging.ini");
+        expected.add("${jetty.home}/start.d/ssl.ini");
+        expected.add("${jetty.home}/start.ini");
+        FSTest.toOsSeparators(expected);
+
+        BaseHome hb = new BaseHome(new String[] { "jetty.home=" + homePath.toString() });
+        BaseHomeTest.assertPathList(hb,"Files found",expected,finder);
+    }
+
+    @Test
+    public void testFindMods() throws IOException
+    {
+        File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
+        Path homePath = homeDir.toPath().toAbsolutePath();
+
+        List<String> expected = new ArrayList<>();
+        File modulesDir = new File(homeDir,"modules");
+        for (File file : modulesDir.listFiles())
+        {
+            if (file.getName().endsWith(".mod"))
+            {
+                expected.add("${jetty.home}/modules/" + file.getName());
+            }
+        }
+        FSTest.toOsSeparators(expected);
+        
+        Path modulesPath = modulesDir.toPath();
+
+        PathFinder finder = new PathFinder();
+        finder.setFileMatcher(PathMatchers.getMatcher("modules/*.mod"));
+        finder.setBase(modulesPath);
+        
+        Files.walkFileTree(modulesPath,EnumSet.of(FileVisitOption.FOLLOW_LINKS),1,finder);
+
+        BaseHome hb = new BaseHome(new String[] { "jetty.home=" + homePath.toString() });
+        BaseHomeTest.assertPathList(hb,"Files found",expected,finder);
+    }
+}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersTest.java
new file mode 100644
index 0000000..460f88d
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersTest.java
@@ -0,0 +1,105 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import static org.hamcrest.Matchers.*;
+
+import java.nio.file.Path;
+
+import org.eclipse.jetty.toolchain.test.OS;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PathMatchersTest
+{
+    private void assertIsAbsolute(String pattern, boolean expected)
+    {
+        Assert.assertThat("isAbsolute(\"" + pattern + "\")",PathMatchers.isAbsolute(pattern),is(expected));
+    }
+
+    @Test
+    public void testIsAbsolute()
+    {
+        if (OS.IS_UNIX)
+        {
+            assertIsAbsolute("/opt/app",true);
+            assertIsAbsolute("/opt/florb",true);
+            assertIsAbsolute("/home/user/benfranklin",true);
+            assertIsAbsolute("glob:/home/user/benfranklin/*.jar",true);
+            assertIsAbsolute("glob:/**/*.jar",true);
+            assertIsAbsolute("regex:/*-[^dev].ini",true);
+        }
+
+        if (OS.IS_WINDOWS)
+        {
+            assertIsAbsolute("C:\\\\System32",true);
+            assertIsAbsolute("C:\\\\Program Files",true);
+        }
+    }
+
+    @Test
+    public void testIsNotAbsolute()
+    {
+        assertIsAbsolute("etc",false);
+        assertIsAbsolute("lib",false);
+        assertIsAbsolute("${user.dir}",false);
+        assertIsAbsolute("**/*.jar",false);
+        assertIsAbsolute("glob:*.ini",false);
+        assertIsAbsolute("regex:*-[^dev].ini",false);
+    }
+
+    private void assertSearchRoot(String pattern, String expectedSearchRoot)
+    {
+        Path actual = PathMatchers.getSearchRoot(pattern);
+        String expectedNormal = FS.separators(expectedSearchRoot);
+        Assert.assertThat(".getSearchRoot(\"" + pattern + "\")",actual.toString(),is(expectedNormal));
+    }
+
+    @Test
+    public void testGetSearchRoot()
+    {
+        if (OS.IS_UNIX)
+        {
+            // absolute first
+            assertSearchRoot("/opt/app/*.jar","/opt/app");
+            assertSearchRoot("/lib/jvm/**/jre/lib/*.jar","/lib/jvm");
+            assertSearchRoot("glob:/var/lib/*.xml","/var/lib");
+            assertSearchRoot("glob:/var/lib/*.{xml,java}","/var/lib");
+            assertSearchRoot("glob:/opt/corporate/lib-{dev,prod}/*.ini","/opt/corporate");
+            assertSearchRoot("regex:/opt/jetty/.*/lib-(dev|prod)/*.ini","/opt/jetty");
+
+            assertSearchRoot("/*.ini","/");
+            assertSearchRoot("/etc/jetty.conf","/etc");
+            assertSearchRoot("/common.conf","/");
+        }
+
+        if (OS.IS_WINDOWS)
+        {
+            // absolute patterns (complete with required windows slash escaping)
+            assertSearchRoot("C:\\\\corp\\\\lib\\\\*.jar","C:\\corp\\lib");
+            assertSearchRoot("D:\\\\lib\\\\**\\\\jre\\\\lib\\\\*.jar","C:\\lib");
+        }
+
+        // some relative paths
+        assertSearchRoot("lib/*.jar","lib");
+        assertSearchRoot("etc/jetty.xml","etc");
+        assertSearchRoot("start.ini",".");
+        assertSearchRoot("start.d/",".");
+    }
+}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/PropsTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/PropsTest.java
index b9f471f..7837764 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/PropsTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/PropsTest.java
@@ -91,6 +91,7 @@
         assertThat(props.expand("port=8080"),is("port=8080"));
         assertThat(props.expand("jdk=${java.version}"),is("jdk=" + System.getProperty("java.version")));
         assertThat(props.expand("id=${name}-${version}"),is("id=jetty-9.1"));
+        assertThat(props.expand("id=${unknown}-${wibble}"),is("id=${unknown}-${wibble}"));
     }
 
     @Test
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/TestEnv.java b/jetty-start/src/test/java/org/eclipse/jetty/start/TestEnv.java
new file mode 100644
index 0000000..84659c2
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/TestEnv.java
@@ -0,0 +1,52 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.IO;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.OS;
+
+public class TestEnv
+{
+    public static void copyTestDir(String testResourceDir, File destDir) throws IOException
+    {
+        FS.ensureDirExists(destDir);
+        File srcDir = MavenTestingUtils.getTestResourceDir(testResourceDir);
+        IO.copyDir(srcDir,destDir);
+    }
+
+    public static void makeFile(File dir, String relFilePath, String... contents) throws IOException
+    {
+        File outputFile = new File(dir,OS.separators(relFilePath));
+        FS.ensureDirExists(outputFile.getParentFile());
+        try (FileWriter writer = new FileWriter(outputFile); PrintWriter out = new PrintWriter(writer))
+        {
+            for (String content : contents)
+            {
+                out.println(content);
+            }
+        }
+    }
+}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java b/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java
index ec419cf..5e0e3c8 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java
@@ -60,6 +60,12 @@
     {
         assertUseCase("home","base.jmx","assert-jmx.txt");
     }
+    
+    @Test
+    public void testWithExtraStartDir_Logging() throws Exception
+    {
+        assertUseCase("home","base.with.extra.start.dirs","assert-extra-start-dir-logging.txt");
+    }
 
     @Test
     public void testWithMissingNpnVersion() throws Exception
@@ -76,7 +82,6 @@
     @Test
     public void testWithSpdyBadNpnVersion() throws Exception
     {
-        //StartLog.enableDebug(); 
         assertUseCase("home","base.enable.spdy.bad.npn.version","assert-enable-spdy-bad-npn-version.txt","java.version=1.7.0_01");
     }
 
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/config/ConfigSourcesTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/config/ConfigSourcesTest.java
new file mode 100644
index 0000000..bc55cb7
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/config/ConfigSourcesTest.java
@@ -0,0 +1,599 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.start.config;
+
+import static org.hamcrest.Matchers.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.jetty.start.ConfigurationAssert;
+import org.eclipse.jetty.start.Props.Prop;
+import org.eclipse.jetty.start.TestEnv;
+import org.eclipse.jetty.start.UsageException;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class ConfigSourcesTest
+{
+    @Rule
+    public TestingDir testdir = new TestingDir();
+
+    private void assertIdOrder(ConfigSources sources, String... expectedOrder)
+    {
+        List<String> actualList = new ArrayList<>();
+        for (ConfigSource source : sources)
+        {
+            actualList.add(source.getId());
+        }
+        List<String> expectedList = Arrays.asList(expectedOrder);
+        ConfigurationAssert.assertOrdered("ConfigSources.id order",expectedList,actualList);
+    }
+
+    private void assertDirOrder(ConfigSources sources, File... expectedDirOrder)
+    {
+        List<String> actualList = new ArrayList<>();
+        for (ConfigSource source : sources)
+        {
+            if (source instanceof DirConfigSource)
+            {
+                actualList.add(((DirConfigSource)source).getDir().toString());
+            }
+        }
+        List<String> expectedList = new ArrayList<>();
+        for (File path : expectedDirOrder)
+        {
+            expectedList.add(path.getAbsolutePath());
+        }
+        ConfigurationAssert.assertOrdered("ConfigSources.dir order",expectedList,actualList);
+    }
+
+    private void assertProperty(ConfigSources sources, String key, String expectedValue)
+    {
+        Prop prop = sources.getProp(key);
+        Assert.assertThat("getProp('" + key + "') should not be null",prop,notNullValue());
+        Assert.assertThat("getProp('" + key + "')",prop.value,is(expectedValue));
+    }
+
+    @Test
+    public void testOrder_BasicConfig() throws IOException
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1");
+
+        ConfigSources sources = new ConfigSources();
+
+        String[] cmdLine = new String[0];
+        sources.add(new CommandLineConfigSource(cmdLine));
+        sources.add(new JettyBaseConfigSource(base.toPath()));
+        sources.add(new JettyHomeConfigSource(home.toPath()));
+
+        assertIdOrder(sources,"<command-line>","${jetty.base}","${jetty.home}");
+    }
+
+    @Test
+    public void testOrder_With1ExtraConfig() throws IOException
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        ConfigSources sources = new ConfigSources();
+
+        String[] cmdLine = new String[0];
+        sources.add(new CommandLineConfigSource(cmdLine));
+        sources.add(new JettyHomeConfigSource(home.toPath()));
+        sources.add(new JettyBaseConfigSource(base.toPath()));
+
+        assertIdOrder(sources,"<command-line>","${jetty.base}",common.getAbsolutePath(),"${jetty.home}");
+    }
+
+    @Test
+    public void testCommandLine_1Extra_FromSimpleProp() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1");
+
+        ConfigSources sources = new ConfigSources();
+
+        // Simple command line reference to extra-start-dir via property (also on command line)
+
+        String[] cmdLine = new String[] {
+                // property
+                "my.common=" + common.getAbsolutePath(),
+                // reference via property
+                "--extra-start-dir=${my.common}" };
+        
+        sources.add(new CommandLineConfigSource(cmdLine));
+        sources.add(new JettyHomeConfigSource(home.toPath()));
+        sources.add(new JettyBaseConfigSource(base.toPath()));
+
+        assertIdOrder(sources,"<command-line>","${jetty.base}","${my.common}","${jetty.home}");
+
+        assertDirOrder(sources,base,common,home);
+
+        assertProperty(sources,"jetty.host","127.0.0.1");
+        assertProperty(sources,"jetty.port","8080"); // from 'common'
+    }
+
+    @Test
+    public void testCommandLine_1Extra_FromPropPrefix() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create opt
+        File opt = testdir.getFile("opt");
+        FS.ensureEmpty(opt);
+
+        // Create common
+        File common = new File(opt,"common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1");
+
+        String dirRef = "${my.opt}" + File.separator + "common";
+
+        ConfigSources sources = new ConfigSources();
+
+        // Simple command line reference to extra-start-dir via property (also on command line)
+        String[] cmdLine = new String[] {
+                // property to 'opt' dir
+                "my.opt=" + opt.getAbsolutePath(),
+                // reference via property prefix
+                "--extra-start-dir=" + dirRef };
+        
+        sources.add(new CommandLineConfigSource(cmdLine));
+        sources.add(new JettyHomeConfigSource(home.toPath()));
+        sources.add(new JettyBaseConfigSource(base.toPath()));
+
+        assertIdOrder(sources,"<command-line>","${jetty.base}",dirRef,"${jetty.home}");
+
+        assertDirOrder(sources,base,common,home);
+
+        assertProperty(sources,"jetty.host","127.0.0.1");
+        assertProperty(sources,"jetty.port","8080"); // from 'common'
+    }
+
+    @Test
+    public void testCommandLine_1Extra_FromCompoundProp() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create opt
+        File opt = testdir.getFile("opt");
+        FS.ensureEmpty(opt);
+
+        // Create common
+        File common = new File(opt,"common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1");
+
+        String dirRef = "${my.opt}" + File.separator + "${my.dir}";
+
+        ConfigSources sources = new ConfigSources();
+
+        // Simple command line reference to extra-start-dir via property (also on command line)
+
+        String[] cmdLine = new String[] {
+                // property to 'opt' dir
+                "my.opt=" + opt.getAbsolutePath(),
+                // property to commmon dir name
+                "my.dir=common",
+                // reference via property prefix
+                "--extra-start-dir=" + dirRef };
+        
+        sources.add(new CommandLineConfigSource(cmdLine));
+        sources.add(new JettyHomeConfigSource(home.toPath()));
+        sources.add(new JettyBaseConfigSource(base.toPath()));
+
+        assertIdOrder(sources,"<command-line>","${jetty.base}",dirRef,"${jetty.home}");
+
+        assertDirOrder(sources,base,common,home);
+
+        assertProperty(sources,"jetty.host","127.0.0.1");
+        assertProperty(sources,"jetty.port","8080"); // from 'common'
+    }
+    
+    @Test
+    public void testRefCommon() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        ConfigSources sources = new ConfigSources();
+        
+        String cmdLine[] = new String[0];
+        sources.add(new CommandLineConfigSource(cmdLine));
+        sources.add(new JettyHomeConfigSource(home.toPath()));
+        sources.add(new JettyBaseConfigSource(base.toPath()));
+
+        assertIdOrder(sources,"<command-line>","${jetty.base}",common.getAbsolutePath(),"${jetty.home}");
+
+        assertDirOrder(sources,base,common,home);
+
+        assertProperty(sources,"jetty.host","127.0.0.1");
+        assertProperty(sources,"jetty.port","8080"); // from 'common'
+    }
+
+    @Test
+    public void testRefCommonAndCorp() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini","jetty.port=8080");
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath(), //
+                "--extra-start-dir=" + corp.getAbsolutePath());
+
+        ConfigSources sources = new ConfigSources();
+
+        String cmdLine[] = new String[0];
+        sources.add(new CommandLineConfigSource(cmdLine));
+        sources.add(new JettyHomeConfigSource(home.toPath()));
+        sources.add(new JettyBaseConfigSource(base.toPath()));
+
+        assertIdOrder(sources,"<command-line>","${jetty.base}",
+                common.getAbsolutePath(),
+                corp.getAbsolutePath(),
+                "${jetty.home}");
+
+        assertDirOrder(sources,base,common,corp,home);
+
+        assertProperty(sources,"jetty.host","127.0.0.1");
+        assertProperty(sources,"jetty.port","8080"); // from 'common'
+    }
+    
+    @Test
+    public void testRefCommonRefCorp() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+        TestEnv.makeFile(corp,"start.ini", //
+                "jetty.port=9090");
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini", //
+                "--extra-start-dir=" + corp.getAbsolutePath(), //
+                "jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        ConfigSources sources = new ConfigSources();
+
+        String cmdLine[] = new String[0];
+        sources.add(new CommandLineConfigSource(cmdLine));
+        sources.add(new JettyHomeConfigSource(home.toPath()));
+        sources.add(new JettyBaseConfigSource(base.toPath()));
+
+        assertIdOrder(sources,"<command-line>","${jetty.base}",
+                common.getAbsolutePath(),
+                corp.getAbsolutePath(),
+                "${jetty.home}");
+
+        assertDirOrder(sources,base,common,corp,home);
+
+        assertProperty(sources,"jetty.host","127.0.0.1");
+        assertProperty(sources,"jetty.port","8080"); // from 'common'
+    }
+    
+    @Test
+    public void testRefCommonRefCorp_FromSimpleProps() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+        TestEnv.makeFile(corp,"start.ini", //
+                "jetty.port=9090");
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini", //
+                "my.corp=" + corp.getAbsolutePath(), //
+                "--extra-start-dir=${my.corp}", //
+                "jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "my.common="+common.getAbsolutePath(), //
+                "--extra-start-dir=${my.common}");
+
+        ConfigSources sources = new ConfigSources();
+
+        String cmdLine[] = new String[0];
+        sources.add(new CommandLineConfigSource(cmdLine));
+        sources.add(new JettyHomeConfigSource(home.toPath()));
+        sources.add(new JettyBaseConfigSource(base.toPath()));
+
+        assertIdOrder(sources,"<command-line>",
+                "${jetty.base}",
+                "${my.common}",
+                "${my.corp}",
+                "${jetty.home}");
+
+        assertDirOrder(sources,base,common,corp,home);
+
+        assertProperty(sources,"jetty.host","127.0.0.1");
+        assertProperty(sources,"jetty.port","8080"); // from 'common'
+    }
+    
+    @Test
+    public void testRefCommonRefCorp_CmdLineRef() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create devops
+        File devops = testdir.getFile("devops");
+        FS.ensureEmpty(devops);
+        TestEnv.makeFile(devops,"start.ini", //
+                "--module=logging", //
+                "jetty.port=2222");
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+        TestEnv.makeFile(corp,"start.ini", //
+                "jetty.port=9090");
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini", //
+                "--extra-start-dir=" + corp.getAbsolutePath(), //
+                "jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        ConfigSources sources = new ConfigSources();
+        
+        String cmdLine[] = new String[]{
+                // command line provided extra-start-dir ref
+                "--extra-start-dir=" + devops.getAbsolutePath()};
+        sources.add(new CommandLineConfigSource(cmdLine));
+        sources.add(new JettyHomeConfigSource(home.toPath()));
+        sources.add(new JettyBaseConfigSource(base.toPath()));
+
+        assertIdOrder(sources,"<command-line>",
+                "${jetty.base}",
+                devops.getAbsolutePath(),
+                common.getAbsolutePath(),
+                corp.getAbsolutePath(),
+                "${jetty.home}");
+
+        assertDirOrder(sources,base,devops,common,corp,home);
+
+        assertProperty(sources,"jetty.host","127.0.0.1");
+        assertProperty(sources,"jetty.port","2222"); // from 'common'
+    }
+    
+    @Test
+    public void testRefCommonRefCorp_CmdLineProp() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+        TestEnv.makeFile(corp,"start.ini", //
+                "jetty.port=9090");
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+        TestEnv.makeFile(common,"start.ini", //
+                "--extra-start-dir=" + corp.getAbsolutePath(), //
+                "jetty.port=8080");
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        ConfigSources sources = new ConfigSources();
+        
+        String cmdLine[] = new String[]{
+             // command line property should override all others
+                "jetty.port=7070"
+        };
+        sources.add(new CommandLineConfigSource(cmdLine));
+        sources.add(new JettyHomeConfigSource(home.toPath()));
+        sources.add(new JettyBaseConfigSource(base.toPath()));
+
+        assertIdOrder(sources,"<command-line>","${jetty.base}",
+                common.getAbsolutePath(),
+                corp.getAbsolutePath(),
+                "${jetty.home}");
+
+        assertDirOrder(sources,base,common,corp,home);
+
+        assertProperty(sources,"jetty.host","127.0.0.1");
+        assertProperty(sources,"jetty.port","7070"); // from <command-line>
+    }
+    
+    @Test
+    public void testBadDoubleRef() throws Exception
+    {
+        // Create home
+        File home = testdir.getFile("home");
+        FS.ensureEmpty(home);
+        TestEnv.copyTestDir("usecases/home",home);
+
+        // Create common
+        File common = testdir.getFile("common");
+        FS.ensureEmpty(common);
+
+        // Create corp
+        File corp = testdir.getFile("corp");
+        FS.ensureEmpty(corp);
+        TestEnv.makeFile(corp,"start.ini", 
+                // standard property
+                "jetty.port=9090",
+                // INTENTIONAL BAD Reference (duplicate)
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        // Populate common
+        TestEnv.makeFile(common,"start.ini", 
+                // standard property
+                "jetty.port=8080",
+                // reference to corp
+                "--extra-start-dir=" + corp.getAbsolutePath());
+
+        // Create base
+        File base = testdir.getFile("base");
+        FS.ensureEmpty(base);
+        TestEnv.makeFile(base,"start.ini", //
+                "jetty.host=127.0.0.1",//
+                "--extra-start-dir=" + common.getAbsolutePath());
+
+        ConfigSources sources = new ConfigSources();
+
+        try
+        {
+            String cmdLine[] = new String[0];
+            sources.add(new CommandLineConfigSource(cmdLine));
+            sources.add(new JettyHomeConfigSource(home.toPath()));
+            sources.add(new JettyBaseConfigSource(base.toPath()));
+            
+            Assert.fail("Should have thrown a UsageException");
+        }
+        catch (UsageException e)
+        {
+            Assert.assertThat("UsageException",e.getMessage(),containsString("Duplicate"));
+        }
+    }
+}
diff --git a/jetty-start/src/test/resources/assert-home-with-jvm.txt b/jetty-start/src/test/resources/assert-home-with-jvm.txt
index b73061b..9774d97 100644
--- a/jetty-start/src/test/resources/assert-home-with-jvm.txt
+++ b/jetty-start/src/test/resources/assert-home-with-jvm.txt
@@ -1,38 +1,38 @@
 # The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty-jmx.xml
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-XML|${jetty.home}/etc/jetty-plus.xml
-XML|${jetty.home}/etc/jetty-annotations.xml
-XML|${jetty.home}/etc/jetty-websockets.xml
-XML|${jetty.home}/etc/jetty-logging.xml
+XML|${jetty.base}/etc/jetty-jmx.xml
+XML|${jetty.base}/etc/jetty.xml
+XML|${jetty.base}/etc/jetty-http.xml
+XML|${jetty.base}/etc/jetty-plus.xml
+XML|${jetty.base}/etc/jetty-annotations.xml
+XML|${jetty.base}/etc/jetty-websockets.xml
+XML|${jetty.base}/etc/jetty-logging.xml
 
 # The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/annotations/javax.annotation-api-1.2.jar
-LIB|${jetty.home}/lib/annotations/org.objectweb.asm-TEST.jar
-LIB|${jetty.home}/lib/jetty-annotations-TEST.jar
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-jmx-TEST.jar
-LIB|${jetty.home}/lib/jetty-jndi-TEST.jar
-LIB|${jetty.home}/lib/jetty-plus-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-security-TEST.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/jndi/javax.activation-1.1.jar
-LIB|${jetty.home}/lib/jndi/javax.transaction-api-1.2.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-LIB|${jetty.home}/lib/websocket/javax.websocket-api-1.0.jar
-LIB|${jetty.home}/lib/websocket/javax-websocket-client-impl-TEST.jar
-LIB|${jetty.home}/lib/websocket/javax-websocket-server-impl-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-api-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-client-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-common-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-server-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-servlet-TEST.jar
+LIB|${jetty.base}/lib/annotations/javax.annotation-api-1.2.jar
+LIB|${jetty.base}/lib/annotations/org.objectweb.asm-TEST.jar
+LIB|${jetty.base}/lib/jetty-annotations-TEST.jar
+LIB|${jetty.base}/lib/jetty-continuation-TEST.jar
+LIB|${jetty.base}/lib/jetty-http-TEST.jar
+LIB|${jetty.base}/lib/jetty-io-TEST.jar
+LIB|${jetty.base}/lib/jetty-jmx-TEST.jar
+LIB|${jetty.base}/lib/jetty-jndi-TEST.jar
+LIB|${jetty.base}/lib/jetty-plus-TEST.jar
+LIB|${jetty.base}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.base}/lib/jetty-security-TEST.jar
+LIB|${jetty.base}/lib/jetty-server-TEST.jar
+LIB|${jetty.base}/lib/jetty-util-TEST.jar
+LIB|${jetty.base}/lib/jetty-xml-TEST.jar
+LIB|${jetty.base}/lib/jndi/javax.activation-1.1.jar
+LIB|${jetty.base}/lib/jndi/javax.transaction-api-1.2.jar
+LIB|${jetty.base}/lib/servlet-api-3.1.jar
+LIB|${jetty.base}/lib/websocket/javax.websocket-api-1.0.jar
+LIB|${jetty.base}/lib/websocket/javax-websocket-client-impl-TEST.jar
+LIB|${jetty.base}/lib/websocket/javax-websocket-server-impl-TEST.jar
+LIB|${jetty.base}/lib/websocket/websocket-api-TEST.jar
+LIB|${jetty.base}/lib/websocket/websocket-client-TEST.jar
+LIB|${jetty.base}/lib/websocket/websocket-common-TEST.jar
+LIB|${jetty.base}/lib/websocket/websocket-server-TEST.jar
+LIB|${jetty.base}/lib/websocket/websocket-servlet-TEST.jar
 LIB|${maven-test-resources}/extra-resources
 LIB|${maven-test-resources}/extra-libs/example.jar
 
diff --git a/jetty-start/src/test/resources/assert-home-with-spaces.txt b/jetty-start/src/test/resources/assert-home-with-spaces.txt
index 01d429a..d63fcce 100644
--- a/jetty-start/src/test/resources/assert-home-with-spaces.txt
+++ b/jetty-start/src/test/resources/assert-home-with-spaces.txt
@@ -2,7 +2,7 @@
 # No XMLs in this home
 
 # The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/example of a library with spaces.jar
+LIB|${jetty.base}/lib/example of a library with spaces.jar
 
 # The Properties we expect (order is irrelevant)
 PROP|test.message=Hello
diff --git a/jetty-start/src/test/resources/assert-home.txt b/jetty-start/src/test/resources/assert-home.txt
index 327b770..5be8d46 100644
--- a/jetty-start/src/test/resources/assert-home.txt
+++ b/jetty-start/src/test/resources/assert-home.txt
@@ -1,37 +1,37 @@
 # The XMLs we expect (order is important)
-XML|${jetty.home}/etc/jetty-jmx.xml
-XML|${jetty.home}/etc/jetty.xml
-XML|${jetty.home}/etc/jetty-http.xml
-XML|${jetty.home}/etc/jetty-plus.xml
-XML|${jetty.home}/etc/jetty-annotations.xml
-XML|${jetty.home}/etc/jetty-websockets.xml
+XML|${jetty.base}/etc/jetty-jmx.xml
+XML|${jetty.base}/etc/jetty.xml
+XML|${jetty.base}/etc/jetty-http.xml
+XML|${jetty.base}/etc/jetty-plus.xml
+XML|${jetty.base}/etc/jetty-annotations.xml
+XML|${jetty.base}/etc/jetty-websockets.xml
 
 # The LIBs we expect (order is irrelevant)
-LIB|${jetty.home}/lib/annotations/javax.annotation-api-1.2.jar
-LIB|${jetty.home}/lib/annotations/org.objectweb.asm-TEST.jar
-LIB|${jetty.home}/lib/jetty-annotations-TEST.jar
-LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
-LIB|${jetty.home}/lib/jetty-http-TEST.jar
-LIB|${jetty.home}/lib/jetty-io-TEST.jar
-LIB|${jetty.home}/lib/jetty-jmx-TEST.jar
-LIB|${jetty.home}/lib/jetty-jndi-TEST.jar
-LIB|${jetty.home}/lib/jetty-plus-TEST.jar
-LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
-LIB|${jetty.home}/lib/jetty-security-TEST.jar
-LIB|${jetty.home}/lib/jetty-server-TEST.jar
-LIB|${jetty.home}/lib/jetty-util-TEST.jar
-LIB|${jetty.home}/lib/jetty-xml-TEST.jar
-LIB|${jetty.home}/lib/jndi/javax.activation-1.1.jar
-LIB|${jetty.home}/lib/jndi/javax.transaction-api-1.2.jar
-LIB|${jetty.home}/lib/servlet-api-3.1.jar
-LIB|${jetty.home}/lib/websocket/javax.websocket-api-1.0.jar
-LIB|${jetty.home}/lib/websocket/javax-websocket-client-impl-TEST.jar
-LIB|${jetty.home}/lib/websocket/javax-websocket-server-impl-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-api-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-client-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-common-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-server-TEST.jar
-LIB|${jetty.home}/lib/websocket/websocket-servlet-TEST.jar
+LIB|${jetty.base}/lib/annotations/javax.annotation-api-1.2.jar
+LIB|${jetty.base}/lib/annotations/org.objectweb.asm-TEST.jar
+LIB|${jetty.base}/lib/jetty-annotations-TEST.jar
+LIB|${jetty.base}/lib/jetty-continuation-TEST.jar
+LIB|${jetty.base}/lib/jetty-http-TEST.jar
+LIB|${jetty.base}/lib/jetty-io-TEST.jar
+LIB|${jetty.base}/lib/jetty-jmx-TEST.jar
+LIB|${jetty.base}/lib/jetty-jndi-TEST.jar
+LIB|${jetty.base}/lib/jetty-plus-TEST.jar
+LIB|${jetty.base}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.base}/lib/jetty-security-TEST.jar
+LIB|${jetty.base}/lib/jetty-server-TEST.jar
+LIB|${jetty.base}/lib/jetty-util-TEST.jar
+LIB|${jetty.base}/lib/jetty-xml-TEST.jar
+LIB|${jetty.base}/lib/jndi/javax.activation-1.1.jar
+LIB|${jetty.base}/lib/jndi/javax.transaction-api-1.2.jar
+LIB|${jetty.base}/lib/servlet-api-3.1.jar
+LIB|${jetty.base}/lib/websocket/javax.websocket-api-1.0.jar
+LIB|${jetty.base}/lib/websocket/javax-websocket-client-impl-TEST.jar
+LIB|${jetty.base}/lib/websocket/javax-websocket-server-impl-TEST.jar
+LIB|${jetty.base}/lib/websocket/websocket-api-TEST.jar
+LIB|${jetty.base}/lib/websocket/websocket-client-TEST.jar
+LIB|${jetty.base}/lib/websocket/websocket-common-TEST.jar
+LIB|${jetty.base}/lib/websocket/websocket-server-TEST.jar
+LIB|${jetty.base}/lib/websocket/websocket-servlet-TEST.jar
 
 # The Properties we expect (order is irrelevant)
 PROP|jetty.port=9090
diff --git a/jetty-start/src/test/resources/extra-start-dirs/logging/etc/jetty-logging.xml b/jetty-start/src/test/resources/extra-start-dirs/logging/etc/jetty-logging.xml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/jetty-start/src/test/resources/extra-start-dirs/logging/etc/jetty-logging.xml
diff --git a/jetty-start/src/test/resources/extra-start-dirs/logging/lib/logging/logback.jar b/jetty-start/src/test/resources/extra-start-dirs/logging/lib/logging/logback.jar
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/jetty-start/src/test/resources/extra-start-dirs/logging/lib/logging/logback.jar
diff --git a/jetty-start/src/test/resources/extra-start-dirs/logging/start.ini b/jetty-start/src/test/resources/extra-start-dirs/logging/start.ini
new file mode 100644
index 0000000..905d6db
--- /dev/null
+++ b/jetty-start/src/test/resources/extra-start-dirs/logging/start.ini
@@ -0,0 +1 @@
+--module=logging
\ No newline at end of file
diff --git a/jetty-start/src/test/resources/extra-start-dirs/more-startd/start.d/more.ini b/jetty-start/src/test/resources/extra-start-dirs/more-startd/start.d/more.ini
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/jetty-start/src/test/resources/extra-start-dirs/more-startd/start.d/more.ini
diff --git a/jetty-start/src/test/resources/usecases/assert-extra-start-dir-logging.txt b/jetty-start/src/test/resources/usecases/assert-extra-start-dir-logging.txt
new file mode 100644
index 0000000..def27d7
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/assert-extra-start-dir-logging.txt
@@ -0,0 +1,18 @@
+# The XMLs we expect (order is important)
+XML|${jetty.home}/etc/jetty-jmx.xml
+XML|${jetty.home}/etc/jetty.xml
+XML|${jetty.home}/etc/jetty-http.xml
+
+# The LIBs we expect (order is irrelevant)
+LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
+LIB|${jetty.home}/lib/jetty-http-TEST.jar
+LIB|${jetty.home}/lib/jetty-io-TEST.jar
+LIB|${jetty.home}/lib/jetty-jmx-TEST.jar
+LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
+LIB|${jetty.home}/lib/jetty-server-TEST.jar
+LIB|${jetty.home}/lib/jetty-util-TEST.jar
+LIB|${jetty.home}/lib/jetty-xml-TEST.jar
+LIB|${jetty.home}/lib/servlet-api-3.1.jar
+
+# The Properties we expect (order is irrelevant)
+PROP|jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/base.with.extra.start.dirs/start.ini b/jetty-start/src/test/resources/usecases/base.with.extra.start.dirs/start.ini
new file mode 100644
index 0000000..81e6d72
--- /dev/null
+++ b/jetty-start/src/test/resources/usecases/base.with.extra.start.dirs/start.ini
@@ -0,0 +1,5 @@
+
+--extra-start-dir=${start.basedir}/../../extra-start-dirs/logging
+--module=server,http,jmx
+
+jetty.port=9090
diff --git a/jetty-start/src/test/resources/usecases/home/modules/ext.mod b/jetty-start/src/test/resources/usecases/home/modules/ext.mod
index c84697a..66c0519 100644
--- a/jetty-start/src/test/resources/usecases/home/modules/ext.mod
+++ b/jetty-start/src/test/resources/usecases/home/modules/ext.mod
@@ -3,7 +3,7 @@
 #
 
 [lib]
-regex:lib/ext/.*\.jar$
+lib/ext/**.jar
 
 [files]
 lib/
diff --git a/jetty-util-ajax/pom.xml b/jetty-util-ajax/pom.xml
index 7fdea25..26c3422 100644
--- a/jetty-util-ajax/pom.xml
+++ b/jetty-util-ajax/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-util-ajax</artifactId>
diff --git a/jetty-util/pom.xml b/jetty-util/pom.xml
index 0b0f987..8850d89 100644
--- a/jetty-util/pom.xml
+++ b/jetty-util/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-util</artifactId>
diff --git a/jetty-util/src/main/config/modules/logging.mod b/jetty-util/src/main/config/modules/logging.mod
index 9d36e2e..a39bfe4 100644
--- a/jetty-util/src/main/config/modules/logging.mod
+++ b/jetty-util/src/main/config/modules/logging.mod
@@ -8,6 +8,10 @@
 [files]
 logs/
 
+[lib]
+lib/logging/**.jar
+resources/
+
 [ini-template]
 ## Logging Configuration
 # Configure jetty logging for default internal behavior STDERR output
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java
index b5e2dcc..16d3701 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java
@@ -575,6 +575,49 @@
             return minus ? (-val) : val;
         throw new NumberFormatException(toString(buffer));
     }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Convert buffer to an integer. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
+     *
+     * @param buffer
+     *            A buffer containing an integer in flush mode. The position is updated.
+     * @return an int
+     */
+    public static int takeInt(ByteBuffer buffer)
+    {
+        int val = 0;
+        boolean started = false;
+        boolean minus = false;
+        int i;
+        for (i = buffer.position(); i < buffer.limit(); i++)
+        {
+            byte b = buffer.get(i);
+            if (b <= SPACE)
+            {
+                if (started)
+                    break;
+            }
+            else if (b >= '0' && b <= '9')
+            {
+                val = val * 10 + (b - '0');
+                started = true;
+            }
+            else if (b == MINUS && !started)
+            {
+                minus = true;
+            }
+            else
+                break;
+        }
+
+        if (started)
+        {
+            buffer.position(i);
+            return minus ? (-val) : val;
+        }
+        throw new NumberFormatException(toString(buffer));
+    }
 
     /**
      * Convert buffer to an long. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringMap.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringMap.java
deleted file mode 100644
index 63f5cd7..0000000
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringMap.java
+++ /dev/null
@@ -1,196 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.util;
-
-import java.nio.ByteBuffer;
-import java.util.AbstractMap;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-/* ------------------------------------------------------------ */
-/** Map implementation Optimized for Strings keys..
- * This String Map has been optimized for mapping small sets of
- * Strings where the most frequently accessed Strings have been put to
- * the map first.
- *
- * It also has the benefit that it can look up entries by substring or
- * sections of char and byte arrays.  This can prevent many String
- * objects from being created just to look up in the map.
- *
- * This map is NOT synchronized.
- * @deprecated Use {@link Trie}
- */
-public class StringMap<O> extends AbstractMap<String,O>
-{
-    private final TreeMap<Object, O> _map;
-    
-    
-    public static final boolean CASE_INSENSTIVE=true;
-    
-    /* ------------------------------------------------------------ */
-
-    private final boolean _caseInsensitive;
-    
-    
-    /* ------------------------------------------------------------ */
-    /** Constructor. 
-     */
-    public StringMap()
-    {
-        this(false);
-    }
-    
-    /* ------------------------------------------------------------ */
-    /** Constructor. 
-     * @param ignoreCase 
-     */
-    public StringMap(final boolean ignoreCase)
-    {
-        _caseInsensitive=ignoreCase;
-        _map = new TreeMap<Object,O>(new Comparator<Object>()
-        {
-            @Override
-            public int compare(Object o1, Object o2)
-            {
-                String s1=(o1 instanceof String)?(String)o1:null;
-                ByteBuffer b1=(o1 instanceof ByteBuffer)?(ByteBuffer)o1:null;
-                if (s1==null && b1==null)
-                    s1=o1.toString();
-                String s2=(String)o2;
-                
-                int n1 = s1==null?b1.remaining():s1.length();
-                int n2 = s2.length();
-                int min = Math.min(n1, n2);
-                for (int i = 0; i < min; i++) {
-                    char c1 = s1==null?(char)b1.get(b1.position()+i):s1.charAt(i);
-                    char c2 = s2.charAt(i);
-                    if (c1 != c2) {
-                        if (ignoreCase)
-                        {
-                            c1 = Character.toUpperCase(c1);
-                            c2 = Character.toUpperCase(c2);
-                            if (c1 != c2) {
-                                c1 = Character.toLowerCase(c1);
-                                c2 = Character.toLowerCase(c2);
-                                if (c1 != c2) {
-                                    // No overflow because of numeric promotion
-                                    return c1 - c2;
-                                }
-                            }
-                        }
-                        else
-                            return c1 - c2;
-                    }
-                }
-                return n1 - n2;
-            }
-        });
-    }
-
-    /* ------------------------------------------------------------ */
-    public boolean isIgnoreCase()
-    {
-        return _caseInsensitive;
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public O put(String key, O value)
-    {
-        return _map.put(key,value);
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public O get(Object key)
-    {
-        return _map.get(key);
-    }
-    
-    /* ------------------------------------------------------------ */
-    public O get(String key)
-    {
-        return _map.get(key);
-    }
-    
-    /* ------------------------------------------------------------ */
-    public O get(String key,int offset,int length)
-    {
-        return _map.get(key.substring(offset,offset+length));
-    }
-    
-    /* ------------------------------------------------------------ */
-    public O get(ByteBuffer buffer)
-    {
-        return _map.get(buffer);
-    }
-    
-    /* ------------------------------------------------------------ */
-    @Override
-    public O remove(Object key)
-    {
-        return _map.remove(key);
-    }
-    
-    /* ------------------------------------------------------------ */
-    public O remove(String key)
-    {
-        return _map.remove(key);
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public Set<Map.Entry<String,O>> entrySet()
-    {
-        Object o=_map.entrySet();
-        return Collections.unmodifiableSet((Set<Map.Entry<String,O>>)o);
-    }
-    
-    /* ------------------------------------------------------------ */
-    @Override
-    public int size()
-    {
-        return _map.size();
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public boolean isEmpty()
-    {
-        return _map.isEmpty();
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public boolean containsKey(Object key)
-    {
-        return _map.containsKey(key);
-    }
-    
-    /* ------------------------------------------------------------ */
-    @Override
-    public void clear()
-    {
-        _map.clear();
-    }
-
-}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
index a826508..ef7f2f5 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
@@ -294,7 +294,7 @@
                     switch ((char)(0xff&b))
                     {
                         case '&':
-                            value = buffer.length()==0?"":buffer.toString();
+                            value = buffer.toReplacedString();
                             buffer.reset();
                             if (key != null)
                             {
@@ -314,7 +314,7 @@
                                 buffer.append(b);
                                 break;
                             }
-                            key = buffer.toString();
+                            key = buffer.toReplacedString();
                             buffer.reset();
                             break;
 
@@ -376,7 +376,7 @@
             
             if (key != null)
             {
-                value = buffer.length()==0?"":buffer.toReplacedString();
+                value = buffer.toReplacedString();
                 buffer.reset();
                 map.add(key,value);
             }
@@ -510,7 +510,7 @@
                     switch ((char) b)
                     {
                         case '&':
-                            value = buffer.length()==0?"":buffer.toString();
+                            value = buffer.toReplacedString();
                             buffer.reset();
                             if (key != null)
                             {
@@ -532,7 +532,7 @@
                                 buffer.append((byte)b);
                                 break;
                             }
-                            key = buffer.toString();
+                            key = buffer.toReplacedString(); 
                             buffer.reset();
                             break;
 
@@ -542,17 +542,26 @@
 
                         case '%':
                             int code0=in.read();
+                            boolean decoded=false;
                             if ('u'==code0)
                             {
-                                int code1=in.read();
-                                if (code1>=0)
+                                code0=in.read(); // XXX: we have to read the next byte, otherwise code0 is always 'u'
+                                if (code0>=0)
                                 {
-                                    int code2=in.read();
-                                    if (code2>=0)
+                                    int code1=in.read();
+                                    if (code1>=0)
                                     {
-                                        int code3=in.read();
-                                        if (code3>=0)
-                                            buffer.getStringBuilder().append(Character.toChars((convertHexDigit(code0)<<12)+(convertHexDigit(code1)<<8)+(convertHexDigit(code2)<<4)+convertHexDigit(code3)));
+                                        int code2=in.read();
+                                        if (code2>=0)
+                                        {
+                                            int code3=in.read();
+                                            if (code3>=0)
+                                            {
+                                                buffer.getStringBuilder().append(Character.toChars
+                                                    ((convertHexDigit(code0)<<12)+(convertHexDigit(code1)<<8)+(convertHexDigit(code2)<<4)+convertHexDigit(code3)));
+                                                decoded=true;
+                                            }
+                                        }
                                     }
                                 }
                             }
@@ -560,8 +569,15 @@
                             {
                                 int code1=in.read();
                                 if (code1>=0)
+                                {
                                     buffer.append((byte)((convertHexDigit(code0)<<4)+convertHexDigit(code1)));
+                                    decoded=true;
+                                }
                             }
+                            
+                            if (!decoded)
+                                buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
+
                             break;
                           
                         default:
@@ -586,13 +602,13 @@
             
             if (key != null)
             {
-                value = buffer.length()==0?"":buffer.toString();
+                value = buffer.toReplacedString();
                 buffer.reset();
                 map.add(key,value);
             }
             else if (buffer.length()>0)
             {
-                map.add(buffer.toString(), "");
+                map.add(buffer.toReplacedString(), "");
             }
         }
     }
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/StringMapTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/StringMapTest.java
deleted file mode 100644
index c20e9f1..0000000
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/StringMapTest.java
+++ /dev/null
@@ -1,209 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.util;
-
-import java.util.Set;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-public class StringMapTest
-{
-    StringMap<String> m0;
-    StringMap<String> m1;
-    StringMap<String> m5;
-    StringMap<String> m5i;
-
-    /*
-     * @see TestCase#setUp()
-     */
-
-    @Before
-    public void setUp() throws Exception
-    {
-        m0=new StringMap<>();
-        m1=new StringMap<>(false);
-        m1.put("abc", "0");
-
-        m5=new StringMap<>(false);
-        m5.put("a", "0");
-        m5.put("ab", "1");
-        m5.put("abc", "2");
-        m5.put("abb", "3");
-        m5.put("bbb", "4");
-
-        m5i=new StringMap<>(true);
-        m5i.put("ab", "1");
-        m5i.put("abc", "2");
-        m5i.put("abb", "3");
-    }
-
-    @Test
-    public void testSize()
-    {
-        Assert.assertEquals(0, m0.size());
-        Assert.assertEquals(1, m1.size());
-        Assert.assertEquals(5, m5.size());
-        Assert.assertEquals(3, m5i.size());
-
-        m1.remove("abc");
-        m5.remove("abc");
-        m5.put("bbb","x");
-        m5i.put("ABC", "x");
-        Assert.assertEquals(0, m0.size());
-        Assert.assertEquals(0, m1.size());
-        Assert.assertEquals(4, m5.size());
-        Assert.assertEquals(3, m5i.size());
-    }
-
-    @Test
-    public void testIsEmpty()
-    {
-        Assert.assertTrue(m0.isEmpty());
-        Assert.assertFalse(m1.isEmpty());
-        Assert.assertFalse(m5.isEmpty());
-        Assert.assertFalse(m5i.isEmpty());
-    }
-
-    @Test
-    public void testClear()
-    {
-        m0.clear();
-        m1.clear();
-        m5.clear();
-        m5i.clear();
-        Assert.assertTrue(m0.isEmpty());
-        Assert.assertTrue(m1.isEmpty());
-        Assert.assertTrue(m5.isEmpty());
-        Assert.assertTrue(m5i.isEmpty());
-        Assert.assertEquals(null, m1.get("abc"));
-        Assert.assertEquals(null, m5.get("abc"));
-        Assert.assertEquals(null, m5i.get("abc"));
-    }
-
-
-    /*
-     * Test for Object put(Object, Object)
-     */
-    @Test
-    public void testPutGet()
-    {
-        Assert.assertEquals("2", m5.get("abc"));
-        Assert.assertEquals(null, m5.get("aBc"));
-        Assert.assertEquals("2", m5i.get("abc"));
-        Assert.assertEquals("2", m5i.get("aBc"));
-
-        m5.put("aBc", "x");
-        m5i.put("AbC", "x");
-
-        StringBuilder buffer=new StringBuilder();
-        buffer.append("aBc");
-        Assert.assertEquals("2", m5.get("abc"));
-        Assert.assertEquals("x", m5.get(buffer));
-        Assert.assertEquals("x", m5i.get((Object)"abc"));
-        Assert.assertEquals("x", m5i.get("aBc"));
-
-
-    }
-
-    /*
-     * Test for Object remove(Object)
-     */
-    @Test
-    public void testRemove()
-    {
-        m0.remove("abc");
-        m1.remove("abc");
-        m5.remove("aBc");
-        m5.remove("bbb");
-        m5i.remove("aBc");
-
-        Assert.assertEquals(0, m0.size());
-        Assert.assertEquals(0, m1.size());
-        Assert.assertEquals(4, m5.size());
-        Assert.assertEquals(2, m5i.size());
-
-        Assert.assertEquals("2", m5.get("abc"));
-        Assert.assertEquals(null, m5.get("bbb"));
-        Assert.assertEquals(null, m5i.get("AbC"));
-    }
-
-    /*
-     * Test for Set entrySet()
-     */
-    @Test
-    public void testEntrySet()
-    {
-        Set es0=m0.entrySet();
-        Set es1=m1.entrySet();
-        Set es5=m5.entrySet();
-        Assert.assertEquals(0, es0.size());
-        Assert.assertEquals(1, es1.size());
-        Assert.assertEquals(5, es5.size());
-    }
-
-    /*
-     * Test for boolean containsKey(Object)
-     */
-    @Test
-    public void testContainsKey()
-    {
-        Assert.assertTrue(m5.containsKey("abc"));
-        Assert.assertTrue(!m5.containsKey("aBc"));
-        Assert.assertTrue(m5.containsKey("bbb"));
-        Assert.assertTrue(!m5.containsKey("xyz"));
-
-        Assert.assertTrue(m5i.containsKey("abc"));
-        Assert.assertTrue(m5i.containsKey("aBc"));
-        Assert.assertTrue(m5i.containsKey("ABC"));
-    }
-
-    @Test
-    public void testToString()
-    {
-        Assert.assertEquals("{}", m0.toString());
-        Assert.assertEquals("{abc=0}", m1.toString());
-        Assert.assertTrue(m5.toString().indexOf("abc=2") > 0);
-    }
-
-    @Test
-    public void testIgnoreCase()
-    {
-        StringMap<String> map = new StringMap<>(true);
-        map.put("POST","1");
-        map.put("HEAD","2");
-        map.put("PUT","3");
-        map.put("OPTIONS","4");
-        map.put("DELETE","5");
-        map.put("TRACE","6");
-        map.put("CONNECT","7");
-        map.put("Upgrade","8");
-
-        Assert.assertEquals("1", map.get("POST"));
-        Assert.assertEquals("1", map.get("pOST"));
-        Assert.assertEquals("1", map.get("Post"));
-
-        Assert.assertEquals("8", map.get("UPGRADE"));
-        Assert.assertEquals("8", map.get("Upgrade"));
-        Assert.assertEquals("8", map.get("upgrade"));
-
-    }
-
-}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/UrlEncodedUtf8Test.java b/jetty-util/src/test/java/org/eclipse/jetty/util/UrlEncodedUtf8Test.java
new file mode 100644
index 0000000..a80c51c
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/UrlEncodedUtf8Test.java
@@ -0,0 +1,165 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class UrlEncodedUtf8Test
+{
+
+    static final Logger LOG=Log.getLogger(UrlEncodedUtf8Test.class);
+
+    
+    @Test
+    public void testIncompleteSequestAtTheEnd() throws Exception
+    {
+        byte[] bytes= { 97, 98, 61, 99, -50 };
+        String test=new String(bytes,StandardCharsets.UTF_8);
+        String expected = "c"+Utf8Appendable.REPLACEMENT;
+
+        fromByteArray(test,bytes,"ab",expected,false);
+        fromInputStream(test,bytes,"ab",expected,false);
+    }
+
+    @Test
+    public void testIncompleteSequestAtTheEnd2() throws Exception
+    {
+        byte[] bytes={ 97, 98, 61, -50 };
+        String test=new String(bytes,StandardCharsets.UTF_8);
+        String expected = ""+Utf8Appendable.REPLACEMENT;
+
+        fromByteArray(test,bytes,"ab",expected,false);
+        fromInputStream(test,bytes,"ab",expected,false);
+        
+    }
+    
+    @Test
+    public void testIncompleteSequestInName() throws Exception
+    {
+        byte[] bytes= { 101, -50, 61, 102, 103, 38, 97, 98, 61, 99, 100 };
+        String test=new String(bytes,StandardCharsets.UTF_8);
+        String name = "e"+Utf8Appendable.REPLACEMENT;
+        String value = "fg";
+
+        fromByteArray(test,bytes,name,value,false);
+        fromInputStream(test,bytes,name,value,false);
+    }
+    
+    @Test
+    public void testIncompleteSequestInValue() throws Exception
+    {
+        byte[] bytes= { 101, 102, 61, 103, -50, 38, 97, 98, 61, 99, 100 };
+        String test=new String(bytes,StandardCharsets.UTF_8);
+        String name = "ef";
+        String value = "g"+Utf8Appendable.REPLACEMENT;
+
+        fromByteArray(test,bytes,name,value,false);
+        fromInputStream(test,bytes,name,value,false);
+        
+    }
+
+    @Test
+    public void testCorrectUnicode() throws Exception
+    {
+        String chars="a=%u0061";
+        byte[] bytes= chars.getBytes(StandardCharsets.UTF_8);
+        String test=new String(bytes,StandardCharsets.UTF_8);
+        String name = "a";
+        String value = "a";
+
+        fromByteArray(test,bytes,name,value,false);
+        fromInputStream(test,bytes,name,value,false);
+        
+    }
+    
+    @Test
+    public void testIncompleteUnicode() throws Exception
+    {
+        String chars="a=%u0";
+        byte[] bytes= chars.getBytes(StandardCharsets.UTF_8);
+        String test=new String(bytes,StandardCharsets.UTF_8);
+        String name = "a";
+        String value = ""+Utf8Appendable.REPLACEMENT;
+
+        fromByteArray(test,bytes,name,value,false);
+        fromInputStream(test,bytes,name,value,false);
+        
+    }
+    
+    @Test
+    public void testIncompletePercent() throws Exception
+    {
+        String chars="a=%A";
+        byte[] bytes= chars.getBytes(StandardCharsets.UTF_8);
+        String test=new String(bytes,StandardCharsets.UTF_8);
+        String name = "a";
+        String value = ""+Utf8Appendable.REPLACEMENT;
+
+        fromByteArray(test,bytes,name,value,false);
+        fromInputStream(test,bytes,name,value,false);
+        
+    }
+
+    static void fromByteArray(String test,byte[] b,String field,String expected,boolean thrown) throws Exception
+    {
+        MultiMap<String> values=new MultiMap<>();
+        try
+        {
+            //safeDecodeUtf8To(b, 0, b.length, values);
+            UrlEncoded.decodeUtf8To(b, 0, b.length, values);
+            if (thrown)
+                Assert.fail();
+            Assert.assertEquals(test, expected, values.getString(field));
+        }
+        catch (Exception e)
+        {
+            if (!thrown)
+                throw e;
+            LOG.ignore(e);
+        }
+    }
+
+    static void fromInputStream(String test, byte[] b,String field, String expected,boolean thrown) throws Exception
+    {
+        InputStream is=new ByteArrayInputStream(b);
+        MultiMap<String> values=new MultiMap<>();
+        try
+        {
+            //safeDecodeUtf8To(is, values, 1000000, 10000000);
+            UrlEncoded.decodeUtf8To(is, values, 1000000, 10000000);
+            if (thrown)
+                Assert.fail();
+            Assert.assertEquals(test, expected, values.getString(field));
+        }
+        catch (Exception e)
+        {
+            if (!thrown)
+                throw e;
+            LOG.ignore(e);
+        }
+    }
+    
+}
diff --git a/jetty-webapp/pom.xml b/jetty-webapp/pom.xml
index b6812cf..6a62b70 100644
--- a/jetty-webapp/pom.xml
+++ b/jetty-webapp/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-webapp</artifactId>
diff --git a/jetty-websocket/javax-websocket-client-impl/pom.xml b/jetty-websocket/javax-websocket-client-impl/pom.xml
index 75986c2..847dc89 100644
--- a/jetty-websocket/javax-websocket-client-impl/pom.xml
+++ b/jetty-websocket/javax-websocket-client-impl/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <groupId>org.eclipse.jetty.websocket</groupId>
     <artifactId>websocket-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/AbstractJsrEventDriver.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/AbstractJsrEventDriver.java
index 3b91b38..d7b9ced 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/AbstractJsrEventDriver.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/AbstractJsrEventDriver.java
@@ -31,11 +31,10 @@
 import org.eclipse.jetty.websocket.common.CloseInfo;
 import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.events.AbstractEventDriver;
-import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.jsr356.JsrSession;
 import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
 
-public abstract class AbstractJsrEventDriver extends AbstractEventDriver implements EventDriver
+public abstract class AbstractJsrEventDriver extends AbstractEventDriver
 {
     protected final EndpointMetadata metadata;
     protected final EndpointConfig config;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java
index a550c20..0cf9df7 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrAnnotatedEventDriver.java
@@ -23,6 +23,7 @@
 import java.io.Reader;
 import java.nio.ByteBuffer;
 import java.util.Map;
+
 import javax.websocket.CloseReason;
 import javax.websocket.DecodeException;
 
@@ -31,7 +32,6 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.Frame;
-import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.common.message.MessageInputStream;
 import org.eclipse.jetty.websocket.common.message.MessageReader;
 import org.eclipse.jetty.websocket.common.message.SimpleBinaryMessage;
@@ -44,7 +44,7 @@
 /**
  * Base implementation for JSR-356 Annotated event drivers.
  */
-public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver implements EventDriver
+public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
 {
     private static final Logger LOG = Log.getLogger(JsrAnnotatedEventDriver.class);
     private final JsrEvents<?, ?> events;
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrEndpointEventDriver.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrEndpointEventDriver.java
index b977147..9ac4906 100644
--- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrEndpointEventDriver.java
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/endpoints/JsrEndpointEventDriver.java
@@ -23,6 +23,7 @@
 import java.io.Reader;
 import java.nio.ByteBuffer;
 import java.util.Map;
+
 import javax.websocket.CloseReason;
 import javax.websocket.Endpoint;
 import javax.websocket.MessageHandler;
@@ -34,7 +35,6 @@
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.Frame;
-import org.eclipse.jetty.websocket.common.events.EventDriver;
 import org.eclipse.jetty.websocket.common.message.MessageInputStream;
 import org.eclipse.jetty.websocket.common.message.MessageReader;
 import org.eclipse.jetty.websocket.jsr356.JsrPongMessage;
@@ -49,7 +49,7 @@
 /**
  * EventDriver for websocket that extend from {@link javax.websocket.Endpoint}
  */
-public class JsrEndpointEventDriver extends AbstractJsrEventDriver implements EventDriver
+public class JsrEndpointEventDriver extends AbstractJsrEventDriver
 {
     private static final Logger LOG = Log.getLogger(JsrEndpointEventDriver.class);
 
diff --git a/jetty-websocket/javax-websocket-server-impl/pom.xml b/jetty-websocket/javax-websocket-server-impl/pom.xml
index 0b62c8f..d03b688 100644
--- a/jetty-websocket/javax-websocket-server-impl/pom.xml
+++ b/jetty-websocket/javax-websocket-server-impl/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <groupId>org.eclipse.jetty.websocket</groupId>
     <artifactId>websocket-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-websocket/pom.xml b/jetty-websocket/pom.xml
index a955fe0..596020b 100644
--- a/jetty-websocket/pom.xml
+++ b/jetty-websocket/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <artifactId>jetty-project</artifactId>
         <groupId>org.eclipse.jetty</groupId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-websocket/websocket-api/pom.xml b/jetty-websocket/websocket-api/pom.xml
index ba16ff0..716dddb 100644
--- a/jetty-websocket/websocket-api/pom.xml
+++ b/jetty-websocket/websocket-api/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.websocket</groupId>
         <artifactId>websocket-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-websocket/websocket-client/pom.xml b/jetty-websocket/websocket-client/pom.xml
index fce652e..5e516d6 100644
--- a/jetty-websocket/websocket-client/pom.xml
+++ b/jetty-websocket/websocket-client/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.websocket</groupId>
         <artifactId>websocket-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java
index da112f9..ab20d77 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/ConnectionManager.java
@@ -32,6 +32,7 @@
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.StatusCode;
 import org.eclipse.jetty.websocket.api.WebSocketException;
 import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
 import org.eclipse.jetty.websocket.client.WebSocketClient;
@@ -158,7 +159,7 @@
         sessions.add(session);
     }
 
-    private void closeAllConnections()
+    private void shutdownAllConnections()
     {
         for (WebSocketSession session : sessions)
         {
@@ -166,11 +167,13 @@
             {
                 try
                 {
-                    session.getConnection().close();
+                    session.getConnection().close(
+                            StatusCode.SHUTDOWN,
+                            "Shutdown");
                 }
                 catch (Throwable t)
                 {
-                    LOG.debug("During Close All Connections",t);
+                    LOG.debug("During Shutdown All Connections",t);
                 }
             }
         }
@@ -203,7 +206,7 @@
     @Override
     protected void doStop() throws Exception
     {
-        closeAllConnections();
+        shutdownAllConnections();
         sessions.clear();
         super.doStop();
         removeBean(selector);
diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java
index 928e911..6616f54 100644
--- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java
+++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientSelectorManager.java
@@ -98,7 +98,7 @@
             else
             {
                 // Standard "ws://"
-                endPoint.setIdleTimeout(connectPromise.getClient().getMaxIdleTimeout());
+                endPoint.setIdleTimeout(connectPromise.getDriver().getPolicy().getIdleTimeout());
                 return newUpgradeConnection(channel,endPoint,connectPromise);
             }
         }
@@ -139,4 +139,9 @@
     {
         this.sslContextFactory = sslContextFactory;
     }
+    
+    public WebSocketPolicy getPolicy()
+    {
+        return policy;
+    }
 }
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientCloseTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientCloseTest.java
new file mode 100644
index 0000000..3a7f338
--- /dev/null
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientCloseTest.java
@@ -0,0 +1,626 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.client;
+
+import static org.hamcrest.Matchers.*;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.SelectChannelEndPoint;
+import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
+import org.eclipse.jetty.toolchain.test.EventQueue;
+import org.eclipse.jetty.toolchain.test.TestTracker;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Scheduler;
+import org.eclipse.jetty.websocket.api.ProtocolException;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.StatusCode;
+import org.eclipse.jetty.websocket.api.WebSocketAdapter;
+import org.eclipse.jetty.websocket.client.io.ConnectionManager;
+import org.eclipse.jetty.websocket.client.io.WebSocketClientSelectorManager;
+import org.eclipse.jetty.websocket.common.CloseInfo;
+import org.eclipse.jetty.websocket.common.OpCode;
+import org.eclipse.jetty.websocket.common.WebSocketFrame;
+import org.eclipse.jetty.websocket.common.WebSocketSession;
+import org.eclipse.jetty.websocket.common.frames.TextFrame;
+import org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection;
+import org.eclipse.jetty.websocket.common.test.BlockheadServer;
+import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
+import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
+import org.eclipse.jetty.websocket.common.test.RawFrameBuilder;
+import org.hamcrest.Matcher;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class ClientCloseTest
+{
+    private static final Logger LOG = Log.getLogger(ClientCloseTest.class);
+
+    private static class CloseTrackingSocket extends WebSocketAdapter
+    {
+        private static final Logger LOG = Log.getLogger(ClientCloseTest.CloseTrackingSocket.class);
+
+        public int closeCode = -1;
+        public String closeReason = null;
+        public CountDownLatch closeLatch = new CountDownLatch(1);
+        public CountDownLatch openLatch = new CountDownLatch(1);
+
+        public EventQueue<String> messageQueue = new EventQueue<>();
+        public EventQueue<Throwable> errorQueue = new EventQueue<>();
+
+        public void assertNoCloseEvent()
+        {
+            Assert.assertThat("Client Close Event",closeLatch.getCount(),is(1L));
+            Assert.assertThat("Client Close Event Status Code ",closeCode,is(-1));
+        }
+
+        public void assertReceivedCloseEvent(int clientTimeoutMs, Matcher<Integer> statusCodeMatcher, Matcher<String> reasonMatcher)
+                throws InterruptedException
+        {
+            long maxTimeout = clientTimeoutMs * 2;
+
+            Assert.assertThat("Client Close Event Occurred",closeLatch.await(maxTimeout,TimeUnit.MILLISECONDS),is(true));
+            Assert.assertThat("Client Close Event Status Code",closeCode,statusCodeMatcher);
+            if (reasonMatcher == null)
+            {
+                Assert.assertThat("Client Close Event Reason",closeReason,nullValue());
+            }
+            else
+            {
+                Assert.assertThat("Client Close Event Reason",closeReason,reasonMatcher);
+            }
+        }
+
+        public void assertReceivedError(Class<? extends Throwable> expectedThrownClass, Matcher<String> messageMatcher) throws TimeoutException,
+                InterruptedException
+        {
+            errorQueue.awaitEventCount(1,500,TimeUnit.MILLISECONDS);
+            Throwable actual = errorQueue.poll();
+            Assert.assertThat("Client Error Event",actual,instanceOf(expectedThrownClass));
+            if (messageMatcher == null)
+            {
+                Assert.assertThat("Client Error Event Message",actual.getMessage(),nullValue());
+            }
+            else
+            {
+                Assert.assertThat("Client Error Event Message",actual.getMessage(),messageMatcher);
+            }
+        }
+
+        public void clearQueues()
+        {
+            messageQueue.clear();
+            errorQueue.clear();
+        }
+
+        @Override
+        public void onWebSocketClose(int statusCode, String reason)
+        {
+            LOG.debug("onWebSocketClose({},{})",statusCode,reason);
+            super.onWebSocketClose(statusCode,reason);
+            closeCode = statusCode;
+            closeReason = reason;
+            closeLatch.countDown();
+        }
+
+        @Override
+        public void onWebSocketConnect(Session session)
+        {
+            super.onWebSocketConnect(session);
+            openLatch.countDown();
+        }
+
+        @Override
+        public void onWebSocketError(Throwable cause)
+        {
+            LOG.debug("onWebSocketError",cause);
+            Assert.assertThat("Error capture",errorQueue.offer(cause),is(true));
+        }
+
+        @Override
+        public void onWebSocketText(String message)
+        {
+            LOG.debug("onWebSocketText({})",message);
+            messageQueue.offer(message);
+        }
+
+        public EndPoint getEndPoint() throws Exception
+        {
+            Session session = getSession();
+            Assert.assertThat("Session type",session,instanceOf(WebSocketSession.class));
+
+            WebSocketSession wssession = (WebSocketSession)session;
+            Field fld = wssession.getClass().getDeclaredField("connection");
+            fld.setAccessible(true);
+            Assert.assertThat("Field: connection",fld,notNullValue());
+
+            Object val = fld.get(wssession);
+            Assert.assertThat("Connection type",val,instanceOf(AbstractWebSocketConnection.class));
+            @SuppressWarnings("resource")
+            AbstractWebSocketConnection wsconn = (AbstractWebSocketConnection)val;
+            return wsconn.getEndPoint();
+        }
+    }
+
+    @Rule
+    public TestTracker tt = new TestTracker();
+
+    private BlockheadServer server;
+    private WebSocketClient client;
+
+    private void confirmConnection(CloseTrackingSocket clientSocket, Future<Session> clientFuture, ServerConnection serverConn) throws Exception
+    {
+        // Wait for client connect on via future
+        clientFuture.get(500,TimeUnit.MILLISECONDS);
+
+        // Wait for client connect via client websocket
+        Assert.assertThat("Client WebSocket is Open",clientSocket.openLatch.await(500,TimeUnit.MILLISECONDS),is(true));
+
+        try
+        {
+            // Send message from client to server
+            final String echoMsg = "echo-test";
+            Future<Void> testFut = clientSocket.getRemote().sendStringByFuture(echoMsg);
+
+            // Wait for send future
+            testFut.get(500,TimeUnit.MILLISECONDS);
+
+            // Read Frame on server side
+            IncomingFramesCapture serverCapture = serverConn.readFrames(1,500,TimeUnit.MILLISECONDS);
+            serverCapture.assertNoErrors();
+            serverCapture.assertFrameCount(1);
+            WebSocketFrame frame = serverCapture.getFrames().poll();
+            Assert.assertThat("Server received frame",frame.getOpCode(),is(OpCode.TEXT));
+            Assert.assertThat("Server received frame payload",frame.getPayloadAsUTF8(),is(echoMsg));
+
+            // Server send echo reply
+            serverConn.write(new TextFrame().setPayload(echoMsg));
+
+            // Wait for received echo
+            clientSocket.messageQueue.awaitEventCount(1,1,TimeUnit.SECONDS);
+
+            // Verify received message
+            String recvMsg = clientSocket.messageQueue.poll();
+            Assert.assertThat("Received message",recvMsg,is(echoMsg));
+
+            // Verify that there are no errors
+            Assert.assertThat("Error events",clientSocket.errorQueue,empty());
+        }
+        finally
+        {
+            clientSocket.clearQueues();
+        }
+    }
+
+    private void confirmServerReceivedCloseFrame(ServerConnection serverConn, int expectedCloseCode, Matcher<String> closeReasonMatcher) throws IOException,
+            TimeoutException
+    {
+        IncomingFramesCapture serverCapture = serverConn.readFrames(1,500,TimeUnit.MILLISECONDS);
+        serverCapture.assertNoErrors();
+        serverCapture.assertFrameCount(1);
+        serverCapture.assertHasFrame(OpCode.CLOSE,1);
+        WebSocketFrame frame = serverCapture.getFrames().poll();
+        Assert.assertThat("Server received close frame",frame.getOpCode(),is(OpCode.CLOSE));
+        CloseInfo closeInfo = new CloseInfo(frame);
+        Assert.assertThat("Server received close code",closeInfo.getStatusCode(),is(expectedCloseCode));
+        if (closeReasonMatcher == null)
+        {
+            Assert.assertThat("Server received close reason",closeInfo.getReason(),nullValue());
+        }
+        else
+        {
+            Assert.assertThat("Server received close reason",closeInfo.getReason(),closeReasonMatcher);
+        }
+    }
+
+    public static class TestWebSocketClient extends WebSocketClient
+    {
+        @Override
+        protected ConnectionManager newConnectionManager()
+        {
+            return new TestConnectionManager(this);
+        }
+    }
+
+    public static class TestConnectionManager extends ConnectionManager
+    {
+        public TestConnectionManager(WebSocketClient client)
+        {
+            super(client);
+        }
+
+        @Override
+        protected WebSocketClientSelectorManager newWebSocketClientSelectorManager(WebSocketClient client)
+        {
+            return new TestSelectorManager(client);
+        }
+    }
+
+    public static class TestSelectorManager extends WebSocketClientSelectorManager
+    {
+        public TestSelectorManager(WebSocketClient client)
+        {
+            super(client);
+        }
+
+        @Override
+        protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey selectionKey) throws IOException
+        {
+            return new TestEndPoint(channel,selectSet,selectionKey,getScheduler(),getPolicy().getIdleTimeout());
+        }
+    }
+
+    public static class TestEndPoint extends SelectChannelEndPoint
+    {
+        public AtomicBoolean congestedFlush = new AtomicBoolean(false);
+
+        public TestEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey key, Scheduler scheduler, long idleTimeout)
+        {
+            super(channel,selector,key,scheduler,idleTimeout);
+        }
+
+        @Override
+        public boolean flush(ByteBuffer... buffers) throws IOException
+        {
+            boolean flushed = super.flush(buffers);
+            congestedFlush.set(!flushed);
+            return flushed;
+        }
+    }
+
+    @Before
+    public void startClient() throws Exception
+    {
+        client = new TestWebSocketClient();
+        client.start();
+    }
+
+    @Before
+    public void startServer() throws Exception
+    {
+        server = new BlockheadServer();
+        server.start();
+    }
+
+    @After
+    public void stopClient() throws Exception
+    {
+        if (client.isRunning())
+        {
+            client.stop();
+        }
+    }
+
+    @After
+    public void stopServer() throws Exception
+    {
+        server.stop();
+    }
+
+    @Test
+    public void testHalfClose() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // client sends close frame (code 1000, normal)
+        final String origCloseReason = "Normal Close";
+        clientSocket.getSession().close(StatusCode.NORMAL,origCloseReason);
+
+        // server receives close frame
+        confirmServerReceivedCloseFrame(serverConn,StatusCode.NORMAL,is(origCloseReason));
+
+        // server sends 2 messages
+        serverConn.write(new TextFrame().setPayload("Hello"));
+        serverConn.write(new TextFrame().setPayload("World"));
+
+        // server sends close frame (code 1000, no reason)
+        CloseInfo sclose = new CloseInfo(StatusCode.NORMAL,"From Server");
+        serverConn.write(sclose.asFrame());
+
+        // client receives 2 messages
+        clientSocket.messageQueue.awaitEventCount(2,1,TimeUnit.SECONDS);
+
+        // Verify received messages
+        String recvMsg = clientSocket.messageQueue.poll();
+        Assert.assertThat("Received message 1",recvMsg,is("Hello"));
+        recvMsg = clientSocket.messageQueue.poll();
+        Assert.assertThat("Received message 2",recvMsg,is("World"));
+
+        // Verify that there are no errors
+        Assert.assertThat("Error events",clientSocket.errorQueue,empty());
+
+        // client close event on ws-endpoint
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.NORMAL),containsString("From Server"));
+    }
+
+    @Test
+    public void testNetworkCongestion() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // client sends BIG frames (until it cannot write anymore)
+        // server must not read (for test purpose, in order to congest connection)
+        // when write is congested, client enqueue close frame
+        // client initiate write, but write never completes
+        EndPoint endp = clientSocket.getEndPoint();
+        Assert.assertThat("EndPoint is testable",endp,instanceOf(TestEndPoint.class));
+        TestEndPoint testendp = (TestEndPoint)endp;
+
+        char msg[] = new char[10240];
+        int writeCount = 0;
+        long writeSize = 0;
+        int i = 0;
+        while (!testendp.congestedFlush.get())
+        {
+            int z = i - ((i / 26) * 26);
+            char c = (char)('a' + z);
+            Arrays.fill(msg,c);
+            clientSocket.getRemote().sendStringByFuture(String.valueOf(msg));
+            writeCount++;
+            writeSize += msg.length;
+        }
+        LOG.debug("Wrote {} frames totalling {} bytes of payload before congestion kicked in",writeCount,writeSize);
+
+        // Verify that there are no errors
+        Assert.assertThat("Error events",clientSocket.errorQueue,empty());
+
+        // client idle timeout triggers close event on client ws-endpoint
+        // client close event on ws-endpoint
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.ABNORMAL),containsString("Timeout"));
+    }
+
+    @Test
+    public void testProtocolException() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // client should not have received close message (yet)
+        clientSocket.assertNoCloseEvent();
+
+        // server sends bad close frame (too big of a reason message)
+        byte msg[] = new byte[400];
+        Arrays.fill(msg,(byte)'x');
+        ByteBuffer bad = ByteBuffer.allocate(500);
+        RawFrameBuilder.putOpFin(bad,OpCode.CLOSE,true);
+        RawFrameBuilder.putLength(bad,msg.length + 2,false);
+        bad.putShort((short)StatusCode.NORMAL);
+        bad.put(msg);
+        BufferUtil.flipToFlush(bad,0);
+        serverConn.write(bad);
+
+        // client should have noticed the error
+        clientSocket.assertReceivedError(ProtocolException.class,containsString("Invalid control frame"));
+
+        // client parse invalid frame, notifies server of close (protocol error)
+        confirmServerReceivedCloseFrame(serverConn,StatusCode.PROTOCOL,allOf(containsString("Invalid control frame"),containsString("length")));
+
+        // server disconnects
+        serverConn.disconnect();
+
+        // client triggers close event on client ws-endpoint
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.PROTOCOL),allOf(containsString("Invalid control frame"),containsString("length")));
+    }
+
+    @Test
+    public void testReadEOF() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // client sends close frame
+        final String origCloseReason = "Normal Close";
+        clientSocket.getSession().close(StatusCode.NORMAL,origCloseReason);
+
+        // server receives close frame
+        confirmServerReceivedCloseFrame(serverConn,StatusCode.NORMAL,is(origCloseReason));
+
+        // client should not have received close message (yet)
+        clientSocket.assertNoCloseEvent();
+
+        // server shuts down connection (no frame reply)
+        serverConn.disconnect();
+
+        // client reads -1 (EOF)
+        // client triggers close event on client ws-endpoint
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.ABNORMAL),containsString("EOF"));
+    }
+
+    @Test
+    public void testServerNoCloseHandshake() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // client sends close frame
+        final String origCloseReason = "Normal Close";
+        clientSocket.getSession().close(StatusCode.NORMAL,origCloseReason);
+
+        // server receives close frame
+        confirmServerReceivedCloseFrame(serverConn,StatusCode.NORMAL,is(origCloseReason));
+
+        // client should not have received close message (yet)
+        clientSocket.assertNoCloseEvent();
+
+        // server never sends close frame handshake
+        // server sits idle
+
+        // client idle timeout triggers close event on client ws-endpoint
+        // assert - close code==1006 (abnormal)
+        // assert - close reason message contains (timeout)
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.ABNORMAL),containsString("Timeout"));
+    }
+
+    @Test
+    public void testStopLifecycle() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        int clientCount = 3;
+        CloseTrackingSocket clientSockets[] = new CloseTrackingSocket[clientCount];
+        ServerConnection serverConns[] = new ServerConnection[clientCount];
+
+        // Connect Multiple Clients
+        for (int i = 0; i < clientCount; i++)
+        {
+            // Client Request Upgrade
+            clientSockets[i] = new CloseTrackingSocket();
+            Future<Session> clientConnectFuture = client.connect(clientSockets[i],server.getWsUri());
+
+            // Server accepts connection
+            serverConns[i] = server.accept();
+            serverConns[i].upgrade();
+
+            // client confirms connection via echo
+            confirmConnection(clientSockets[i],clientConnectFuture,serverConns[i]);
+        }
+
+        // client lifecycle stop
+        client.stop();
+
+        // clients send close frames (code 1001, shutdown)
+        for (int i = 0; i < clientCount; i++)
+        {
+            // server receives close frame
+            confirmServerReceivedCloseFrame(serverConns[i],StatusCode.SHUTDOWN,containsString("Shutdown"));
+        }
+
+        // clients disconnect
+        for (int i = 0; i < clientCount; i++)
+        {
+            clientSockets[i].assertReceivedCloseEvent(timeout,is(StatusCode.SHUTDOWN),containsString("Shutdown"));
+        }
+    }
+
+    @Test
+    public void testWriteException() throws Exception
+    {
+        // Set client timeout
+        final int timeout = 1000;
+        client.setMaxIdleTimeout(timeout);
+
+        // Client connects
+        CloseTrackingSocket clientSocket = new CloseTrackingSocket();
+        Future<Session> clientConnectFuture = client.connect(clientSocket,server.getWsUri());
+
+        // Server accepts connect
+        ServerConnection serverConn = server.accept();
+        serverConn.upgrade();
+
+        // client confirms connection via echo
+        confirmConnection(clientSocket,clientConnectFuture,serverConn);
+
+        // setup client endpoint for write failure (test only)
+        EndPoint endp = clientSocket.getEndPoint();
+        endp.shutdownOutput();
+
+        // client enqueue close frame
+        // client write failure
+        final String origCloseReason = "Normal Close";
+        clientSocket.getSession().close(StatusCode.NORMAL,origCloseReason);
+
+        clientSocket.assertReceivedError(EofException.class,null);
+
+        // client triggers close event on client ws-endpoint
+        // assert - close code==1006 (abnormal)
+        // assert - close reason message contains (write failure)
+        clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.ABNORMAL),containsString("EOF"));
+    }
+}
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java
index e06e883..2cea2fd 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ServerWriteThread.java
@@ -19,7 +19,6 @@
 package org.eclipse.jetty.websocket.client;
 
 import java.io.IOException;
-import java.util.concurrent.Exchanger;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -32,7 +31,6 @@
 {
     private static final Logger LOG = Log.getLogger(ServerWriteThread.class);
     private final ServerConnection conn;
-    private Exchanger<String> exchanger;
     private int slowness = -1;
     private int messageCount = 100;
     private String message = "Hello";
@@ -42,11 +40,6 @@
         this.conn = conn;
     }
 
-    public Exchanger<String> getExchanger()
-    {
-        return exchanger;
-    }
-
     public String getMessage()
     {
         return message;
@@ -73,12 +66,6 @@
             {
                 conn.write(new TextFrame().setPayload(message));
 
-                if (exchanger != null)
-                {
-                    // synchronized on exchange
-                    exchanger.exchange(message);
-                }
-
                 m.incrementAndGet();
 
                 if (slowness > 0)
@@ -93,11 +80,6 @@
         }
     }
 
-    public void setExchanger(Exchanger<String> exchanger)
-    {
-        this.exchanger = exchanger;
-    }
-
     public void setMessage(String message)
     {
         this.message = message;
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java
index 80720f3..4f56ec6 100644
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java
+++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/SlowServerTest.java
@@ -49,7 +49,7 @@
     public void startClient() throws Exception
     {
         client = new WebSocketClient();
-        client.getPolicy().setIdleTimeout(60000);
+        client.setMaxIdleTimeout(60000);
         client.start();
     }
 
@@ -78,7 +78,7 @@
     {
         JettyTrackingSocket tsocket = new JettyTrackingSocket();
         client.setMasker(new ZeroMasker());
-        client.getPolicy().setIdleTimeout(60000);
+        client.setMaxIdleTimeout(60000);
 
         URI wsUri = server.getWsUri();
         Future<Session> future = client.connect(tsocket,wsUri);
@@ -123,41 +123,38 @@
     @Slow
     public void testServerSlowToSend() throws Exception
     {
-        // final Exchanger<String> exchanger = new Exchanger<String>();
-        JettyTrackingSocket tsocket = new JettyTrackingSocket();
-        // tsocket.messageExchanger = exchanger;
+        JettyTrackingSocket clientSocket = new JettyTrackingSocket();
         client.setMasker(new ZeroMasker());
-        client.getPolicy().setIdleTimeout(60000);
+        client.setMaxIdleTimeout(60000);
 
         URI wsUri = server.getWsUri();
-        Future<Session> future = client.connect(tsocket,wsUri);
+        Future<Session> clientConnectFuture = client.connect(clientSocket,wsUri);
 
-        ServerConnection sconnection = server.accept();
-        sconnection.setSoTimeout(60000);
-        sconnection.upgrade();
+        ServerConnection serverConn = server.accept();
+        serverConn.setSoTimeout(60000);
+        serverConn.upgrade();
 
         // Confirm connected
-        future.get(500,TimeUnit.MILLISECONDS);
-        tsocket.waitForConnected(500,TimeUnit.MILLISECONDS);
+        clientConnectFuture.get(500,TimeUnit.MILLISECONDS);
+        clientSocket.waitForConnected(500,TimeUnit.MILLISECONDS);
 
         // Have server write slowly.
         int messageCount = 1000;
 
-        ServerWriteThread writer = new ServerWriteThread(sconnection);
+        ServerWriteThread writer = new ServerWriteThread(serverConn);
         writer.setMessageCount(messageCount);
         writer.setMessage("Hello");
-        // writer.setExchanger(exchanger);
         writer.setSlowness(10);
         writer.start();
         writer.join();
 
         // Verify receive
-        Assert.assertThat("Message Receive Count",tsocket.messageQueue.size(),is(messageCount));
+        Assert.assertThat("Message Receive Count",clientSocket.messageQueue.size(),is(messageCount));
 
         // Close
-        sconnection.close(StatusCode.NORMAL);
+        serverConn.close(StatusCode.NORMAL);
 
-        Assert.assertTrue("Client Socket Closed",tsocket.closeLatch.await(10,TimeUnit.SECONDS));
-        tsocket.assertCloseCode(StatusCode.NORMAL);
+        Assert.assertTrue("Client Socket Closed",clientSocket.closeLatch.await(10,TimeUnit.SECONDS));
+        clientSocket.assertCloseCode(StatusCode.NORMAL);
     }
 }
diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/TimeoutTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/TimeoutTest.java
deleted file mode 100644
index 72c1fc1..0000000
--- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/TimeoutTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.websocket.client;
-
-import static org.hamcrest.Matchers.lessThanOrEqualTo;
-
-import java.net.URI;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.toolchain.test.TestTracker;
-import org.eclipse.jetty.websocket.api.Session;
-import org.eclipse.jetty.websocket.api.StatusCode;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer;
-import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Various tests for Timeout handling
- */
-public class TimeoutTest
-{
-    @Rule
-    public TestTracker tt = new TestTracker();
-
-    private BlockheadServer server;
-    private WebSocketClient client;
-
-    @Before
-    public void startClient() throws Exception
-    {
-        client = new WebSocketClient();
-        client.getPolicy().setIdleTimeout(250); // idle timeout (for all tests here)
-        client.start();
-    }
-
-    @Before
-    public void startServer() throws Exception
-    {
-        server = new BlockheadServer();
-        server.start();
-    }
-
-    @After
-    public void stopClient() throws Exception
-    {
-        client.stop();
-    }
-
-    @After
-    public void stopServer() throws Exception
-    {
-        server.stop();
-    }
-
-    /**
-     * In a situation where the upgrade/connection is successful, and there is no activity for a while, the idle timeout triggers on the client side and
-     * automatically initiates a close handshake.
-     */
-    @Test
-    public void testIdleDetectedByClient() throws Exception
-    {
-        JettyTrackingSocket wsocket = new JettyTrackingSocket();
-
-        URI wsUri = server.getWsUri();
-        client.setMaxIdleTimeout(1000);
-        Future<Session> future = client.connect(wsocket,wsUri);
-
-        ServerConnection ssocket = server.accept();
-        ssocket.upgrade();
-
-        try
-        {
-            ssocket.startEcho();
-            // Validate that connect occurred
-            future.get(500,TimeUnit.MILLISECONDS);
-            wsocket.waitForConnected(500,TimeUnit.MILLISECONDS);
-
-            // Wait for inactivity idle timeout.
-            long start = System.currentTimeMillis();
-            wsocket.waitForClose(2,TimeUnit.SECONDS);
-            long end = System.currentTimeMillis();
-            long dur = (end - start);
-            // Make sure idle timeout takes less than 5 total seconds
-            Assert.assertThat("Idle Timeout",dur,lessThanOrEqualTo(3000L));
-
-            // Client should see a close event, with abnormal status NO_CLOSE
-            wsocket.assertCloseCode(StatusCode.ABNORMAL);
-        }
-        finally
-        {
-            ssocket.stopEcho();
-        }
-    }
-}
diff --git a/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties b/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties
index 7c9bd36..9668b13 100644
--- a/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties
+++ b/jetty-websocket/websocket-client/src/test/resources/jetty-logging.properties
@@ -1,12 +1,17 @@
 org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
 org.eclipse.jetty.LEVEL=WARN
 # org.eclipse.jetty.LEVEL=DEBUG
-# org.eclipse.jetty.io.ChannelEndPoint.LEVEL=INFO
+# org.eclipse.jetty.io.ChannelEndPoint.LEVEL=DEBUG
+# org.eclipse.jetty.io.SelectChannelEndPoint.LEVEL=DEBUG
+# org.eclipse.jetty.io.IdleTimeout.LEVEL=DEBUG
+# org.eclipse.jetty.io.FillInterest.LEVEL=DEBUG
+# org.eclipse.jetty.io.AbstractConnection.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.LEVEL=WARN
 # org.eclipse.jetty.websocket.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.client.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.LEVEL=DEBUG
-# org.eclipse.jetty.websocket.common.io.WriteBytesProvider.LEVEL=DEBUG
+# org.eclipse.jetty.websocket.common.io.IOState.LEVEL=DEBUG
+
 # org.eclipse.jetty.websocket.common.Generator.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.common.Parser.LEVEL=DEBUG
 # org.eclipse.jetty.websocket.client.TrackingSocket.LEVEL=DEBUG
diff --git a/jetty-websocket/websocket-common/pom.xml b/jetty-websocket/websocket-common/pom.xml
index ada7d5c..7a7fa30 100644
--- a/jetty-websocket/websocket-common/pom.xml
+++ b/jetty-websocket/websocket-common/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <groupId>org.eclipse.jetty.websocket</groupId>
     <artifactId>websocket-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/CloseInfo.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/CloseInfo.java
index 03a737c..f14c252 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/CloseInfo.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/CloseInfo.java
@@ -20,6 +20,7 @@
 
 import java.nio.ByteBuffer;
 
+import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
 import org.eclipse.jetty.util.Utf8StringBuilder;
@@ -65,7 +66,8 @@
             statusCode |= (data.get() & 0xFF) << 8;
             statusCode |= (data.get() & 0xFF);
 
-            if(validate) {
+            if (validate)
+            {
                 if ((statusCode < StatusCode.NORMAL) || (statusCode == StatusCode.UNDEFINED) || (statusCode == StatusCode.NO_CLOSE)
                         || (statusCode == StatusCode.NO_CODE) || ((statusCode > 1011) && (statusCode <= 2999)) || (statusCode >= 5000))
                 {
@@ -120,7 +122,7 @@
 
     public CloseInfo(int statusCode)
     {
-        this(statusCode, null);
+        this(statusCode,null);
     }
 
     public CloseInfo(int statusCode, String reason)
@@ -144,8 +146,9 @@
             utf = StringUtil.getUtf8Bytes(reason);
             len += utf.length;
         }
-        
-        ByteBuffer buf = ByteBuffer.allocate(len);
+
+        ByteBuffer buf = BufferUtil.allocate(len);
+        BufferUtil.flipToFill(buf);
         buf.put((byte)((statusCode >>> 8) & 0xFF));
         buf.put((byte)((statusCode >>> 0) & 0xFF));
 
@@ -153,7 +156,7 @@
         {
             buf.put(utf,0,utf.length);
         }
-        buf.flip();
+        BufferUtil.flipToFlush(buf,0);
 
         return buf;
     }
@@ -162,7 +165,14 @@
     {
         CloseFrame frame = new CloseFrame();
         frame.setFin(true);
-        frame.setPayload(asByteBuffer());
+        if ((statusCode >= 1000) && (statusCode != StatusCode.NO_CLOSE) && (statusCode != StatusCode.NO_CODE))
+        {
+            if (statusCode == StatusCode.FAILED_TLS_HANDSHAKE)
+            {
+                throw new ProtocolException("Close Frame with status code " + statusCode + " not allowed (per RFC6455)");
+            }
+            frame.setPayload(asByteBuffer());
+        }
         return frame;
     }
 
@@ -180,10 +190,10 @@
     {
         return !((statusCode == StatusCode.NORMAL) || (statusCode == StatusCode.NO_CODE));
     }
-    
+
     public boolean isAbnormal()
     {
-        return (statusCode == StatusCode.ABNORMAL);
+        return (statusCode != StatusCode.NORMAL);
     }
 
     @Override
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java
index 0cf7e5f..86195a0 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java
@@ -96,8 +96,9 @@
 
     private void assertSanePayloadLength(long len)
     {
-        if (LOG.isDebugEnabled())
-            LOG.debug("Payload Length: {} - {}",len,this);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("{} Payload Length: {} - {}",policy.getBehavior(),len,this);
+        }
 
         // Since we use ByteBuffer so often, having lengths over Integer.MAX_VALUE is really impossible.
         if (len > Integer.MAX_VALUE)
@@ -239,7 +240,7 @@
         incomingFramesHandler.incomingError(e);
     }
 
-    public void parse(ByteBuffer buffer)
+    public void parse(ByteBuffer buffer) throws WebSocketException
     {
         if (buffer.remaining() <= 0)
         {
@@ -266,13 +267,20 @@
         {
             buffer.position(buffer.limit()); // consume remaining
             reset();
+            // let session know
             notifyWebSocketException(e);
+            // need to throw for proper close behavior in connection
+            throw e;
         }
         catch (Throwable t)
         {
             buffer.position(buffer.limit()); // consume remaining
             reset();
-            notifyWebSocketException(new WebSocketException(t));
+            // let session know
+            WebSocketException e = new WebSocketException(t);
+            notifyWebSocketException(e);
+            // need to throw for proper close behavior in connection
+            throw e;
         }
     }
 
@@ -299,7 +307,9 @@
     private boolean parseFrame(ByteBuffer buffer)
     {
         if (LOG.isDebugEnabled())
+        {
             LOG.debug("{} Parsing {} bytes",policy.getBehavior(),buffer.remaining());
+        }
         while (buffer.hasRemaining())
         {
             switch (state)
@@ -318,7 +328,8 @@
                     }
                     
                     if (LOG.isDebugEnabled())
-                        LOG.debug("OpCode {}, fin={} rsv={}{}{}",
+                        LOG.debug("{} OpCode {}, fin={} rsv={}{}{}",
+                                policy.getBehavior(),
                                 OpCode.name(opcode),
                                 fin,
                                 (isRsv1InUse()?'1':'.'),
@@ -412,11 +423,6 @@
                                 throw new ProtocolException("RSV3 not allowed to be set");   
                         }
                     }
-                    else
-                    {
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("OpCode {}, fin={} rsv=000",OpCode.name(opcode),fin);
-                    }
                     
                     state = State.PAYLOAD_LEN;
                     break;
@@ -591,8 +597,9 @@
             buffer.limit(limit);
             buffer.position(buffer.position() + window.remaining());
 
-            if (LOG.isDebugEnabled())
-                LOG.debug("Window: {}",BufferUtil.toDetailString(window));
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("{} Window: {}",policy.getBehavior(),BufferUtil.toDetailString(window));
+            }
 
             maskProcessor.process(window);
 
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java
index e038e4d..04637e3 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java
@@ -41,6 +41,7 @@
 import org.eclipse.jetty.websocket.api.SuspendToken;
 import org.eclipse.jetty.websocket.api.UpgradeRequest;
 import org.eclipse.jetty.websocket.api.UpgradeResponse;
+import org.eclipse.jetty.websocket.api.WebSocketBehavior;
 import org.eclipse.jetty.websocket.api.WebSocketException;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
@@ -90,20 +91,19 @@
     @Override
     public void close()
     {
-        this.close(StatusCode.NORMAL, null);
+        this.close(StatusCode.NORMAL,null);
     }
 
     @Override
     public void close(CloseStatus closeStatus)
     {
-        this.close(closeStatus.getCode(), closeStatus.getPhrase());
+        this.close(closeStatus.getCode(),closeStatus.getPhrase());
     }
 
     @Override
     public void close(int statusCode, String reason)
     {
-        connection.close(statusCode, reason);
-        notifyClose(statusCode, reason);
+        connection.close(statusCode,reason);
     }
 
     /**
@@ -115,7 +115,7 @@
         connection.disconnect();
 
         // notify of harsh disconnect
-        notifyClose(StatusCode.NO_CLOSE, "Harsh disconnect");
+        notifyClose(StatusCode.NO_CLOSE,"Harsh disconnect");
     }
 
     public void dispatch(Runnable runnable)
@@ -130,7 +130,7 @@
         out.append(indent).append(" +- incomingHandler : ");
         if (incomingHandler instanceof Dumpable)
         {
-            ((Dumpable)incomingHandler).dump(out, indent + "    ");
+            ((Dumpable)incomingHandler).dump(out,indent + "    ");
         }
         else
         {
@@ -140,7 +140,7 @@
         out.append(indent).append(" +- outgoingHandler : ");
         if (outgoingHandler instanceof Dumpable)
         {
-            ((Dumpable)outgoingHandler).dump(out, indent + "    ");
+            ((Dumpable)outgoingHandler).dump(out,indent + "    ");
         }
         else
         {
@@ -273,7 +273,7 @@
     {
         final int prime = 31;
         int result = 1;
-        result = (prime * result) + ((connection == null) ? 0 : connection.hashCode());
+        result = (prime * result) + ((connection == null)?0:connection.hashCode());
         return result;
     }
 
@@ -328,7 +328,11 @@
 
     public void notifyClose(int statusCode, String reason)
     {
-        websocket.onClose(new CloseInfo(statusCode, reason));
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("notifyClose({},{})",statusCode,reason);
+        }
+        websocket.onClose(new CloseInfo(statusCode,reason));
     }
 
     public void notifyError(Throwable cause)
@@ -342,12 +346,13 @@
     {
         switch (state)
         {
-            case CLOSING:
+            case CLOSED:
                 // notify session listeners
                 for (SessionListener listener : sessionListeners)
                 {
                     try
                     {
+                        LOG.debug("{}.onSessionClosed()",listener.getClass().getSimpleName());
                         listener.onSessionClosed(this);
                     }
                     catch (Throwable t)
@@ -355,18 +360,10 @@
                         LOG.ignore(t);
                     }
                 }
-                break;
-            case CLOSED:
                 IOState ioState = this.connection.getIOState();
-                // The session only cares about abnormal close, as we need to notify
-                // the endpoint of this close scenario.
-                if (ioState.wasAbnormalClose())
-                {
-                    CloseInfo close = ioState.getCloseInfo();
-                    LOG.debug("Detected abnormal close: {}", close);
-                    // notify local endpoint
-                    notifyClose(close.getStatusCode(), close.getReason());
-                }
+                CloseInfo close = ioState.getCloseInfo();
+                // confirmed close of local endpoint
+                notifyClose(close.getStatusCode(),close.getReason());
                 break;
             case OPEN:
                 // notify session listeners
@@ -400,17 +397,32 @@
         connection.getIOState().onConnected();
 
         // Connect remote
-        remote = new WebSocketRemoteEndpoint(connection, outgoingHandler, getBatchMode());
+        remote = new WebSocketRemoteEndpoint(connection,outgoingHandler,getBatchMode());
 
-        // Open WebSocket
-        websocket.openSession(this);
-
-        // Open connection
-        connection.getIOState().onOpened();
-
-        if (LOG.isDebugEnabled())
+        try
         {
-            LOG.debug("open -> {}", dump());
+            // Open WebSocket
+            websocket.openSession(this);
+
+            // Open connection
+            connection.getIOState().onOpened();
+
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("open -> {}",dump());
+            }
+        }
+        catch (Throwable t)
+        {
+            // Exception on end-user WS-Endpoint.
+            // Fast-fail & close connection with reason.
+            int statusCode = StatusCode.SERVER_ERROR;
+            if(policy.getBehavior() == WebSocketBehavior.CLIENT)
+            {
+                statusCode = StatusCode.POLICY_VIOLATION;
+            }
+            
+            close(statusCode,t.getMessage());
         }
     }
 
@@ -450,11 +462,11 @@
                 List<String> values = entry.getValue();
                 if (values != null)
                 {
-                    this.parameterMap.put(entry.getKey(), values.toArray(new String[values.size()]));
+                    this.parameterMap.put(entry.getKey(),values.toArray(new String[values.size()]));
                 }
                 else
                 {
-                    this.parameterMap.put(entry.getKey(), new String[0]);
+                    this.parameterMap.put(entry.getKey(),new String[0]);
                 }
             }
         }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java
index 46a6612..7a55b2d 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/AbstractEventDriver.java
@@ -88,13 +88,7 @@
     {
         if (LOG.isDebugEnabled())
         {
-            LOG.debug("incoming(WebSocketException)",e);
-        }
-
-        if (e instanceof CloseException)
-        {
-            CloseException close = (CloseException)e;
-            terminateConnection(close.getStatusCode(),close.getMessage());
+            LOG.debug("incomingError(" + e.getClass().getName() + ")",e);
         }
 
         onError(e);
@@ -105,7 +99,7 @@
     {
         if (LOG.isDebugEnabled())
         {
-            LOG.debug("{}.onFrame({})",websocket.getClass().getSimpleName(),frame);
+            LOG.debug("incomingFrame({})",frame);
         }
 
         try
@@ -121,9 +115,6 @@
                     CloseFrame closeframe = (CloseFrame)frame;
                     CloseInfo close = new CloseInfo(closeframe,validate);
 
-                    // notify user websocket pojo
-                    onClose(close);
-
                     // process handshake
                     session.getConnection().getIOState().onCloseRemote(close);
 
@@ -229,6 +220,7 @@
         catch (Throwable t)
         {
             unhandled(t);
+            throw t;
         }
     }
 
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java
index 1576339..cea5909 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java
@@ -32,7 +32,6 @@
 
 import org.eclipse.jetty.io.AbstractConnection;
 import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.Connection;
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.BufferUtil;
 import org.eclipse.jetty.util.StringUtil;
@@ -54,13 +53,13 @@
 import org.eclipse.jetty.websocket.common.ConnectionState;
 import org.eclipse.jetty.websocket.common.Generator;
 import org.eclipse.jetty.websocket.common.LogicalConnection;
+import org.eclipse.jetty.websocket.common.OpCode;
 import org.eclipse.jetty.websocket.common.Parser;
 import org.eclipse.jetty.websocket.common.WebSocketSession;
 import org.eclipse.jetty.websocket.common.io.IOState.ConnectionStateListener;
 
 /**
- * Provides the implementation of {@link LogicalConnection} within the
- * framework of the new {@link Connection} framework of {@code jetty-io}.
+ * Provides the implementation of {@link LogicalConnection} within the framework of the new {@link Connection} framework of {@code jetty-io}.
  */
 public abstract class AbstractWebSocketConnection extends AbstractConnection implements LogicalConnection, ConnectionStateListener, Dumpable
 {
@@ -68,7 +67,7 @@
     {
         private Flusher(ByteBufferPool bufferPool, Generator generator, EndPoint endpoint)
         {
-            super(bufferPool, generator, endpoint, getPolicy().getMaxBinaryMessageBufferSize(), 8);
+            super(bufferPool,generator,endpoint,getPolicy().getMaxBinaryMessageBufferSize(),8);
         }
 
         @Override
@@ -106,7 +105,7 @@
             // Abnormal Close
             reason = CloseStatus.trimMaxReasonLength(reason);
             session.notifyError(x);
-            session.notifyClose(StatusCode.NO_CLOSE,reason);
+            session.notifyClose(StatusCode.ABNORMAL,reason);
 
             disconnect(); // disconnect endpoint & connection
         }
@@ -114,16 +113,23 @@
 
     public class OnDisconnectCallback implements WriteCallback
     {
+        private final boolean outputOnly;
+
+        public OnDisconnectCallback(boolean outputOnly)
+        {
+            this.outputOnly = outputOnly;
+        }
+
         @Override
         public void writeFailed(Throwable x)
         {
-            disconnect();
+            disconnect(outputOnly);
         }
 
         @Override
         public void writeSuccess()
         {
-            disconnect();
+            disconnect(outputOnly);
         }
     }
 
@@ -212,10 +218,10 @@
     @Override
     public void close(int statusCode, String reason)
     {
+        LOG.debug("close({},{})",statusCode,reason);
         CloseInfo close = new CloseInfo(statusCode,reason);
         if (statusCode == StatusCode.ABNORMAL)
         {
-            flusher.close(); // TODO this makes the IdleTimeoutTest pass, but I'm dubious it is the correct way
             ioState.onAbnormalClose(close);
         }
         else
@@ -224,7 +230,6 @@
         }
     }
 
-
     @Override
     public void disconnect()
     {
@@ -360,7 +365,9 @@
     @Override
     public void onClose()
     {
+        LOG.debug("{} onClose()",policy.getBehavior());
         super.onClose();
+        // ioState.onDisconnected();
         flusher.close();
     }
 
@@ -379,18 +386,15 @@
                 {
                     // Fire out a close frame, indicating abnormal shutdown, then disconnect
                     CloseInfo abnormal = new CloseInfo(StatusCode.SHUTDOWN,"Abnormal Close - " + ioState.getCloseInfo().getReason());
-                    outgoingFrame(abnormal.asFrame(),new OnDisconnectCallback(), BatchMode.OFF);
+                    outgoingFrame(abnormal.asFrame(),new OnDisconnectCallback(false),BatchMode.OFF);
                 }
-                else
-                {
-                    // Just disconnect
-                    this.disconnect();
-                }
+                // Just disconnect
+                this.disconnect(false);
                 break;
             case CLOSING:
                 CloseInfo close = ioState.getCloseInfo();
-                // append close frame
-                outgoingFrame(close.asFrame(),new OnDisconnectCallback(), BatchMode.OFF);
+                // reply to close handshake from remote
+                outgoingFrame(close.asFrame(),new OnDisconnectCallback(true),BatchMode.OFF);
             default:
                 break;
         }
@@ -441,20 +445,26 @@
     @Override
     protected boolean onReadTimeout()
     {
-        LOG.debug("{} Read Timeout",policy.getBehavior());
-
         IOState state = getIOState();
-        if ((state.getConnectionState() == ConnectionState.CLOSING) || (state.getConnectionState() == ConnectionState.CLOSED))
+        ConnectionState cstate = state.getConnectionState();
+        LOG.debug("{} Read Timeout - {}",policy.getBehavior(),cstate);
+
+        if (cstate == ConnectionState.CLOSED)
         {
-            // close already initiated, extra timeouts not relevant
+            // close already completed, extra timeouts not relevant
             // allow underlying connection and endpoint to disconnect on its own
             return true;
         }
 
-        // Initiate close - politely send close frame.
-        session.notifyError(new SocketTimeoutException("Timeout on Read"));
-        // This is an Abnormal Close condition
-        close(StatusCode.ABNORMAL,"Idle Timeout");
+        try
+        {
+            session.notifyError(new SocketTimeoutException("Timeout on Read"));
+        }
+        finally
+        {
+            // This is an Abnormal Close condition
+            close(StatusCode.ABNORMAL,"Idle Timeout");
+        }
 
         return false;
     }
@@ -470,7 +480,21 @@
             LOG.debug("outgoingFrame({}, {})",frame,callback);
         }
 
-        flusher.enqueue(frame,callback, batchMode);
+        CloseInfo close = null;
+        // grab a copy of the frame details before masking and whatnot
+        if (frame.getOpCode() == OpCode.CLOSE)
+        {
+            close = new CloseInfo(frame);
+        }
+
+        flusher.enqueue(frame,callback,batchMode);
+
+        // now trigger local close
+        if (close != null)
+        {
+            LOG.debug("outgoing CLOSE frame - {}: {}",frame,close);
+            ioState.onCloseLocal(close);
+        }
     }
 
     private int read(ByteBuffer buffer)
@@ -498,7 +522,6 @@
                         LOG.debug("Filled {} bytes - {}",filled,BufferUtil.toDetailString(buffer));
                     }
                     parser.parse(buffer);
-                    // TODO: has the end user application already consumed what it was given?
                 }
             }
         }
@@ -514,6 +537,12 @@
             close(e.getStatusCode(),e.getMessage());
             return -1;
         }
+        catch (Throwable t)
+        {
+            LOG.warn(t);
+            close(StatusCode.ABNORMAL,t.getMessage());
+            return -1;
+        }
     }
 
     @Override
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java
index a7bb3cb..6e995e5 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/FrameFlusher.java
@@ -29,7 +29,6 @@
 import org.eclipse.jetty.io.EndPoint;
 import org.eclipse.jetty.util.ArrayQueue;
 import org.eclipse.jetty.util.BufferUtil;
-import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.IteratingCallback;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -45,246 +44,22 @@
  */
 public class FrameFlusher
 {
-    public static final BinaryFrame FLUSH_FRAME = new BinaryFrame();
-    private static final Logger LOG = Log.getLogger(FrameFlusher.class);
-
-    private final ByteBufferPool bufferPool;
-    private final EndPoint endpoint;
-    private final int bufferSize;
-    private final Generator generator;
-    private final int maxGather;
-    private final Object lock = new Object();
-    private final ArrayQueue<FrameEntry> queue = new ArrayQueue<>(16, 16, lock);
-    private final Flusher flusher = new Flusher();
-    private final AtomicBoolean closed = new AtomicBoolean();
-    private volatile Throwable failure;
-
-    public FrameFlusher(ByteBufferPool bufferPool, Generator generator, EndPoint endpoint, int bufferSize, int maxGather)
-    {
-        this.bufferPool = bufferPool;
-        this.endpoint = endpoint;
-        this.bufferSize = bufferSize;
-        this.generator = Objects.requireNonNull(generator);
-        this.maxGather = maxGather;
-    }
-
-    public void enqueue(Frame frame, WriteCallback callback, BatchMode batchMode)
-    {
-        if (closed.get())
-        {
-            notifyCallbackFailure(callback, new EOFException("Connection has been closed locally"));
-            return;
-        }
-        if (flusher.isFailed())
-        {
-            notifyCallbackFailure(callback, failure);
-            return;
-        }
-
-        FrameEntry entry = new FrameEntry(frame, callback, batchMode);
-
-        synchronized (lock)
-        {
-            switch (frame.getOpCode())
-            {
-                case OpCode.PING:
-                {
-                    // Prepend PINGs so they are processed first.
-                    queue.add(0, entry);
-                    break;
-                }
-                case OpCode.CLOSE:
-                {
-                    // There may be a chance that other frames are
-                    // added after this close frame, but we will
-                    // fail them later to keep it simple here.
-                    closed.set(true);
-                    queue.add(entry);
-                    break;
-                }
-                default:
-                {
-                    queue.add(entry);
-                    break;
-                }
-            }
-        }
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("{} queued {}", this, entry);
-
-        flusher.iterate();
-    }
-
-    public void close()
-    {
-        if (closed.compareAndSet(false, true))
-        {
-            LOG.debug("{} closing {}", this);
-            EOFException eof = new EOFException("Connection has been closed locally");
-            flusher.failed(eof);
-
-            // Fail also queued entries.
-            List<FrameEntry> entries = new ArrayList<>();
-            synchronized (lock)
-            {
-                entries.addAll(queue);
-                queue.clear();
-            }
-            // Notify outside sync block.
-            for (FrameEntry entry : entries)
-                notifyCallbackFailure(entry.callback, eof);
-        }
-    }
-
-    protected void onFailure(Throwable x)
-    {
-        LOG.warn(x);
-    }
-
-    protected void notifyCallbackSuccess(WriteCallback callback)
-    {
-        try
-        {
-            if (callback != null)
-                callback.writeSuccess();
-        }
-        catch (Throwable x)
-        {
-            LOG.debug("Exception while notifying success of callback " + callback, x);
-        }
-    }
-
-    protected void notifyCallbackFailure(WriteCallback callback, Throwable failure)
-    {
-        try
-        {
-            if (callback != null)
-                callback.writeFailed(failure);
-        }
-        catch (Throwable x)
-        {
-            LOG.debug("Exception while notifying failure of callback " + callback, x);
-        }
-    }
-
-    @Override
-    public String toString()
-    {
-        ByteBuffer aggregate = flusher.aggregate;
-        return String.format("%s[queueSize=%d,aggregateSize=%d,failure=%s]",
-                getClass().getSimpleName(),
-                queue.size(),
-                aggregate == null ? 0 : aggregate.position(),
-                failure);
-    }
-
     private class Flusher extends IteratingCallback
     {
         private final List<FrameEntry> entries = new ArrayList<>(maxGather);
-        private final List<ByteBuffer> buffers = new ArrayList<>(maxGather * 2 + 1);
+        private final List<ByteBuffer> buffers = new ArrayList<>((maxGather * 2) + 1);
         private ByteBuffer aggregate;
         private BatchMode batchMode;
 
-        @Override
-        protected Action process() throws Exception
-        {
-            int space = aggregate == null ? bufferSize : BufferUtil.space(aggregate);
-            BatchMode currentBatchMode = BatchMode.AUTO;
-            synchronized (lock)
-            {
-                while (entries.size() <= maxGather && !queue.isEmpty())
-                {
-                    FrameEntry entry = queue.remove(0);
-                    currentBatchMode = BatchMode.max(currentBatchMode, entry.batchMode);
-
-                    // Force flush if we need to.
-                    if (entry.frame == FLUSH_FRAME)
-                        currentBatchMode = BatchMode.OFF;
-
-                    int payloadLength = BufferUtil.length(entry.frame.getPayload());
-                    int approxFrameLength = Generator.MAX_HEADER_LENGTH + payloadLength;
-
-                    // If it is a "big" frame, avoid copying into the aggregate buffer.
-                    if (approxFrameLength > (bufferSize >> 2))
-                        currentBatchMode = BatchMode.OFF;
-
-                    // If the aggregate buffer overflows, do not batch.
-                    space -= approxFrameLength;
-                    if (space <= 0)
-                        currentBatchMode = BatchMode.OFF;
-
-                    entries.add(entry);
-                }
-            }
-
-            if (LOG.isDebugEnabled())
-                LOG.debug("{} processing {} entries: {}", FrameFlusher.this, entries.size(), entries);
-
-            if (entries.isEmpty())
-            {
-                if (batchMode != BatchMode.AUTO)
-                {
-                    // Nothing more to do, release the aggregate buffer if we need to.
-                    // Releasing it here rather than in succeeded() allows for its reuse.
-                    releaseAggregate();
-                    return Action.IDLE;
-                }
-
-                LOG.debug("{} auto flushing", FrameFlusher.this);
-                return flush();
-            }
-
-            batchMode = currentBatchMode;
-
-            return currentBatchMode == BatchMode.OFF ? flush() : batch();
-        }
-
-        private Action flush()
-        {
-            if (!BufferUtil.isEmpty(aggregate))
-            {
-                buffers.add(aggregate);
-                if (LOG.isDebugEnabled())
-                    LOG.debug("{} flushing aggregate {}", FrameFlusher.this, aggregate);
-            }
-
-            // Do not allocate the iterator here.
-            for (int i = 0; i < entries.size(); ++i)
-            {
-                FrameEntry entry = entries.get(i);
-                // Skip the "synthetic" frame used for flushing.
-                if (entry.frame == FLUSH_FRAME)
-                    continue;
-                buffers.add(entry.generateHeaderBytes());
-                ByteBuffer payload = entry.frame.getPayload();
-                if (BufferUtil.hasContent(payload))
-                    buffers.add(payload);
-            }
-
-            if (LOG.isDebugEnabled())
-                LOG.debug("{} flushing {} frames: {}", FrameFlusher.this, entries.size(), entries);
-
-            if (buffers.isEmpty())
-            {
-                releaseAggregate();
-                // We may have the FLUSH_FRAME to notify.
-                succeedEntries();
-                return Action.IDLE;
-            }
-
-            endpoint.write(this, buffers.toArray(new ByteBuffer[buffers.size()]));
-            buffers.clear();
-            return Action.SCHEDULED;
-        }
-
         private Action batch()
         {
             if (aggregate == null)
             {
-                aggregate = bufferPool.acquire(bufferSize, true);
+                aggregate = bufferPool.acquire(bufferSize,true);
                 if (LOG.isDebugEnabled())
-                    LOG.debug("{} acquired aggregate buffer {}", FrameFlusher.this, aggregate);
+                {
+                    LOG.debug("{} acquired aggregate buffer {}",FrameFlusher.this,aggregate);
+                }
             }
 
             // Do not allocate the iterator here.
@@ -296,17 +71,149 @@
 
                 ByteBuffer payload = entry.frame.getPayload();
                 if (BufferUtil.hasContent(payload))
-                    BufferUtil.append(aggregate, payload);
+                {
+                    BufferUtil.append(aggregate,payload);
+                }
             }
             if (LOG.isDebugEnabled())
-                LOG.debug("{} aggregated {} frames: {}", FrameFlusher.this, entries.size(), entries);
+            {
+                LOG.debug("{} aggregated {} frames: {}",FrameFlusher.this,entries.size(),entries);
+            }
             succeeded();
             return Action.SCHEDULED;
         }
 
+        @Override
+        protected void completed()
+        {
+            // This IteratingCallback never completes.
+        }
+
+        @Override
+        public void failed(Throwable x)
+        {
+            for (FrameEntry entry : entries)
+            {
+                notifyCallbackFailure(entry.callback,x);
+                entry.release();
+            }
+            entries.clear();
+            super.failed(x);
+            failure = x;
+            onFailure(x);
+        }
+
+        private Action flush()
+        {
+            if (!BufferUtil.isEmpty(aggregate))
+            {
+                buffers.add(aggregate);
+                if (LOG.isDebugEnabled())
+                {
+                    LOG.debug("{} flushing aggregate {}",FrameFlusher.this,aggregate);
+                }
+            }
+
+            // Do not allocate the iterator here.
+            for (int i = 0; i < entries.size(); ++i)
+            {
+                FrameEntry entry = entries.get(i);
+                // Skip the "synthetic" frame used for flushing.
+                if (entry.frame == FLUSH_FRAME)
+                {
+                    continue;
+                }
+                buffers.add(entry.generateHeaderBytes());
+                ByteBuffer payload = entry.frame.getPayload();
+                if (BufferUtil.hasContent(payload))
+                {
+                    buffers.add(payload);
+                }
+            }
+
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("{} flushing {} frames: {}",FrameFlusher.this,entries.size(),entries);
+            }
+
+            if (buffers.isEmpty())
+            {
+                releaseAggregate();
+                // We may have the FLUSH_FRAME to notify.
+                succeedEntries();
+                return Action.IDLE;
+            }
+
+            endpoint.write(this,buffers.toArray(new ByteBuffer[buffers.size()]));
+            buffers.clear();
+            return Action.SCHEDULED;
+        }
+
+        @Override
+        protected Action process() throws Exception
+        {
+            int space = aggregate == null?bufferSize:BufferUtil.space(aggregate);
+            BatchMode currentBatchMode = BatchMode.AUTO;
+            synchronized (lock)
+            {
+                while ((entries.size() <= maxGather) && !queue.isEmpty())
+                {
+                    FrameEntry entry = queue.remove(0);
+                    currentBatchMode = BatchMode.max(currentBatchMode,entry.batchMode);
+
+                    // Force flush if we need to.
+                    if (entry.frame == FLUSH_FRAME)
+                    {
+                        currentBatchMode = BatchMode.OFF;
+                    }
+
+                    int payloadLength = BufferUtil.length(entry.frame.getPayload());
+                    int approxFrameLength = Generator.MAX_HEADER_LENGTH + payloadLength;
+
+                    // If it is a "big" frame, avoid copying into the aggregate buffer.
+                    if (approxFrameLength > (bufferSize >> 2))
+                    {
+                        currentBatchMode = BatchMode.OFF;
+                    }
+
+                    // If the aggregate buffer overflows, do not batch.
+                    space -= approxFrameLength;
+                    if (space <= 0)
+                    {
+                        currentBatchMode = BatchMode.OFF;
+                    }
+
+                    entries.add(entry);
+                }
+            }
+
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("{} processing {} entries: {}",FrameFlusher.this,entries.size(),entries);
+            }
+
+            if (entries.isEmpty())
+            {
+                if (batchMode != BatchMode.AUTO)
+                {
+                    // Nothing more to do, release the aggregate buffer if we need to.
+                    // Releasing it here rather than in succeeded() allows for its reuse.
+                    releaseAggregate();
+                    return Action.IDLE;
+                }
+
+                LOG.debug("{} auto flushing",FrameFlusher.this);
+                return flush();
+            }
+
+            batchMode = currentBatchMode;
+
+            return currentBatchMode == BatchMode.OFF?flush():batch();
+        }
+
         private void releaseAggregate()
         {
-            if (aggregate != null && BufferUtil.isEmpty(aggregate))
+            if ((aggregate != null) && BufferUtil.isEmpty(aggregate))
             {
                 bufferPool.release(aggregate);
                 aggregate = null;
@@ -331,26 +238,6 @@
             }
             entries.clear();
         }
-
-        @Override
-        protected void completed()
-        {
-            // This IteratingCallback never completes.
-        }
-
-        @Override
-        public void failed(Throwable x)
-        {
-            for (FrameEntry entry : entries)
-            {
-                notifyCallbackFailure(entry.callback, x);
-                entry.release();
-            }
-            entries.clear();
-            super.failed(x);
-            failure = x;
-            onFailure(x);
-        }
     }
 
     private class FrameEntry
@@ -374,7 +261,7 @@
 
         private void generateHeaderBytes(ByteBuffer buffer)
         {
-            generator.generateHeaderBytes(frame, buffer);
+            generator.generateHeaderBytes(frame,buffer);
         }
 
         private void release()
@@ -389,7 +276,145 @@
         @Override
         public String toString()
         {
-            return String.format("%s[%s,%s,%s,%s]", getClass().getSimpleName(), frame, callback, batchMode, failure);
+            return String.format("%s[%s,%s,%s,%s]",getClass().getSimpleName(),frame,callback,batchMode,failure);
         }
     }
+
+    public static final BinaryFrame FLUSH_FRAME = new BinaryFrame();
+    private static final Logger LOG = Log.getLogger(FrameFlusher.class);
+    private final ByteBufferPool bufferPool;
+    private final EndPoint endpoint;
+    private final int bufferSize;
+    private final Generator generator;
+    private final int maxGather;
+    private final Object lock = new Object();
+    private final ArrayQueue<FrameEntry> queue = new ArrayQueue<>(16,16,lock);
+    private final Flusher flusher = new Flusher();
+    private final AtomicBoolean closed = new AtomicBoolean();
+    private volatile Throwable failure;
+
+    public FrameFlusher(ByteBufferPool bufferPool, Generator generator, EndPoint endpoint, int bufferSize, int maxGather)
+    {
+        this.bufferPool = bufferPool;
+        this.endpoint = endpoint;
+        this.bufferSize = bufferSize;
+        this.generator = Objects.requireNonNull(generator);
+        this.maxGather = maxGather;
+    }
+
+    public void close()
+    {
+        if (closed.compareAndSet(false,true))
+        {
+            LOG.debug("{} closing {}",this);
+            EOFException eof = new EOFException("Connection has been closed locally");
+            flusher.failed(eof);
+
+            // Fail also queued entries.
+            List<FrameEntry> entries = new ArrayList<>();
+            synchronized (lock)
+            {
+                entries.addAll(queue);
+                queue.clear();
+            }
+            // Notify outside sync block.
+            for (FrameEntry entry : entries)
+            {
+                notifyCallbackFailure(entry.callback,eof);
+            }
+        }
+    }
+
+    public void enqueue(Frame frame, WriteCallback callback, BatchMode batchMode)
+    {
+        if (closed.get())
+        {
+            notifyCallbackFailure(callback,new EOFException("Connection has been closed locally"));
+            return;
+        }
+        if (flusher.isFailed())
+        {
+            notifyCallbackFailure(callback,failure);
+            return;
+        }
+
+        FrameEntry entry = new FrameEntry(frame,callback,batchMode);
+
+        synchronized (lock)
+        {
+            switch (frame.getOpCode())
+            {
+                case OpCode.PING:
+                {
+                    // Prepend PINGs so they are processed first.
+                    queue.add(0,entry);
+                    break;
+                }
+                case OpCode.CLOSE:
+                {
+                    // There may be a chance that other frames are
+                    // added after this close frame, but we will
+                    // fail them later to keep it simple here.
+                    closed.set(true);
+                    queue.add(entry);
+                    break;
+                }
+                default:
+                {
+                    queue.add(entry);
+                    break;
+                }
+            }
+        }
+
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("{} queued {}",this,entry);
+        }
+
+        flusher.iterate();
+    }
+
+    protected void notifyCallbackFailure(WriteCallback callback, Throwable failure)
+    {
+        try
+        {
+            if (callback != null)
+            {
+                callback.writeFailed(failure);
+            }
+        }
+        catch (Throwable x)
+        {
+            LOG.debug("Exception while notifying failure of callback " + callback,x);
+        }
+    }
+
+    protected void notifyCallbackSuccess(WriteCallback callback)
+    {
+        try
+        {
+            if (callback != null)
+            {
+                callback.writeSuccess();
+            }
+        }
+        catch (Throwable x)
+        {
+            LOG.debug("Exception while notifying success of callback " + callback,x);
+        }
+    }
+
+    protected void onFailure(Throwable x)
+    {
+        LOG.warn(x);
+    }
+
+    @Override
+    public String toString()
+    {
+        ByteBuffer aggregate = flusher.aggregate;
+        return String.format("%s[queueSize=%d,aggregateSize=%d,failure=%s]",getClass().getSimpleName(),queue.size(),aggregate == null?0:aggregate.position(),
+                failure);
+    }
 }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java
index 6d4dbc4..815d2d3 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/IOState.java
@@ -139,6 +139,10 @@
     {
         for (ConnectionStateListener listener : listeners)
         {
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("{}.onConnectionStateChange({})",listener.getClass().getSimpleName(),state.name());
+            }
             listener.onConnectionStateChange(state);
         }
     }
@@ -166,8 +170,7 @@
             }
 
             this.state = ConnectionState.CLOSED;
-            if (closeInfo == null)
-                this.closeInfo = close;
+            this.closeInfo = close;
             this.inputAvailable = false;
             this.outputAvailable = false;
             this.closeHandshakeSource = CloseHandshakeSource.ABNORMAL;
@@ -193,16 +196,16 @@
 
         if (initialState == ConnectionState.CONNECTED)
         {
-            // fast close. a local close request from end-user onConnected() method
+            // fast close. a local close request from end-user onConnect/onOpen method
             LOG.debug("FastClose in CONNECTED detected");
             // Force the state open (to allow read/write to endpoint)
             onOpened();
+            LOG.debug("FastClose continuing with Closure");
         }
 
         synchronized (this)
         {
-            if (closeInfo == null)
-                closeInfo = close;
+            closeInfo = close;
 
             boolean in = inputAvailable;
             boolean out = outputAvailable;
@@ -236,7 +239,6 @@
             LOG.debug("notifying state listeners: {}",event);
             notifyStateListeners(event);
 
-            /*
             // if abnormal, we don't expect an answer.
             if (close.isAbnormal())
             {
@@ -253,7 +255,6 @@
                 notifyStateListeners(event);
                 return;
             }
-            */
         }
     }
 
@@ -272,8 +273,7 @@
                 return;
             }
 
-            if (closeInfo == null)
-                closeInfo = close;
+            closeInfo = close;
 
             boolean in = inputAvailable;
             boolean out = outputAvailable;
@@ -360,7 +360,7 @@
             // already opened
             return;
         }
-        
+
         if (this.state != ConnectionState.CONNECTED)
         {
             LOG.debug("Unable to open, not in CONNECTED state: {}",this.state);
@@ -394,12 +394,11 @@
                 return;
             }
 
-            CloseInfo close = new CloseInfo(StatusCode.NO_CLOSE,"Read EOF");
+            CloseInfo close = new CloseInfo(StatusCode.ABNORMAL,"Read EOF");
 
             this.cleanClose = false;
             this.state = ConnectionState.CLOSED;
-            if (closeInfo == null)
-                this.closeInfo = close;
+            this.closeInfo = close;
             this.inputAvailable = false;
             this.outputAvailable = false;
             this.closeHandshakeSource = CloseHandshakeSource.ABNORMAL;
@@ -408,6 +407,58 @@
         notifyStateListeners(event);
     }
 
+    public void onDisconnected()
+    {
+        ConnectionState event = null;
+        synchronized (this)
+        {
+            if (this.state == ConnectionState.CLOSED)
+            {
+                // already closed
+                return;
+            }
+
+            CloseInfo close = new CloseInfo(StatusCode.ABNORMAL,"Disconnected");
+
+            this.cleanClose = false;
+            this.state = ConnectionState.CLOSED;
+            this.closeInfo = close;
+            this.inputAvailable = false;
+            this.outputAvailable = false;
+            this.closeHandshakeSource = CloseHandshakeSource.ABNORMAL;
+            event = this.state;
+        }
+        notifyStateListeners(event);
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append(this.getClass().getSimpleName());
+        str.append("@").append(Integer.toHexString(hashCode()));
+        str.append("[").append(state);
+        str.append(',');
+        if (!inputAvailable)
+        {
+            str.append('!');
+        }
+        str.append("in,");
+        if (!outputAvailable)
+        {
+            str.append('!');
+        }
+        str.append("out");
+        if ((state == ConnectionState.CLOSED) || (state == ConnectionState.CLOSING))
+        {
+            str.append(",close=").append(closeInfo);
+            str.append(",clean=").append(cleanClose);
+            str.append(",closeSource=").append(closeHandshakeSource);
+        }
+        str.append(']');
+        return str.toString();
+    }
+
     public boolean wasAbnormalClose()
     {
         return closeHandshakeSource == CloseHandshakeSource.ABNORMAL;
@@ -427,4 +478,5 @@
     {
         return closeHandshakeSource == CloseHandshakeSource.REMOTE;
     }
+
 }
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ReflectUtils.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ReflectUtils.java
index 32b9c1b..2194ec5 100644
--- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ReflectUtils.java
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/util/ReflectUtils.java
@@ -392,4 +392,4 @@
         }
         return name;
     }
-}
+}
\ No newline at end of file
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/CloseInfoTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/CloseInfoTest.java
new file mode 100644
index 0000000..a40e306
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/CloseInfoTest.java
@@ -0,0 +1,166 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.websocket.common;
+
+import static org.eclipse.jetty.websocket.api.StatusCode.*;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.websocket.api.ProtocolException;
+import org.eclipse.jetty.websocket.common.frames.CloseFrame;
+import org.junit.Test;
+
+public class CloseInfoTest
+{
+    /**
+     * A test where no close is provided
+     */
+    @Test
+    public void testAnonymousClose()
+    {
+        CloseInfo close = new CloseInfo();
+        assertThat("close.code",close.getStatusCode(),is(NO_CODE));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        CloseFrame frame = close.asFrame();
+        assertThat("close frame op code",frame.getOpCode(),is(OpCode.CLOSE));
+        // should result in no payload
+        assertThat("close frame has payload",frame.hasPayload(),is(false));
+        assertThat("close frame payload length",frame.getPayloadLength(),is(0));
+    }
+    
+    /**
+     * A test where NO_CODE (1005) is provided
+     */
+    @Test
+    public void testNoCode()
+    {
+        CloseInfo close = new CloseInfo(NO_CODE);
+        assertThat("close.code",close.getStatusCode(),is(NO_CODE));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        CloseFrame frame = close.asFrame();
+        assertThat("close frame op code",frame.getOpCode(),is(OpCode.CLOSE));
+        // should result in no payload
+        assertThat("close frame has payload",frame.hasPayload(),is(false));
+        assertThat("close frame payload length",frame.getPayloadLength(),is(0));
+    }
+    
+    /**
+     * A test where NO_CLOSE (1006) is provided
+     */
+    @Test
+    public void testNoClose()
+    {
+        CloseInfo close = new CloseInfo(NO_CLOSE);
+        assertThat("close.code",close.getStatusCode(),is(NO_CLOSE));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        CloseFrame frame = close.asFrame();
+        assertThat("close frame op code",frame.getOpCode(),is(OpCode.CLOSE));
+        // should result in no payload
+        assertThat("close frame has payload",frame.hasPayload(),is(false));
+        assertThat("close frame payload length",frame.getPayloadLength(),is(0));
+    }
+
+    /**
+     * A test of FAILED_TLS_HANDSHAKE (1007)
+     */
+    @Test
+    public void testFailedTlsHandshake()
+    {
+        CloseInfo close = new CloseInfo(FAILED_TLS_HANDSHAKE);
+        assertThat("close.code",close.getStatusCode(),is(FAILED_TLS_HANDSHAKE));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        try
+        {
+            @SuppressWarnings("unused")
+            CloseFrame frame = close.asFrame();
+            fail("Expected " + ProtocolException.class.getName());
+        }
+        catch (ProtocolException e)
+        {
+            // expected path
+            assertThat("ProtocolException message",e.getMessage(),containsString("not allowed (per RFC6455)"));
+        }
+    }
+
+    /**
+     * A test of NORMAL (1000)
+     */
+    @Test
+    public void testNormal()
+    {
+        CloseInfo close = new CloseInfo(NORMAL);
+        assertThat("close.code",close.getStatusCode(),is(NORMAL));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        CloseFrame frame = close.asFrame();
+        assertThat("close frame op code",frame.getOpCode(),is(OpCode.CLOSE));
+        assertThat("close frame payload length",frame.getPayloadLength(),is(2));
+    }
+    
+    private ByteBuffer asByteBuffer(int statusCode, String reason)
+    {
+        int len = 2; // status code length
+        byte utf[] = null;
+        if (StringUtil.isNotBlank(reason))
+        {
+            utf = StringUtil.getUtf8Bytes(reason);
+            len += utf.length;
+        }
+
+        ByteBuffer buf = BufferUtil.allocate(len);
+        BufferUtil.flipToFill(buf);
+        buf.put((byte)((statusCode >>> 8) & 0xFF));
+        buf.put((byte)((statusCode >>> 0) & 0xFF));
+
+        if (utf != null)
+        {
+            buf.put(utf,0,utf.length);
+        }
+        BufferUtil.flipToFlush(buf,0);
+        
+        return buf;
+    }
+    
+    @Test
+    public void testFromFrame()
+    {
+        ByteBuffer payload = asByteBuffer(NORMAL,null);
+        assertThat("payload length", payload.remaining(), is(2));
+        CloseFrame frame = new CloseFrame();
+        frame.setPayload(payload);
+        
+        // create from frame
+        CloseInfo close = new CloseInfo(frame);
+        assertThat("close.code",close.getStatusCode(),is(NORMAL));
+        assertThat("close.reason",close.getReason(),nullValue());
+
+        // and back again
+        frame = close.asFrame();
+        assertThat("close frame op code",frame.getOpCode(),is(OpCode.CLOSE));
+        assertThat("close frame payload length",frame.getPayloadLength(),is(2));
+    }
+}
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase4.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase4.java
index fe6011d..6b0f018 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase4.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/ab/TestABCase4.java
@@ -21,6 +21,7 @@
 import java.nio.ByteBuffer;
 
 import org.eclipse.jetty.util.log.StacklessLogging;
+import org.eclipse.jetty.websocket.api.ProtocolException;
 import org.eclipse.jetty.websocket.api.WebSocketBehavior;
 import org.eclipse.jetty.websocket.api.WebSocketException;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
@@ -39,8 +40,7 @@
     {
         ByteBuffer expected = ByteBuffer.allocate(32);
 
-        expected.put(new byte[]
-                { (byte)0x8b, 0x00 });
+        expected.put(new byte[] { (byte)0x8b, 0x00 });
 
         expected.flip();
 
@@ -50,10 +50,17 @@
         {
             Parser parser = new UnitParser(policy);
             parser.setIncomingFramesHandler(capture);
-            parser.parse(expected);
+            try
+            {
+                parser.parse(expected);
+            }
+            catch (ProtocolException ignore)
+            {
+                // ignore
+            }
         }
 
-        Assert.assertEquals( "error on undefined opcode", 1, capture.getErrorCount(WebSocketException.class)) ;
+        Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
 
         Throwable known = capture.getErrors().poll();
 
@@ -65,8 +72,7 @@
     {
         ByteBuffer expected = ByteBuffer.allocate(32);
 
-        expected.put(new byte[]
-                { (byte)0x8c, 0x01, 0x00 });
+        expected.put(new byte[] { (byte)0x8c, 0x01, 0x00 });
 
         expected.flip();
 
@@ -76,24 +82,29 @@
         {
             Parser parser = new UnitParser(policy);
             parser.setIncomingFramesHandler(capture);
-            parser.parse(expected);
+            try
+            {
+                parser.parse(expected);
+            }
+            catch (ProtocolException ignore)
+            {
+                // ignore
+            }
         }
 
-        Assert.assertEquals( "error on undefined opcode", 1, capture.getErrorCount(WebSocketException.class)) ;
+        Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
 
         Throwable known = capture.getErrors().poll();
 
         Assert.assertTrue("undefined option should be in message",known.getMessage().contains("Unknown opcode: 12"));
     }
 
-
     @Test
     public void testParserNonControlOpCode3Case4_1_1() throws Exception
     {
         ByteBuffer expected = ByteBuffer.allocate(32);
 
-        expected.put(new byte[]
-                { (byte)0x83, 0x00 });
+        expected.put(new byte[] { (byte)0x83, 0x00 });
 
         expected.flip();
 
@@ -103,10 +114,17 @@
         {
             Parser parser = new UnitParser(policy);
             parser.setIncomingFramesHandler(capture);
-            parser.parse(expected);
+            try
+            {
+                parser.parse(expected);
+            }
+            catch (ProtocolException ignore)
+            {
+                // ignore
+            }
         }
 
-        Assert.assertEquals( "error on undefined opcode", 1, capture.getErrorCount(WebSocketException.class)) ;
+        Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
 
         Throwable known = capture.getErrors().poll();
 
@@ -118,8 +136,7 @@
     {
         ByteBuffer expected = ByteBuffer.allocate(32);
 
-        expected.put(new byte[]
-                { (byte)0x84, 0x01, 0x00 });
+        expected.put(new byte[] { (byte)0x84, 0x01, 0x00 });
 
         expected.flip();
 
@@ -129,10 +146,17 @@
         {
             Parser parser = new UnitParser(policy);
             parser.setIncomingFramesHandler(capture);
-            parser.parse(expected);
+            try
+            {
+                parser.parse(expected);
+            }
+            catch (ProtocolException ignore)
+            {
+                // ignore
+            }
         }
 
-        Assert.assertEquals( "error on undefined opcode", 1, capture.getErrorCount(WebSocketException.class)) ;
+        Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
 
         Throwable known = capture.getErrors().poll();
 
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java
index bd02460..b4634c5 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java
@@ -18,7 +18,8 @@
 
 package org.eclipse.jetty.websocket.common.test;
 
-import java.io.Closeable;
+import static org.hamcrest.Matchers.*;
+
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -68,10 +69,6 @@
 import org.eclipse.jetty.websocket.common.io.http.HttpResponseHeaderParser;
 import org.junit.Assert;
 
-import static org.hamcrest.Matchers.anyOf;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-
 /**
  * A simple websocket client for performing unit tests with.
  * <p>
@@ -84,7 +81,7 @@
  * with regards to basic IO behavior, a write should work as expected, a read should work as expected, but <u>what</u> byte it sends or reads is not within its
  * scope.
  */
-public class BlockheadClient implements IncomingFrames, OutgoingFrames, ConnectionStateListener, Closeable
+public class BlockheadClient implements IncomingFrames, OutgoingFrames, ConnectionStateListener, AutoCloseable
 {
     private static final String REQUEST_HASH_KEY = "dGhlIHNhbXBsZSBub25jZQ==";
     private static final int BUFFER_SIZE = 8192;
@@ -182,22 +179,14 @@
 
     public void close(int statusCode, String message)
     {
+        LOG.debug("close({},{})",statusCode,message);
         CloseInfo close = new CloseInfo(statusCode,message);
 
-        ioState.onCloseLocal(close);
-
         if (!ioState.isClosed())
         {
-            WebSocketFrame frame = close.asFrame();
-            LOG.debug("Issuing: {}",frame);
-            try
-            {
-                write(frame);
-            }
-            catch (IOException e)
-            {
-                LOG.debug(e);
-            }
+            ioState.onCloseLocal(close);
+        } else {
+            LOG.debug("Not issuing close. ioState = {}",ioState);
         }
     }
 
@@ -429,13 +418,8 @@
         {
             LOG.info("Client parsed {} frames",count);
         }
-
-        if (frame.getOpCode() == OpCode.CLOSE)
-        {
-            CloseInfo close = new CloseInfo(frame);
-            ioState.onCloseRemote(close);
-        }
-
+        
+        // Capture Frame Copy
         WebSocketFrame copy = WebSocketFrame.copy(frame);
         incomingFrames.incomingFrame(copy);
     }
@@ -448,6 +432,7 @@
     @Override
     public void onConnectionStateChange(ConnectionState state)
     {
+        LOG.debug("CLIENT onConnectionStateChange() - {}", state);
         switch (state)
         {
             case CLOSED:
@@ -455,10 +440,17 @@
                 // this.disconnect();
                 break;
             case CLOSING:
-                if (ioState.wasRemoteCloseInitiated())
+                CloseInfo close = ioState.getCloseInfo();
+                
+                WebSocketFrame frame = close.asFrame();
+                LOG.debug("Issuing: {}",frame);
+                try
                 {
-                    CloseInfo close = ioState.getCloseInfo();
-                    close(close.getStatusCode(),close.getReason());
+                    write(frame);
+                }
+                catch (IOException e)
+                {
+                    LOG.debug(e);
                 }
                 break;
             default:
@@ -532,8 +524,14 @@
         return len;
     }
 
+    @Deprecated
     public IncomingFramesCapture readFrames(int expectedCount, TimeUnit timeoutUnit, int timeoutDuration) throws IOException, TimeoutException
     {
+        return readFrames(expectedCount,timeoutDuration,timeoutUnit);
+    }
+    
+    public IncomingFramesCapture readFrames(int expectedCount, int timeoutDuration, TimeUnit timeoutUnit) throws IOException, TimeoutException
+    {
         LOG.debug("Read: waiting for {} frame(s) from server",expectedCount);
 
         ByteBuffer buf = bufferPool.acquire(BUFFER_SIZE,false);
@@ -701,6 +699,7 @@
     {
         if (!ioState.isOpen())
         {
+            LOG.debug("IO Not Open / Not Writing: {}",frame);
             return;
         }
         LOG.debug("write(Frame->{}) to {}",frame,outgoing);
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java
index c3cd1ad..1b665bd 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java
@@ -54,6 +54,7 @@
 import org.eclipse.jetty.websocket.api.extensions.Frame;
 import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
 import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
+import org.eclipse.jetty.websocket.api.extensions.Frame.Type;
 import org.eclipse.jetty.websocket.common.AcceptHash;
 import org.eclipse.jetty.websocket.common.CloseInfo;
 import org.eclipse.jetty.websocket.common.Generator;
@@ -124,7 +125,6 @@
         {
             write(new CloseFrame());
             flush();
-            disconnect();
         }
 
         public void close(int statusCode) throws IOException
@@ -132,7 +132,6 @@
             CloseInfo close = new CloseInfo(statusCode);
             write(close.asFrame());
             flush();
-            disconnect();
         }
 
         public void disconnect()
@@ -229,6 +228,19 @@
                 CloseInfo close = new CloseInfo(frame);
                 LOG.debug("Close frame: {}",close);
             }
+
+            Type type = frame.getType();
+            if (echoing.get() && (type.isData() || type.isContinuation()))
+            {
+                try
+                {
+                    write(WebSocketFrame.copy(frame));
+                }
+                catch (IOException e)
+                {
+                    LOG.warn(e);
+                }
+            }
         }
 
         @Override
@@ -317,9 +329,18 @@
             return len;
         }
 
+        /**
+         * @deprecated use {@link #readFrames(int, int, TimeUnit)} for correct parameter order
+         */
+        @Deprecated
         public IncomingFramesCapture readFrames(int expectedCount, TimeUnit timeoutUnit, int timeoutDuration) throws IOException, TimeoutException
         {
-            LOG.debug("Read: waiting for {} frame(s) from server",expectedCount);
+            return readFrames(expectedCount,timeoutDuration,timeoutUnit);
+        }
+
+        public IncomingFramesCapture readFrames(int expectedCount, int timeoutDuration, TimeUnit timeoutUnit) throws IOException, TimeoutException
+        {
+            LOG.debug("Read: waiting for {} frame(s) from client",expectedCount);
             int startCount = incomingFrames.size();
 
             ByteBuffer buf = bufferPool.acquire(BUFFER_SIZE,false);
@@ -562,13 +583,22 @@
         public void write(Frame frame) throws IOException
         {
             LOG.debug("write(Frame->{}) to {}",frame,outgoing);
-            outgoing.outgoingFrame(frame,null, BatchMode.OFF);
+            outgoing.outgoingFrame(frame,null,BatchMode.OFF);
         }
 
         public void write(int b) throws IOException
         {
             getOutputStream().write(b);
         }
+
+        public void write(ByteBuffer buf) throws IOException
+        {
+            byte arr[] = BufferUtil.toArray(buf);
+            if ((arr != null) && (arr.length > 0))
+            {
+                getOutputStream().write(arr);
+            }
+        }
     }
 
     private static final Logger LOG = Log.getLogger(BlockheadServer.class);
diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java
index 3d6c641..8ebfc94 100644
--- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java
+++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/Fuzzer.java
@@ -45,7 +45,7 @@
 /**
  * Fuzzing utility for the AB tests.
  */
-public class Fuzzer
+public class Fuzzer implements AutoCloseable
 {
     public static enum CloseState
     {
@@ -117,8 +117,14 @@
         buf.flip();
         return buf;
     }
+    
+    @Override
+    public void close() throws Exception
+    {
+        this.client.disconnect();
+    }
 
-    public void close()
+    public void disconnect()
     {
         this.client.disconnect();
     }
@@ -187,23 +193,6 @@
         // TODO Should test for no more frames. success if connection closed.
     }
 
-    public void expectServerDisconnect(DisconnectMode mode)
-    {
-        client.expectServerDisconnect();
-        IOState ios = client.getIOState();
-
-        switch (mode)
-        {
-            case CLEAN:
-                Assert.assertTrue(ios.wasRemoteCloseInitiated());
-                Assert.assertTrue(ios.wasCleanClose());
-                break;
-            case UNCLEAN:
-                Assert.assertTrue(ios.wasRemoteCloseInitiated());
-                break;
-        }
-    }
-
     public CloseState getCloseState()
     {
         IOState ios = client.getIOState();
diff --git a/jetty-websocket/websocket-server/pom.xml b/jetty-websocket/websocket-server/pom.xml
index 5288e54..19def58 100644
--- a/jetty-websocket/websocket-server/pom.xml
+++ b/jetty-websocket/websocket-server/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.websocket</groupId>
         <artifactId>websocket-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java
index bebe7c0..f6a2f2c 100644
--- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java
+++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java
@@ -44,6 +44,7 @@
 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
 import org.eclipse.jetty.util.thread.Scheduler;
 import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
+import org.eclipse.jetty.websocket.api.StatusCode;
 import org.eclipse.jetty.websocket.api.WebSocketException;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
 import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
@@ -199,11 +200,23 @@
         }
     }
 
-    protected void closeAllConnections()
+    protected void shutdownAllConnections()
     {
         for (WebSocketSession session : openSessions)
         {
-            session.close();
+            if (session.getConnection() != null)
+            {
+                try
+                {
+                    session.getConnection().close(
+                            StatusCode.SHUTDOWN,
+                            "Shutdown");
+                }
+                catch (Throwable t)
+                {
+                    LOG.debug("During Shutdown All Connections",t);
+                }
+            }
         }
         openSessions.clear();
     }
@@ -269,7 +282,7 @@
     @Override
     protected void doStop() throws Exception
     {
-        closeAllConnections();
+        shutdownAllConnections();
         super.doStop();
     }
 
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java
index 7a6998b..7fdada4 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/IdleTimeoutTest.java
@@ -18,14 +18,22 @@
 
 package org.eclipse.jetty.websocket.server;
 
+import static org.hamcrest.Matchers.*;
+
 import java.util.concurrent.TimeUnit;
 
+import org.eclipse.jetty.websocket.api.StatusCode;
+import org.eclipse.jetty.websocket.common.CloseInfo;
+import org.eclipse.jetty.websocket.common.OpCode;
+import org.eclipse.jetty.websocket.common.WebSocketFrame;
 import org.eclipse.jetty.websocket.common.frames.TextFrame;
 import org.eclipse.jetty.websocket.common.test.BlockheadClient;
+import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
 import org.eclipse.jetty.websocket.server.helper.RFCSocket;
 import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
 import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
 import org.junit.AfterClass;
+import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -83,8 +91,13 @@
             // The server could not read this frame, if it is in this half closed state
             client.write(new TextFrame().setPayload("Hello"));
 
-            // Expect server to be disconnected at this point
-            client.expectServerDisconnect();
+            // Expect server to have closed due to its own timeout
+            IncomingFramesCapture capture = client.readFrames(1,500,TimeUnit.MILLISECONDS);
+            WebSocketFrame frame = capture.getFrames().poll();
+            Assert.assertThat("frame opcode",frame.getOpCode(),is(OpCode.CLOSE));
+            CloseInfo close = new CloseInfo(frame);
+            Assert.assertThat("close code",close.getStatusCode(),is(StatusCode.SHUTDOWN));
+            Assert.assertThat("close reason",close.getReason(),containsString("Timeout"));
         }
         finally
         {
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java
index 0c2ddb3..5a4707c 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java
@@ -114,7 +114,7 @@
         public void onWebSocketConnect(Session sess)
         {
             LOG.debug("onWebSocketConnect({})",sess);
-            sess.close();
+            sess.close(StatusCode.NORMAL,"FastCloseServer");
         }
     }
 
@@ -129,14 +129,10 @@
         public void onWebSocketConnect(Session sess)
         {
             LOG.debug("onWebSocketConnect({})",sess);
+            // Test failure due to unhandled exception
+            // this should trigger a fast-fail closure during open/connect
             throw new RuntimeException("Intentional FastFail");
         }
-
-        @Override
-        public void onWebSocketError(Throwable cause)
-        {
-            errors.add(cause);
-        }
     }
 
     private static final Logger LOG = Log.getLogger(WebSocketCloseTest.class);
@@ -163,30 +159,28 @@
     @Test
     public void testFastClose() throws Exception
     {
-        BlockheadClient client = new BlockheadClient(server.getServerUri());
-        client.setProtocols("fastclose");
-        client.setTimeout(TimeUnit.SECONDS,1);
-        try
+        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
+            client.setProtocols("fastclose");
+            client.setTimeout(TimeUnit.SECONDS,1);
             client.connect();
             client.sendStandardRequest();
             client.expectUpgradeResponse();
 
+            // Verify that client got close frame
             IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1);
             WebSocketFrame frame = capture.getFrames().poll();
             Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
             CloseInfo close = new CloseInfo(frame);
             Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.NORMAL));
-
+            
+            // Notify server of close handshake
             client.write(close.asFrame()); // respond with close
-
+            
+            // ensure server socket got close event
             Assert.assertThat("Fast Close Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
             Assert.assertThat("Fast Close.statusCode",closeSocket.closeStatusCode,is(StatusCode.NORMAL));
         }
-        finally
-        {
-            client.close();
-        }
     }
 
     /**
@@ -195,11 +189,10 @@
     @Test
     public void testFastFail() throws Exception
     {
-        BlockheadClient client = new BlockheadClient(server.getServerUri());
-        client.setProtocols("fastfail");
-        client.setTimeout(TimeUnit.SECONDS,1);
-        try
+        try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
         {
+            client.setProtocols("fastfail");
+            client.setTimeout(TimeUnit.SECONDS,1);
             try (StacklessLogging scope = new StacklessLogging(AbstractEventDriver.class))
             {
                 client.connect();
@@ -214,14 +207,11 @@
 
                 client.write(close.asFrame()); // respond with close
 
+                // ensure server socket got close event
                 Assert.assertThat("Fast Fail Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
                 Assert.assertThat("Fast Fail.statusCode",closeSocket.closeStatusCode,is(StatusCode.SERVER_ERROR));
                 Assert.assertThat("Fast Fail.errors",closeSocket.errors.size(),is(1));
             }
         }
-        finally
-        {
-            client.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java
index 20ff642..502fb80 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java
@@ -48,18 +48,12 @@
         expect.add(new TextFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -81,18 +75,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -114,18 +102,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -147,18 +129,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -180,18 +156,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -213,18 +183,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -246,18 +210,12 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -284,19 +242,13 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.SLOW);
             fuzzer.setSlowSendSegmentSize(segmentSize);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -314,18 +266,12 @@
         expect.add(new BinaryFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -347,18 +293,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -380,18 +320,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -413,18 +347,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -446,18 +374,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -479,18 +401,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -512,18 +428,12 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 
@@ -550,19 +460,13 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(SendMode.SLOW);
             fuzzer.setSlowSendSegmentSize(segmentSize);
             fuzzer.send(send);
             fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
         }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java
index 0e43806..4a1a1c1 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase2.java
@@ -50,18 +50,13 @@
 
         WebSocketFrame expect = new PongFrame();
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -89,18 +84,13 @@
         send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -128,8 +118,7 @@
         send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -137,10 +126,6 @@
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -159,18 +144,13 @@
         expect.add(new PongFrame().setPayload(copyOf(payload)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -179,8 +159,7 @@
     @Test
     public void testCase2_3() throws Exception
     {
-        byte payload[] = new byte[]
-        { 0x00, (byte)0xFF, (byte)0xFE, (byte)0xFD, (byte)0xFC, (byte)0xFB, 0x00, (byte)0xFF };
+        byte payload[] = new byte[] { 0x00, (byte)0xFF, (byte)0xFE, (byte)0xFD, (byte)0xFC, (byte)0xFB, 0x00, (byte)0xFF };
 
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new PingFrame().setPayload(payload));
@@ -190,18 +169,13 @@
         expect.add(new PongFrame().setPayload(copyOf(payload)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -221,18 +195,13 @@
         expect.add(new PongFrame().setPayload(copyOf(payload)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -241,32 +210,26 @@
     @Test
     public void testCase2_5() throws Exception
     {
-        try(StacklessLogging scope = new StacklessLogging(Parser.class))
+        try (StacklessLogging scope = new StacklessLogging(Parser.class))
         {
             byte payload[] = new byte[126]; // intentionally too big
             Arrays.fill(payload,(byte)'5');
             ByteBuffer buf = ByteBuffer.wrap(payload);
-    
+
             List<WebSocketFrame> send = new ArrayList<>();
             // trick websocket frame into making extra large payload for ping
             send.add(new BadFrame(OpCode.PING).setPayload(buf));
             send.add(new CloseInfo(StatusCode.NORMAL,"Test 2.5").asFrame());
-    
+
             List<WebSocketFrame> expect = new ArrayList<>();
             expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
-    
-            Fuzzer fuzzer = new Fuzzer(this);
-            try
+
+            try (Fuzzer fuzzer = new Fuzzer(this))
             {
                 fuzzer.connect();
                 fuzzer.setSendMode(Fuzzer.SendMode.BULK);
                 fuzzer.send(send);
                 fuzzer.expect(expect);
-                fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.CLEAN);
-            }
-            finally
-            {
-                fuzzer.close();
             }
         }
     }
@@ -288,8 +251,7 @@
         expect.add(new PongFrame().setPayload(copyOf(payload)));
         expect.add(new CloseInfo(StatusCode.NORMAL,"Test 2.6").asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -297,10 +259,6 @@
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -316,18 +274,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -343,18 +296,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -372,17 +320,12 @@
         expect.add(new PongFrame().setPayload("our ping")); // our pong
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java
index 4b5373b..de9ec69 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java
@@ -51,18 +51,13 @@
 
         WebSocketFrame expect = new CloseInfo(StatusCode.PROTOCOL).asFrame();
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -80,18 +75,13 @@
         expect.add(new TextFrame().setPayload("small")); // echo on good frame
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -109,18 +99,13 @@
         expect.add(new TextFrame().setPayload("small")); // echo on good frame
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -138,8 +123,7 @@
         expect.add(new TextFrame().setPayload("small")); // echo on good frame
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -147,10 +131,6 @@
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -168,18 +148,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -197,18 +172,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -230,17 +200,12 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java
index ffb6961..61f59ad 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java
@@ -53,18 +53,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -82,18 +77,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -111,18 +101,13 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -132,7 +117,7 @@
     public void testCase4_1_4() throws Exception
     {
         ByteBuffer buf = ByteBuffer.wrap(StringUtil.getUtf8Bytes("bad"));
-        
+
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new TextFrame().setPayload("hello"));
         send.add(new BadFrame((byte)6).setPayload(buf)); // intentionally bad
@@ -142,18 +127,13 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -163,7 +143,7 @@
     public void testCase4_1_5() throws Exception
     {
         ByteBuffer buf = ByteBuffer.wrap(StringUtil.getUtf8Bytes("bad"));
-        
+
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new TextFrame().setPayload("hello"));
         send.add(new BadFrame((byte)7).setPayload(buf)); // intentionally bad
@@ -173,18 +153,13 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -199,18 +174,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -220,25 +190,20 @@
     public void testCase4_2_2() throws Exception
     {
         ByteBuffer buf = ByteBuffer.wrap(StringUtil.getUtf8Bytes("bad"));
-        
+
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new BadFrame((byte)12).setPayload(buf)); // intentionally bad
 
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -256,18 +221,13 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -277,7 +237,7 @@
     public void testCase4_2_4() throws Exception
     {
         ByteBuffer buf = ByteBuffer.wrap(StringUtil.getUtf8Bytes("bad"));
-        
+
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new TextFrame().setPayload("hello"));
         send.add(new BadFrame((byte)14).setPayload(buf)); // intentionally bad
@@ -287,18 +247,13 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -308,7 +263,7 @@
     public void testCase4_2_5() throws Exception
     {
         ByteBuffer buf = ByteBuffer.wrap(StringUtil.getUtf8Bytes("bad"));
-        
+
         List<WebSocketFrame> send = new ArrayList<>();
         send.add(new TextFrame().setPayload("hello"));
         send.add(new BadFrame((byte)15).setPayload(buf)); // intentionally bad
@@ -318,17 +273,12 @@
         expect.add(new TextFrame().setPayload("hello")); // echo
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java
index c957fc9..c6349a3 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java
@@ -57,19 +57,14 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
-    }
+            }
 
     /**
      * Send continuation+fin, then text+fin (framewise)
@@ -85,18 +80,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -113,8 +103,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -122,10 +111,6 @@
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -142,18 +127,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -170,18 +150,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -198,8 +173,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -207,10 +181,6 @@
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -230,18 +200,13 @@
         expect.add(new TextFrame().setPayload("fragment1fragment2"));
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -262,18 +227,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.sendAndIgnoreBrokenPipe(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -294,18 +254,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -322,18 +277,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -365,8 +315,7 @@
         expect2.add(new TextFrame().setPayload("f1,f2,f3,f4,f5"));
         expect2.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -382,10 +331,6 @@
             fuzzer.send(send2);
             fuzzer.expect(expect2);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -402,18 +347,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -442,8 +382,7 @@
         expect2.add(new TextFrame().setPayload("f1,f2,f3,f4,f5"));
         expect2.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
@@ -456,10 +395,6 @@
             fuzzer.send(send2);
             fuzzer.expect(expect2);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -488,8 +423,7 @@
         expect2.add(new TextFrame().setPayload("f1,f2,f3,f4,f5"));
         expect2.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -503,10 +437,6 @@
             fuzzer.send(send2);
             fuzzer.expect(expect2);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -524,18 +454,13 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -553,18 +478,13 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -582,8 +502,7 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -591,10 +510,6 @@
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -614,18 +529,13 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -645,18 +555,13 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -676,8 +581,7 @@
         expect.add(new TextFrame().setPayload("hello, world"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -685,10 +589,6 @@
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -706,17 +606,12 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try(Fuzzer fuzzer = new Fuzzer(this);StacklessLogging supress = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java
index 6ead418..c11aebf 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java
@@ -90,18 +90,13 @@
         expect.add(new TextFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -120,18 +115,13 @@
         expect.add(new TextFrame());
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -150,18 +140,13 @@
         expect.add(new TextFrame().setPayload("middle"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -172,7 +157,7 @@
     {
         String utf1 = "Hello-\uC2B5@\uC39F\uC3A4";
         String utf2 = "\uC3BC\uC3A0\uC3A1-UTF-8!!";
-        
+
         ByteBuffer b1 = ByteBuffer.wrap(StringUtil.getUtf8Bytes(utf1));
         ByteBuffer b2 = ByteBuffer.wrap(StringUtil.getUtf8Bytes(utf2));
 
@@ -189,18 +174,13 @@
         expect.add(new TextFrame().setPayload(e1));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -220,18 +200,13 @@
         expect.add(new TextFrame().setPayload(ByteBuffer.wrap(msg)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -250,18 +225,13 @@
         expect.add(new TextFrame().setPayload(ByteBuffer.wrap(msg)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -279,18 +249,13 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -311,8 +276,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -325,10 +289,6 @@
 
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -351,8 +311,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -363,10 +322,6 @@
             fuzzer.send(new ContinuationFrame().setPayload(ByteBuffer.wrap(part3)).setFin(true));
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -393,15 +348,13 @@
             List<WebSocketFrame> expect = new ArrayList<>();
             expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-            Fuzzer fuzzer = new Fuzzer(this);
-            try
+            try (Fuzzer fuzzer = new Fuzzer(this))
             {
                 fuzzer.connect();
 
                 ByteBuffer net = fuzzer.asNetworkBuffer(send);
 
-                int splits[] =
-                { 17, 21, net.limit() };
+                int splits[] = { 17, 21, net.limit() };
 
                 ByteBuffer part1 = net.slice(); // Header + good UTF
                 part1.limit(splits[0]);
@@ -419,12 +372,6 @@
                 fuzzer.send(part3); // the rest (shouldn't work)
 
                 fuzzer.expect(expect);
-
-                fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.UNCLEAN);
-            }
-            finally
-            {
-                fuzzer.close();
             }
         }
     }
@@ -445,8 +392,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging scope = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging scope = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
 
@@ -460,9 +406,5 @@
 
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_BadUTF.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_BadUTF.java
index 7c61776..3ff1643 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_BadUTF.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_BadUTF.java
@@ -163,18 +163,15 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging supress = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this))
         {
-            fuzzer.connect();
-            fuzzer.setSendMode(Fuzzer.SendMode.BULK);
-            fuzzer.send(send);
-            fuzzer.expect(expect);
-            fuzzer.expectServerDisconnect(Fuzzer.DisconnectMode.UNCLEAN);
-        }
-        finally
-        {
-            fuzzer.close();
+            try (StacklessLogging supress = new StacklessLogging(Parser.class))
+            {
+                fuzzer.connect();
+                fuzzer.setSendMode(Fuzzer.SendMode.BULK);
+                fuzzer.send(send);
+                fuzzer.expect(expect);
+            }
         }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java
index 8eebe6e..f5e9cbe 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java
@@ -139,17 +139,12 @@
         expect.add(new TextFrame().setPayload(clone(msg)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java
index 8c60551..b8ad71b 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java
@@ -64,18 +64,13 @@
         expect.add(new TextFrame().setPayload("Hello World"));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -91,8 +86,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -100,10 +94,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -119,8 +109,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -128,10 +117,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -147,8 +132,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -156,10 +140,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -176,8 +156,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -185,10 +164,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -210,8 +185,7 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -219,10 +193,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -237,8 +207,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -246,10 +215,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -258,8 +223,7 @@
     @Test
     public void testCase7_3_2() throws Exception
     {
-        byte payload[] = new byte[]
-        { 0x00 };
+        byte payload[] = new byte[] { 0x00 };
         ByteBuffer buf = ByteBuffer.wrap(payload);
 
         List<WebSocketFrame> send = new ArrayList<>();
@@ -268,8 +232,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging scope = new StacklessLogging(Parser.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging scope = new StacklessLogging(Parser.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -277,10 +240,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -295,8 +254,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -304,10 +262,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -322,8 +276,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL,"Hic").asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -331,10 +284,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -353,8 +302,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.NORMAL,reason).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging logging = new StacklessLogging(AbstractWebSocketConnection.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging logging = new StacklessLogging(AbstractWebSocketConnection.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -362,10 +310,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -390,8 +334,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try(StacklessLogging scope = new StacklessLogging(Parser.class,CloseInfo.class))
+        try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging scope = new StacklessLogging(Parser.class,CloseInfo.class))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -399,9 +342,5 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java
index b3626f1..c76e174 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java
@@ -98,8 +98,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -107,10 +106,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -131,8 +126,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -140,9 +134,5 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java
index 425f657..5e8da15 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java
@@ -93,8 +93,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseFrame().setPayload(clone(payload)));
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -102,10 +101,6 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -125,8 +120,7 @@
         List<WebSocketFrame> expect = new ArrayList<>();
         expect.add(new CloseFrame().setPayload(clone(payload)));
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
@@ -134,9 +128,5 @@
             fuzzer.expect(expect);
             fuzzer.expectNoMoreFrames();
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 }
diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java
index 3950394..1b117f4 100644
--- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java
+++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase9.java
@@ -96,18 +96,13 @@
         expect.add(toDataFrame(opcode).setPayload(copyOf(msg)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect,TimeUnit.SECONDS,8);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     private void assertSlowFrameEcho(byte opcode, int overallMsgSize, int segmentSize) throws Exception
@@ -124,8 +119,7 @@
         expect.add(toDataFrame(opcode).setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
@@ -133,10 +127,6 @@
             fuzzer.send(send);
             fuzzer.expect(expect,TimeUnit.SECONDS,8);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -157,18 +147,13 @@
         expect.add(new TextFrame().setPayload(msg));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -189,18 +174,13 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -222,18 +202,13 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect,TimeUnit.SECONDS,4);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -255,18 +230,13 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect,TimeUnit.SECONDS,8);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -288,18 +258,13 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect,TimeUnit.SECONDS,16);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -321,18 +286,13 @@
         expect.add(new TextFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect,TimeUnit.SECONDS,32);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -352,18 +312,13 @@
         expect.add(new BinaryFrame().setPayload(copyOf(data)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -384,18 +339,13 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -417,18 +367,13 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect,TimeUnit.SECONDS,4);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -450,18 +395,13 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect,TimeUnit.SECONDS,8);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -483,18 +423,13 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect,TimeUnit.SECONDS,16);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
@@ -516,18 +451,13 @@
         expect.add(new BinaryFrame().setPayload(clone(buf)));
         expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
 
-        Fuzzer fuzzer = new Fuzzer(this);
-        try
+        try(Fuzzer fuzzer = new Fuzzer(this))
         {
             fuzzer.connect();
             fuzzer.setSendMode(Fuzzer.SendMode.BULK);
             fuzzer.send(send);
             fuzzer.expect(expect,TimeUnit.SECONDS,32);
         }
-        finally
-        {
-            fuzzer.close();
-        }
     }
 
     /**
diff --git a/jetty-websocket/websocket-servlet/pom.xml b/jetty-websocket/websocket-servlet/pom.xml
index b90d2f6..0904097 100644
--- a/jetty-websocket/websocket-servlet/pom.xml
+++ b/jetty-websocket/websocket-servlet/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <groupId>org.eclipse.jetty.websocket</groupId>
         <artifactId>websocket-parent</artifactId>
-        <version>9.1.5-SNAPSHOT</version>
+        <version>9.2.0-SNAPSHOT</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
diff --git a/jetty-xml/pom.xml b/jetty-xml/pom.xml
index 96e50cf..517cd7e 100644
--- a/jetty-xml/pom.xml
+++ b/jetty-xml/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>jetty-xml</artifactId>
diff --git a/pom.xml b/pom.xml
index e0b06dd..87c22e7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
     <version>22</version>
   </parent>
   <artifactId>jetty-project</artifactId>
-  <version>9.1.5-SNAPSHOT</version>
+  <version>9.2.0-SNAPSHOT</version>
   <name>Jetty :: Project</name>
   <url>http://www.eclipse.org/jetty</url>
   <packaging>pom</packaging>
@@ -17,6 +17,10 @@
     <slf4j-version>1.6.1</slf4j-version>
     <jetty-test-policy-version>1.2</jetty-test-policy-version>
     <npn.api.version>1.1.0.v20120525</npn.api.version>
+    <alpn.api.version>1.0.0</alpn.api.version>
+    <!-- default values are unsupported, but required to be defined for reactor sanity reasons -->
+    <npn.version>undefined</npn.version>
+    <alpn.version>undefined</alpn.version>
   </properties>
   <scm>
     <connection>scm:git:http://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git</connection>
@@ -126,10 +130,11 @@
               <rules>
                 <requireMavenVersion>
                   <version>[3.0.0,)</version>
+                  <message>[ERROR] OLD MAVEN [${maven.version}] in use, Jetty ${project.version} requires Maven 3.0.0 or newer</message>
                 </requireMavenVersion>
                 <requireJavaVersion>
-                  <version>[1.7,)</version>
-                  <message>[ERROR] OLD JDK [${java.version}] in use. Jetty ${project.version} requires JDK 1.7 or newer</message>
+                  <version>[1.7.0-40,)</version>
+                  <message>[ERROR] OLD JDK [${java.version}] in use. Jetty ${project.version} requires JDK 1.7.0_40 or newer</message>
                 </requireJavaVersion>
                 <versionTxtRule implementation="org.eclipse.jetty.toolchain.enforcer.rules.VersionTxtRule" />
                 <versionOsgiRule implementation="org.eclipse.jetty.toolchain.enforcer.rules.RequireOsgiCompatibleVersionRule" />
@@ -419,6 +424,8 @@
     <module>jetty-servlets</module>
     <module>jetty-util-ajax</module>
     <module>jetty-jsp</module>
+    <module>apache-jsp</module>
+    <module>apache-jstl</module>
     <module>jetty-maven-plugin</module>
     <module>jetty-jspc-maven-plugin</module>
     <module>jetty-deploy</module>
@@ -431,15 +438,17 @@
     <module>jetty-client</module>
     <module>jetty-proxy</module>
     <module>jetty-jaspi</module>
-    <module>jetty-osgi</module>
     <module>jetty-rewrite</module>
     <module>jetty-nosql</module>
     <module>tests</module>
     <module>examples</module>
+    <module>jetty-quickstart</module>
     <module>jetty-distribution</module>
     <module>jetty-runner</module>
     <module>jetty-monitor</module>
     <module>jetty-http-spi</module>
+    <module>jetty-osgi</module>
+    <module>jetty-alpn</module>
 
     <!-- modules that need fixed and added back, or simply dropped and not maintained
     <module>jetty-rhttp</module>
@@ -466,12 +475,12 @@
       <dependency>
         <groupId>org.ow2.asm</groupId>
         <artifactId>asm</artifactId>
-        <version>4.1</version>
+        <version>5.0.1</version>
       </dependency>
       <dependency>
         <groupId>org.ow2.asm</groupId>
         <artifactId>asm-commons</artifactId>
-        <version>4.1</version>
+        <version>5.0.1</version>
       </dependency>
 
       <dependency>
@@ -519,6 +528,12 @@
       </dependency>
 
       <dependency>
+        <groupId>org.mortbay.jasper</groupId>
+        <artifactId>apache-jsp</artifactId>
+        <version>8.0.3.v20140313</version>
+      </dependency>
+
+      <dependency>
         <groupId>org.eclipse.jetty.orbit</groupId>
         <artifactId>org.eclipse.jdt.core</artifactId>
        <version>3.8.2.v20130121</version>
@@ -805,102 +820,6 @@
       </build>
     </profile>
     <profile>
-      <id>7u9</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_9</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.3.v20130313</npn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u10</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_10</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.3.v20130313</npn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u11</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_11</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.3.v20130313</npn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u13</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_13</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.4.v20130313</npn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u15</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_15</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.5.v20130313</npn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u17</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_17</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.5.v20130313</npn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u21</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_21</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.5.v20130313</npn.version>
-      </properties>
-    </profile>
-    <profile>
-      <id>7u25</id>
-      <activation>
-        <property>
-          <name>java.version</name>
-          <value>1.7.0_25</value>
-        </property>
-      </activation>
-      <properties>
-        <npn.version>1.1.5.v20130313</npn.version>
-      </properties>
-    </profile>
-    <profile>
       <id>7u40</id>
       <activation>
         <property>
@@ -910,6 +829,7 @@
       </activation>
       <properties>
         <npn.version>1.1.6.v20130911</npn.version>
+        <alpn.version>7.0.0.v20140317</alpn.version>
       </properties>
     </profile>
     <profile>
@@ -922,6 +842,7 @@
       </activation>
       <properties>
         <npn.version>1.1.6.v20130911</npn.version>
+        <alpn.version>7.0.0.v20140317</alpn.version>
       </properties>
     </profile>
     <profile>
@@ -934,6 +855,7 @@
       </activation>
       <properties>
         <npn.version>1.1.6.v20130911</npn.version>
+        <alpn.version>7.0.0.v20140317</alpn.version>
       </properties>
     </profile>
     <profile>
@@ -946,6 +868,31 @@
       </activation>
       <properties>
         <npn.version>1.1.7.v20140316</npn.version>
+        <alpn.version>7.0.0.v20140317</alpn.version>
+      </properties>
+    </profile>
+    <profile>
+      <id>8u00</id>
+      <activation>
+        <property>
+          <name>java.version</name>
+          <value>1.8.0</value>
+        </property>
+      </activation>
+      <properties>
+        <alpn.version>8.0.0.v20140317</alpn.version>
+      </properties>
+    </profile>
+    <profile>
+      <id>8u05</id>
+      <activation>
+        <property>
+          <name>java.version</name>
+          <value>1.8.0_05</value>
+        </property>
+      </activation>
+      <properties>
+        <alpn.version>8.0.0.v20140317</alpn.version>
       </properties>
     </profile>
   </profiles>
diff --git a/tests/pom.xml b/tests/pom.xml
index ef36591..717de70 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>org.eclipse.jetty</groupId>
     <artifactId>jetty-project</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <groupId>org.eclipse.jetty.tests</groupId>
diff --git a/tests/test-continuation/pom.xml b/tests/test-continuation/pom.xml
index ec02de3..fd6cc1e 100644
--- a/tests/test-continuation/pom.xml
+++ b/tests/test-continuation/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
diff --git a/tests/test-integration/pom.xml b/tests/test-integration/pom.xml
index 3d0baac..928ec92 100644
--- a/tests/test-integration/pom.xml
+++ b/tests/test-integration/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>test-integration</artifactId>
diff --git a/tests/test-loginservice/pom.xml b/tests/test-loginservice/pom.xml
index fd99f8c..263585a 100644
--- a/tests/test-loginservice/pom.xml
+++ b/tests/test-loginservice/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <artifactId>test-loginservice</artifactId>
   <name>Jetty Tests :: Login Service</name>
diff --git a/tests/test-sessions/pom.xml b/tests/test-sessions/pom.xml
index 7843d64..ec7205b 100644
--- a/tests/test-sessions/pom.xml
+++ b/tests/test-sessions/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <artifactId>test-sessions-parent</artifactId>
   <name>Jetty Tests :: Sessions :: Parent</name>
diff --git a/tests/test-sessions/test-hash-sessions/pom.xml b/tests/test-sessions/test-hash-sessions/pom.xml
index feae3f9..45b53ee 100644
--- a/tests/test-sessions/test-hash-sessions/pom.xml
+++ b/tests/test-sessions/test-hash-sessions/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-sessions-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <artifactId>test-hash-sessions</artifactId>
   <name>Jetty Tests :: Sessions :: Hash</name>
diff --git a/tests/test-sessions/test-jdbc-sessions/pom.xml b/tests/test-sessions/test-jdbc-sessions/pom.xml
index 3b5e12e..8eebb4c 100644
--- a/tests/test-sessions/test-jdbc-sessions/pom.xml
+++ b/tests/test-sessions/test-jdbc-sessions/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-sessions-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <artifactId>test-jdbc-sessions</artifactId>
   <name>Jetty Tests :: Sessions :: JDBC</name>
diff --git a/tests/test-sessions/test-sessions-common/pom.xml b/tests/test-sessions/test-sessions-common/pom.xml
index 369041a..c3830b8 100644
--- a/tests/test-sessions/test-sessions-common/pom.xml
+++ b/tests/test-sessions/test-sessions-common/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-sessions-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <artifactId>test-sessions-common</artifactId>
   <name>Jetty Tests :: Sessions :: Common</name>
diff --git a/tests/test-webapps/pom.xml b/tests/test-webapps/pom.xml
index 5d872c3..702fb1b 100644
--- a/tests/test-webapps/pom.xml
+++ b/tests/test-webapps/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>tests-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <artifactId>test-webapps-parent</artifactId>
diff --git a/tests/test-webapps/test-jaas-webapp/pom.xml b/tests/test-webapps/test-jaas-webapp/pom.xml
index f44221b..e86e72e 100644
--- a/tests/test-webapps/test-jaas-webapp/pom.xml
+++ b/tests/test-webapps/test-jaas-webapp/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <artifactId>test-jaas-webapp</artifactId>
   <name>Jetty Tests :: WebApp :: JAAS</name>
diff --git a/tests/test-webapps/test-jetty-webapp/pom.xml b/tests/test-webapps/test-jetty-webapp/pom.xml
index 588f0c0..732c304 100644
--- a/tests/test-webapps/test-jetty-webapp/pom.xml
+++ b/tests/test-webapps/test-jetty-webapp/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
@@ -157,33 +157,6 @@
           </loginServices>
         </configuration>
       </plugin>
-      <!-- uncomment to precompile jsps -->
-      <!--
-      <plugin>
-          <groupId>org.eclipse.jetty</groupId>
-          <artifactId>jetty-jspc-maven-plugin</artifactId>
-          <version>${project.version}</version>
-          <executions>
-            <execution>
-              <id>jspc</id>
-              <goals>
-                <goal>jspc</goal>
-              </goals>
-              <configuration>
-                 <includes>**/*.foo</includes>
-                 <excludes>**/*.fff</excludes>
-              </configuration>
-            </execution>
-          </executions>
-        </plugin>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-war-plugin</artifactId>
-          <configuration>
-            <webXml>${basedir}/target/web.xml</webXml>
-          </configuration>
-        </plugin>
-        -->
     </plugins>
   </build>
   <dependencies>
@@ -261,4 +234,37 @@
       <scope>provided</scope>
     </dependency>
   </dependencies>
+  <profiles>
+    <profile>
+      <id>precompile-jsp</id>
+      <build>
+        <plugins>
+         <plugin>
+          <groupId>org.eclipse.jetty</groupId>
+          <artifactId>jetty-jspc-maven-plugin</artifactId>
+          <version>${project.version}</version>
+          <executions>
+            <execution>
+              <id>jspc</id>
+              <goals>
+                <goal>jspc</goal>
+              </goals>
+              <configuration>
+                 <includes>**/*.foo</includes>
+                 <excludes>**/*.fff</excludes>
+              </configuration>
+            </execution>
+          </executions>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-war-plugin</artifactId>
+          <configuration>
+            <webXml>${basedir}/target/web.xml</webXml>
+          </configuration>
+        </plugin>
+       </plugins>
+     </build>
+    </profile>
+  </profiles>
 </project>
diff --git a/tests/test-webapps/test-jndi-webapp/pom.xml b/tests/test-webapps/test-jndi-webapp/pom.xml
index 31074cf..ddfe948 100644
--- a/tests/test-webapps/test-jndi-webapp/pom.xml
+++ b/tests/test-webapps/test-jndi-webapp/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <artifactId>test-jndi-webapp</artifactId>
   <name>Jetty Tests :: WebApp :: JNDI</name>
diff --git a/tests/test-webapps/test-mock-resources/pom.xml b/tests/test-webapps/test-mock-resources/pom.xml
index 1b078df..c8e313d 100644
--- a/tests/test-webapps/test-mock-resources/pom.xml
+++ b/tests/test-webapps/test-mock-resources/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <name>Jetty Tests :: WebApp :: Mock Resources</name>
   <artifactId>test-mock-resources</artifactId>
diff --git a/tests/test-webapps/test-proxy-webapp/pom.xml b/tests/test-webapps/test-proxy-webapp/pom.xml
index 4a3ccac..61c13f9 100644
--- a/tests/test-webapps/test-proxy-webapp/pom.xml
+++ b/tests/test-webapps/test-proxy-webapp/pom.xml
@@ -20,7 +20,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
diff --git a/tests/test-webapps/test-servlet-spec/pom.xml b/tests/test-webapps/test-servlet-spec/pom.xml
index 9dccef1..07ec7ee 100644
--- a/tests/test-webapps/test-servlet-spec/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <artifactId>test-servlet-spec-parent</artifactId>
   <name>Jetty Tests :: Spec Test WebApp :: Parent</name>
diff --git a/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml b/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
index 358b9e4..9210bab 100644
--- a/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-container-initializer/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-servlet-spec-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <artifactId>test-container-initializer</artifactId>
   <packaging>jar</packaging>
diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml b/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
index e21f34c..440a58c 100644
--- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-servlet-spec-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <name>Jetty Tests :: Webapps :: Spec Webapp</name>
   <artifactId>test-spec-webapp</artifactId>
diff --git a/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml b/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
index eb874e8..da0f311 100644
--- a/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
+++ b/tests/test-webapps/test-servlet-spec/test-web-fragment/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-servlet-spec-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <name>Jetty Tests :: WebApp :: Servlet Spec :: Fragment Jar</name>
   <groupId>org.eclipse.jetty.tests</groupId>
diff --git a/tests/test-webapps/test-webapp-rfc2616/pom.xml b/tests/test-webapps/test-webapp-rfc2616/pom.xml
index 8930d64..eeba737 100644
--- a/tests/test-webapps/test-webapp-rfc2616/pom.xml
+++ b/tests/test-webapps/test-webapp-rfc2616/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>org.eclipse.jetty.tests</groupId>
     <artifactId>test-webapps-parent</artifactId>
-    <version>9.1.5-SNAPSHOT</version>
+    <version>9.2.0-SNAPSHOT</version>
   </parent>
   <artifactId>test-webapp-rfc2616</artifactId>
   <name>Jetty Tests :: WebApp :: RFC2616</name>