You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by km...@apache.org on 2016/02/25 21:54:22 UTC

[2/5] knox git commit: [KNOX-670] - Knox should be able to sost simple web apps

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayAppFuncTest.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayAppFuncTest.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayAppFuncTest.java
new file mode 100644
index 0000000..52f5eed
--- /dev/null
+++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayAppFuncTest.java
@@ -0,0 +1,685 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.gateway;
+
+import java.io.File;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.UUID;
+
+import com.jayway.restassured.RestAssured;
+import org.apache.commons.io.FileUtils;
+import org.apache.directory.server.protocol.shared.transport.TcpTransport;
+import org.apache.hadoop.gateway.security.ldap.SimpleLdapDirectoryServer;
+import org.apache.hadoop.gateway.services.DefaultGatewayServices;
+import org.apache.hadoop.gateway.services.GatewayServices;
+import org.apache.hadoop.gateway.services.ServiceLifecycleException;
+import org.apache.hadoop.gateway.services.topology.TopologyService;
+import org.apache.hadoop.test.TestUtils;
+import org.apache.hadoop.test.mock.MockServer;
+import org.apache.http.HttpStatus;
+import org.apache.log4j.Appender;
+import org.hamcrest.MatcherAssert;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.jayway.restassured.RestAssured.given;
+import static com.jayway.restassured.config.ConnectionConfig.connectionConfig;
+import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
+import static org.apache.hadoop.test.TestUtils.LOG_ENTER;
+import static org.apache.hadoop.test.TestUtils.LOG_EXIT;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.Matchers.arrayWithSize;
+import static org.hamcrest.Matchers.hasItemInArray;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
+import static org.xmlmatchers.transform.XmlConverters.the;
+import static org.xmlmatchers.xpath.HasXPath.hasXPath;
+import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
+
+public class GatewayAppFuncTest {
+
+  private static Logger LOG = LoggerFactory.getLogger( GatewayAppFuncTest.class );
+  private static Class DAT = GatewayAppFuncTest.class;
+
+  private static Enumeration<Appender> appenders;
+  private static GatewayTestConfig config;
+  private static DefaultGatewayServices services;
+  private static GatewayServer gateway;
+  private static int gatewayPort;
+  private static String gatewayUrl;
+  private static String clusterUrl;
+  private static SimpleLdapDirectoryServer ldap;
+  private static TcpTransport ldapTransport;
+  private static int ldapPort;
+  private static Properties params;
+  private static TopologyService topos;
+  private static MockServer mockWebHdfs;
+
+  @BeforeClass
+  public static void setupSuite() throws Exception {
+    LOG_ENTER();
+    RestAssured.config = newConfig().connectionConfig(connectionConfig().closeIdleConnectionsAfterEachResponse());
+    //appenders = NoOpAppender.setUp();
+    setupLdap();
+    setupGateway();
+    LOG_EXIT();
+  }
+
+  @AfterClass
+  public static void cleanupSuite() throws Exception {
+    LOG_ENTER();
+    gateway.stop();
+    ldap.stop( true );
+    FileUtils.deleteQuietly( new File( config.getGatewayHomeDir() ) );
+    //NoOpAppender.tearDown( appenders );
+    LOG_EXIT();
+  }
+
+  @After
+  public void cleanupTest() throws Exception {
+    FileUtils.cleanDirectory( new File( config.getGatewayTopologyDir() ) );
+    FileUtils.cleanDirectory( new File( config.getGatewayDeploymentDir() ) );
+  }
+
+  public static void setupLdap() throws Exception {
+    URL usersUrl = TestUtils.getResourceUrl( DAT, "users.ldif" );
+    ldapPort = TestUtils.findFreePort();
+    ldapTransport = new TcpTransport( ldapPort );
+    ldap = new SimpleLdapDirectoryServer( "dc=hadoop,dc=apache,dc=org", new File( usersUrl.toURI() ), ldapTransport );
+    ldap.start();
+    LOG.info( "LDAP port = " + ldapTransport.getPort() );
+  }
+
+  public static void setupGateway() throws Exception {
+
+    File targetDir = new File( System.getProperty( "user.dir" ), "target" );
+    File gatewayDir = new File( targetDir, "gateway-home-" + UUID.randomUUID() );
+    gatewayDir.mkdirs();
+
+    config = new GatewayTestConfig();
+    config.setGatewayHomeDir( gatewayDir.getAbsolutePath() );
+
+    URL svcsFileUrl = TestUtils.getResourceUrl( DAT, "test-svcs/readme.txt" );
+    File svcsFile = new File( svcsFileUrl.getFile() );
+    File svcsDir = svcsFile.getParentFile();
+    config.setGatewayServicesDir( svcsDir.getAbsolutePath() );
+
+    URL appsFileUrl = TestUtils.getResourceUrl( DAT, "test-apps/readme.txt" );
+    File appsFile = new File( appsFileUrl.getFile() );
+    File appsDir = appsFile.getParentFile();
+    config.setGatewayApplicationsDir( appsDir.getAbsolutePath() );
+
+    File topoDir = new File( config.getGatewayTopologyDir() );
+    topoDir.mkdirs();
+
+    File deployDir = new File( config.getGatewayDeploymentDir() );
+    deployDir.mkdirs();
+
+
+    setupMockServers();
+    startGatewayServer();
+  }
+
+  public static void setupMockServers() throws Exception {
+    mockWebHdfs = new MockServer( "WEBHDFS", true );
+  }
+
+  public static void startGatewayServer() throws Exception {
+    services = new DefaultGatewayServices();
+    Map<String,String> options = new HashMap<String,String>();
+    options.put( "persist-master", "false" );
+    options.put( "master", "password" );
+    try {
+      services.init( config, options );
+    } catch ( ServiceLifecycleException e ) {
+      e.printStackTrace(); // I18N not required.
+    }
+    topos = services.getService(GatewayServices.TOPOLOGY_SERVICE);
+
+    gateway = GatewayServer.startGateway( config, services );
+    MatcherAssert.assertThat( "Failed to start gateway.", gateway, notNullValue() );
+
+    gatewayPort = gateway.getAddresses()[0].getPort();
+    gatewayUrl = "http://localhost:" + gatewayPort + "/" + config.getGatewayPath();
+    clusterUrl = gatewayUrl + "/test-topology";
+
+    LOG.info( "Gateway port = " + gateway.getAddresses()[ 0 ].getPort() );
+
+    params = new Properties();
+    params.put( "LDAP_URL", "ldap://localhost:" + ldapTransport.getPort() );
+    params.put( "WEBHDFS_URL", "http://localhost:" + mockWebHdfs.getPort() );
+  }
+
+  @Test( timeout = TestUtils.MEDIUM_TIMEOUT )
+  public void testSimpleStaticHelloAppDeployUndeploy() throws Exception {
+    LOG_ENTER();
+
+    String topoStr = TestUtils.merge( DAT, "test-static-hello-topology.xml", params );
+    File topoFile = new File( config.getGatewayTopologyDir(), "test-topology.xml" );
+    FileUtils.writeStringToFile( topoFile, topoStr );
+
+    topos.reloadTopologies();
+
+    String username = "guest";
+    String password = "guest-password";
+    String serviceUrl =  clusterUrl + "/static-hello-app-path/index.html";
+    String body = given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .contentType( "text/html" )
+        .when().get( serviceUrl ).asString();
+    assertThat( the(body), hasXPath( "/html/head/title/text()", equalTo("Static Hello Application") ) );
+
+    serviceUrl =  clusterUrl + "/static-hello-app-path/";
+    body = given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .contentType( "text/html" )
+        .when().get( serviceUrl ).asString();
+    assertThat( the(body), hasXPath( "/html/head/title/text()", equalTo("Static Hello Application") ) );
+
+    serviceUrl =  clusterUrl + "/static-hello-app-path";
+    body = given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .contentType( "text/html" )
+        .when().get( serviceUrl ).asString();
+    assertThat( the(body), hasXPath( "/html/head/title/text()", equalTo("Static Hello Application") ) );
+
+    assertThat( "Failed to delete test topology file", FileUtils.deleteQuietly( topoFile ), is(true) );
+    topos.reloadTopologies();
+
+    given()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .statusCode( HttpStatus.SC_NOT_FOUND )
+        .when().get( serviceUrl );
+
+    LOG_EXIT();
+  }
+
+  @Test( timeout = TestUtils.MEDIUM_TIMEOUT )
+  public void testSimpleDynamicAppDeployUndeploy() throws Exception {
+    LOG_ENTER();
+
+    String topoStr = TestUtils.merge( DAT, "test-dynamic-app-topology.xml", params );
+    File topoFile = new File( config.getGatewayTopologyDir(), "test-topology.xml" );
+    FileUtils.writeStringToFile( topoFile, topoStr );
+
+    topos.reloadTopologies();
+
+    String username = "guest";
+    String password = "guest-password";
+
+    given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .body( is( clusterUrl + "/dynamic-app-path/?null" ) )
+        .when().get( clusterUrl + "/dynamic-app-path" );
+
+    assertThat( "Failed to delete test topology file", FileUtils.deleteQuietly( topoFile ), is(true) );
+    topos.reloadTopologies();
+
+    given()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .statusCode( HttpStatus.SC_NOT_FOUND )
+        .when()
+        .get( clusterUrl + "/dynamic-app-path" );
+
+    LOG_EXIT();
+  }
+
+  @Test( timeout = TestUtils.MEDIUM_TIMEOUT )
+  public void testNakedAppDeploy() throws Exception {
+    LOG_ENTER();
+
+    String topoStr = TestUtils.merge( DAT, "test-naked-app-topology.xml", params );
+    File topoFile = new File( config.getGatewayTopologyDir(), "test-topology.xml" );
+    FileUtils.writeStringToFile( topoFile, topoStr );
+
+    topos.reloadTopologies();
+
+    given()
+        //.log().all()
+        .expect()
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .body( is( gatewayUrl + "/test-topology/dynamic-app/?null" ) )
+        .when().get( gatewayUrl + "/test-topology/dynamic-app" );
+
+    LOG_EXIT();
+  }
+
+  @Test//( timeout = TestUtils.MEDIUM_TIMEOUT )
+  public void testDefaultAppName() throws Exception {
+    LOG_ENTER();
+
+    String topoStr = TestUtils.merge( DAT, "test-default-app-name-topology.xml", params );
+    File topoFile = new File( config.getGatewayTopologyDir(), "test-topology.xml" );
+    FileUtils.writeStringToFile( topoFile, topoStr );
+
+    topos.reloadTopologies();
+
+    String username = "guest";
+    String password = "guest-password";
+
+    given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .body( is( clusterUrl + "/dynamic-app/?null" ) )
+        .when().get( clusterUrl + "/dynamic-app" );
+
+    assertThat( "Failed to delete test topology file", FileUtils.deleteQuietly( topoFile ), is(true) );
+    topos.reloadTopologies();
+
+    given()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .statusCode( HttpStatus.SC_NOT_FOUND )
+        .when()
+        .get( clusterUrl + "/dynamic-app" );
+
+    File deployDir = new File( config.getGatewayDeploymentDir() );
+    assertThat( deployDir.listFiles(), is(arrayWithSize(0)) );
+
+    LOG_EXIT();
+  }
+
+  @Test//( timeout = TestUtils.MEDIUM_TIMEOUT )
+  public void testMultiApps() throws Exception {
+    LOG_ENTER();
+
+    String topoStr = TestUtils.merge( DAT, "test-multi-apps-topology.xml", params );
+    File topoFile = new File( config.getGatewayTopologyDir(), "test-topology.xml" );
+    FileUtils.writeStringToFile( topoFile, topoStr );
+
+    topos.reloadTopologies();
+
+    String username = "guest";
+    String password = "guest-password";
+
+    String body = given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .contentType( "text/html" )
+        .when().get( clusterUrl + "/static-hello-app-path/index.html" ).asString();
+    assertThat( the(body), hasXPath( "/html/head/title/text()", equalTo("Static Hello Application") ) );
+
+    body = given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .contentType( "" )
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .when().get( clusterUrl + "/static-json-app/one.json" ).asString();
+    assertThat( body, sameJSONAs( "{'test-name-one':'test-value-one'}" ) );
+
+    given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .body( is( clusterUrl + "/dynamic-app-path/?null" ) )
+        .when().get( clusterUrl + "/dynamic-app-path" );
+
+    body = given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .contentType( "application/xml" )
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .when().get( clusterUrl + "/test.xml" ).asString();
+    assertThat( the(body), hasXPath( "/test" ) );
+
+    assertThat( FileUtils.deleteQuietly( topoFile ), is(true) );
+    topos.reloadTopologies();
+
+    given()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .statusCode( HttpStatus.SC_NOT_FOUND )
+        .when().get( clusterUrl + "/static-hello-app-path/index.html" );
+    given()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .statusCode( HttpStatus.SC_NOT_FOUND )
+        .when().get( clusterUrl + "/static-json-app/one.json" );
+    given()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .statusCode( HttpStatus.SC_NOT_FOUND )
+        .when().get( clusterUrl + "/dynamic-app-path" );
+    given()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .statusCode( HttpStatus.SC_NOT_FOUND )
+        .when().get( clusterUrl + "/test.xml" );
+
+    LOG_EXIT();
+  }
+
+  @Test( timeout = TestUtils.MEDIUM_TIMEOUT )
+  public void testServicesAndApplications() throws Exception {
+    LOG_ENTER();
+
+    String topoStr = TestUtils.merge( DAT, "test-svcs-and-apps-topology.xml", params );
+    File topoFile = new File( config.getGatewayTopologyDir(), "test-topology.xml" );
+    FileUtils.writeStringToFile( topoFile, topoStr );
+
+    topos.reloadTopologies();
+
+    String username = "guest";
+    String password = "guest-password";
+
+    mockWebHdfs.expect()
+        .method( "GET" )
+        .pathInfo( "/v1/" )
+        .queryParam( "op", "GETHOMEDIRECTORY" )
+        .queryParam( "user.name", "guest" )
+        .respond()
+        .status( HttpStatus.SC_OK )
+        .content( "{\"path\":\"/users/guest\"}", Charset.forName("UTF-8") )
+        .contentType( "application/json" );
+    given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .queryParam( "op", "GETHOMEDIRECTORY" )
+        .expect()
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .contentType( "application/json" )
+        .body( "path", is( "/users/guest") )
+        .when().get( clusterUrl + "/webhdfs/v1" );
+    assertThat( mockWebHdfs.isEmpty(), is(true) );
+
+    String body = given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .contentType( "application/xml" )
+        .when().get( clusterUrl + "/static-xml-app/test.xml" ).asString();
+    assertThat( the(body), hasXPath( "test" ) );
+
+    body = given()
+        //.log().all()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .contentType( "" )
+        //.log().all()
+        .statusCode( HttpStatus.SC_OK )
+        .when().get( clusterUrl + "/app-two/one.json" ).asString();
+    assertThat( body, sameJSONAs( "{'test-name-one':'test-value-one'}" ) );
+
+    assertThat( FileUtils.deleteQuietly( topoFile ), is(true) );
+    topos.reloadTopologies();
+
+    given()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .statusCode( HttpStatus.SC_NOT_FOUND )
+        .when().get( clusterUrl + "/app-one/index.html" );
+    given()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .statusCode( HttpStatus.SC_NOT_FOUND )
+        .when().get( clusterUrl + "/app-two/one.json" );
+    given()
+        .auth().preemptive().basic( username, password )
+        .expect()
+        .statusCode( HttpStatus.SC_NOT_FOUND )
+        .when().get( clusterUrl + "/test.xml" );
+
+    File deployDir = new File( config.getGatewayDeploymentDir() );
+    assertThat( deployDir.listFiles(), is(arrayWithSize(0)) );
+
+    LOG_EXIT();
+  }
+
+  @Test//( timeout = TestUtils.MEDIUM_TIMEOUT )
+  public void testDeploymentCleanup() throws Exception {
+    LOG_ENTER();
+
+    String username = "guest";
+    String password = "guest-password";
+
+    int oldVersionLimit = config.getGatewayDeploymentsBackupVersionLimit();
+
+    try {
+      gateway.stop();
+      config.setGatewayDeploymentsBackupVersionLimit( 1 );
+      startGatewayServer();
+
+      String topoStr = TestUtils.merge( DAT, "test-dynamic-app-topology.xml", params );
+      File topoFile = new File( config.getGatewayTopologyDir(), "test-topology.xml" );
+      FileUtils.writeStringToFile( topoFile, topoStr );
+      topos.reloadTopologies();
+
+      File deployDir = new File( config.getGatewayDeploymentDir() );
+      String[] topoDirs1 = deployDir.list();
+      assertThat( topoDirs1, is(arrayWithSize(1)) );
+
+      given()
+          //.log().all()
+          .auth().preemptive().basic( username, password )
+          .expect()
+          //.log().all()
+          .statusCode( HttpStatus.SC_OK )
+          .body( is( clusterUrl + "/dynamic-app-path/?null" ) )
+          .when().get( clusterUrl + "/dynamic-app-path" );
+
+      TestUtils.waitUntilNextSecond();
+      FileUtils.touch( topoFile );
+
+      topos.reloadTopologies();
+      String[] topoDirs2 = deployDir.list();
+      assertThat( topoDirs2, is(arrayWithSize(2)) );
+      assertThat( topoDirs2, hasItemInArray(topoDirs1[0]) );
+
+      given()
+          //.log().all()
+          .auth().preemptive().basic( username, password )
+          .expect()
+          //.log().all()
+          .statusCode( HttpStatus.SC_OK )
+          .body( is( clusterUrl + "/dynamic-app-path/?null" ) )
+          .when().get( clusterUrl + "/dynamic-app-path" );
+
+      TestUtils.waitUntilNextSecond();
+      FileUtils.touch( topoFile );
+      topos.reloadTopologies();
+
+      String[] topoDirs3 = deployDir.list();
+      assertThat( topoDirs3, is(arrayWithSize(2)) );
+      assertThat( topoDirs3, not(hasItemInArray(topoDirs1[0])) );
+
+      given()
+          //.log().all()
+          .auth().preemptive().basic( username, password )
+          .expect()
+          //.log().all()
+          .statusCode( HttpStatus.SC_OK )
+          .body( is( clusterUrl + "/dynamic-app-path/?null" ) )
+          .when().get( clusterUrl + "/dynamic-app-path" );
+
+    } finally {
+      gateway.stop();
+      config.setGatewayDeploymentsBackupAgeLimit( oldVersionLimit );
+      startGatewayServer();
+    }
+
+    LOG_EXIT();
+  }
+
+  @Test( timeout = TestUtils.MEDIUM_TIMEOUT )
+  public void testDefaultTopology() throws Exception {
+    LOG_ENTER();
+
+    try {
+      gateway.stop();
+      config.setGatewayDeploymentsBackupVersionLimit( 1 );
+      startGatewayServer();
+
+      String topoStr = TestUtils.merge( DAT, "test-dynamic-app-topology.xml", params );
+      File topoFile = new File( config.getGatewayTopologyDir(), "test-topology.xml" );
+      FileUtils.writeStringToFile( topoFile, topoStr );
+
+      topos.reloadTopologies();
+
+      File deployDir = new File( config.getGatewayDeploymentDir() );
+      String[] topoDirs = deployDir.list();
+      assertThat( topoDirs, is(arrayWithSize(1)) );
+
+      String username = "guest";
+      String password = "guest-password";
+
+      given()
+          //.log().all()
+          .auth().preemptive().basic( username, password )
+          .expect()
+          //.log().all()
+          .statusCode( HttpStatus.SC_OK )
+          .body( is( clusterUrl + "/dynamic-app-path/?null" ) )
+          .when().get( clusterUrl + "/dynamic-app-path" );
+
+      given()
+          //.log().all()
+          .auth().preemptive().basic( username, password )
+          .expect()
+          //.log().all()
+          .body( is( clusterUrl + "/dynamic-app-path/?null" ) )
+          .when().get( clusterUrl + "/dynamic-app-path" );
+
+      topoStr = TestUtils.merge( DAT, "test-dynamic-app-topology.xml", params );
+      topoFile = new File( config.getGatewayTopologyDir(), "test-topology-2.xml" );
+      FileUtils.writeStringToFile( topoFile, topoStr );
+
+      topos.reloadTopologies();
+
+      given()
+          //.log().all()
+          .auth().preemptive().basic( username, password )
+          .expect()
+          //.log().all()
+          .statusCode( HttpStatus.SC_OK )
+          .body( is( gatewayUrl + "/test-topology" + "/dynamic-app-path/?null" ) )
+          .when().get( gatewayUrl + "/test-topology/dynamic-app-path" );
+
+      given()
+          //.log().all()
+          .auth().preemptive().basic( username, password )
+          .expect()
+          //.log().all()
+          .statusCode( HttpStatus.SC_OK )
+          .body( is( gatewayUrl + "/test-topology-2" + "/dynamic-app-path/?null" ) )
+          .when().get( gatewayUrl + "/test-topology-2/dynamic-app-path" );
+
+      given()
+          //.log().all()
+          .auth().preemptive().basic( username, password )
+          .expect()
+          //.log().all()
+          .statusCode( HttpStatus.SC_NOT_FOUND )
+          .body( is( clusterUrl + "/dynamic-app-path/?null" ) );
+
+      gateway.stop();
+      config.setDefaultTopologyName( "test-topology" );
+      startGatewayServer();
+
+      given()
+          //.log().all()
+          .auth().preemptive().basic( username, password )
+          .expect()
+          //.log().all()
+          .statusCode( HttpStatus.SC_OK )
+          .body( is( gatewayUrl + "/test-topology" + "/dynamic-app-path/?null" ) )
+          .when().get( gatewayUrl + "/test-topology/dynamic-app-path" );
+
+      given()
+          //.log().all()
+          .auth().preemptive().basic( username, password )
+          .expect()
+          //.log().all()
+          .statusCode( HttpStatus.SC_OK )
+          .body( is( gatewayUrl + "/test-topology-2" + "/dynamic-app-path/?null" ) )
+          .when().get( gatewayUrl + "/test-topology-2/dynamic-app-path" );
+
+      given()
+          //.log().all()
+          .auth().preemptive().basic( username, password )
+          .expect()
+          //.log().all()
+          .body( is( clusterUrl + "/dynamic-app-path/?null" ) )
+          .when().get( clusterUrl + "/dynamic-app-path" );
+
+    } finally {
+      gateway.stop();
+      config.setDefaultTopologyName( null );
+      startGatewayServer();
+    }
+
+    LOG_EXIT();
+  }
+
+  public static Collection<String> toNames( File[] files ) {
+    List<String> names = new ArrayList<String>( files.length );
+    for( File file : files ) {
+      names.add( file.getAbsolutePath() );
+    }
+    return names;
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayDeployFuncTest.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayDeployFuncTest.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayDeployFuncTest.java
index 990d776..534f3b0 100644
--- a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayDeployFuncTest.java
+++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayDeployFuncTest.java
@@ -218,7 +218,7 @@ public class GatewayDeployFuncTest {
   public void testDeployRedeployUndeploy() throws InterruptedException, IOException {
     LOG_ENTER();
     long sleep = 200;
-    int numFilesInWar = 5;
+    int numFilesInWebInf = 4; // # files in WEB-INF (ie gateway.xml, rewrite.xml, shiro.ini, web.xml)
     String username = "guest";
     String password = "guest-password";
     String serviceUrl =  clusterUrl + "/test-service-path/test-service-resource";
@@ -226,7 +226,7 @@ public class GatewayDeployFuncTest {
 
     File topoDir = new File( config.getGatewayTopologyDir() );
     File deployDir = new File( config.getGatewayDeploymentDir() );
-    File warDir;
+    File earDir;
 
     // Make sure deployment directory is empty.
     assertThat( topoDir.listFiles().length, is( 0 ) );
@@ -235,10 +235,10 @@ public class GatewayDeployFuncTest {
     File descriptor = writeTestTopology( "test-cluster", createTopology() );
     long writeTime = System.currentTimeMillis();
 
-    warDir = waitForFiles( deployDir, "test-cluster.war\\.[0-9A-Fa-f]+", 1, 0, sleep );
-    for( File webInfDir : warDir.listFiles() ) {
-      waitForFiles( webInfDir, ".*", numFilesInWar, 0, sleep );
-    }
+    earDir = waitForFiles( deployDir, "test-cluster\\.topo\\.[0-9A-Fa-f]+", 1, 0, sleep );
+    File warDir = new File( earDir, "%2F" );
+    File webInfDir = new File( warDir, "WEB-INF" );
+    waitForFiles( webInfDir, ".*", numFilesInWebInf, 0, sleep );
     waitForAccess( serviceUrl, username, password, sleep );
 
     // Wait to make sure a second has passed to ensure the the file timestamps are different.
@@ -252,10 +252,10 @@ public class GatewayDeployFuncTest {
     assertThat( topoTimestampAfter, greaterThan( topoTimestampBefore ) );
 
     // Check to make sure there are two war directories with the same root.
-    warDir = waitForFiles( deployDir, "test-cluster.war\\.[0-9A-Fa-f]+", 2, 1, sleep );
-    for( File webInfDir : warDir.listFiles() ) {
-      waitForFiles( webInfDir, ".*", numFilesInWar, 0, sleep );
-    }
+    earDir = waitForFiles( deployDir, "test-cluster\\.topo\\.[0-9A-Fa-f]+", 2, 1, sleep );
+    warDir = new File( earDir, "%2F" );
+    webInfDir = new File( warDir, "WEB-INF" );
+    waitForFiles( webInfDir, ".*", numFilesInWebInf, 0, sleep );
     waitForAccess( serviceUrl, username, password, sleep );
 
     // Wait to make sure a second has passed to ensure the the file timestamps are different.
@@ -269,10 +269,10 @@ public class GatewayDeployFuncTest {
     assertThat( topoTimestampAfter, greaterThan( topoTimestampBefore ) );
 
     // Check to make sure there are two war directories with the same root.
-    warDir = waitForFiles( deployDir, "test-cluster.war\\.[0-9A-Fa-f]+", 3, 2, sleep );
-    for( File webInfDir : warDir.listFiles() ) {
-      waitForFiles( webInfDir, ".*", numFilesInWar, 0, sleep );
-    }
+    earDir = waitForFiles( deployDir, "test-cluster\\.topo\\.[0-9A-Fa-f]+", 3, 2, sleep );
+    warDir = new File( earDir, "%2F" );
+    webInfDir = new File( warDir, "WEB-INF" );
+    waitForFiles( webInfDir, ".*", numFilesInWebInf, 0, sleep );
     waitForAccess( serviceUrl, username, password, sleep );
 
     // Delete the test topology.

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java
index 37d1f6f..29e8a15 100644
--- a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java
+++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java
@@ -38,11 +38,18 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig {
   private String kerberosLoginConfig = "/etc/knox/conf/krb5JAASLogin.conf";
   private String frontendUrl = null;
   private boolean xForwardedEnabled = true;
+  private String gatewayApplicationsDir = null;
+  private String gatewayServicesDir;
+  private String defaultTopologyName = "default";
 
   public void setGatewayHomeDir( String gatewayHomeDir ) {
     this.gatewayHomeDir = gatewayHomeDir;
   }
 
+  public String getGatewayHomeDir() {
+    return this.gatewayHomeDir;
+  }
+
   @Override
   public String getGatewayConfDir() {
     return gatewayHomeDir;
@@ -151,12 +158,13 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig {
     return kerberosLoginConfig;
   }
 
-  /* (non-Javadoc)
-   * @see org.apache.hadoop.gateway.config.GatewayConfig#getDefaultTopologyName()
-   */
   @Override
   public String getDefaultTopologyName() {
-    return "default";
+    return defaultTopologyName;
+  }
+
+  public void setDefaultTopologyName( String defaultTopologyName ) {
+    this.defaultTopologyName = defaultTopologyName;
   }
 
   /* (non-Javadoc)
@@ -237,10 +245,31 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig {
 //   this.kerberosLoginConfig = kerberosLoginConfig;
 //  }
 
-   @Override
-   public String getGatewayServicesDir() {
-      return gatewayHomeDir + "/data/services";
-   }
+  @Override
+  public String getGatewayServicesDir() {
+    if( gatewayServicesDir != null ) {
+      return gatewayServicesDir;
+    } else {
+      return getGatewayDataDir() + "/services";
+    }
+  }
+
+  public void setGatewayServicesDir( String gatewayServicesDir ) {
+    this.gatewayServicesDir = gatewayServicesDir;
+  }
+
+  @Override
+  public String getGatewayApplicationsDir() {
+    if( gatewayApplicationsDir != null ) {
+      return gatewayApplicationsDir;
+    } else {
+      return getGatewayConfDir() + "/applications";
+    }
+  }
+
+  public void setGatewayApplicationsDir( String gatewayApplicationsDir ) {
+    this.gatewayApplicationsDir = gatewayApplicationsDir;
+  }
 
   @Override
   public boolean isXForwardedEnabled() {
@@ -289,4 +318,25 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig {
     return 8*1024;
   }
 
+  private int backupVersionLimit = -1;
+
+  public void setGatewayDeploymentsBackupVersionLimit( int newBackupVersionLimit ) {
+    backupVersionLimit = newBackupVersionLimit;
+  }
+
+  public int getGatewayDeploymentsBackupVersionLimit() {
+    return backupVersionLimit;
+  }
+
+  private long backupAgeLimit = -1;
+
+  @Override
+  public long getGatewayDeploymentsBackupAgeLimit() {
+    return backupAgeLimit;
+  }
+
+  public void setGatewayDeploymentsBackupAgeLimit( long newBackupAgeLimit ) {
+    backupAgeLimit = newBackupAgeLimit;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/java/org/apache/hadoop/gateway/TestServlet.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/TestServlet.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/TestServlet.java
new file mode 100644
index 0000000..a0b0935
--- /dev/null
+++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/TestServlet.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.hadoop.gateway;
+
+import java.io.IOException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class TestServlet extends HttpServlet {
+
+  @Override
+  protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws IOException {
+    response.setContentType( "text/plain" );
+    response.getWriter().write( request.getRequestURL().toString() + "?" + request.getQueryString() );
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/java/org/apache/hadoop/gateway/deploy/DeploymentFactoryFuncTest.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/deploy/DeploymentFactoryFuncTest.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/deploy/DeploymentFactoryFuncTest.java
index fc28dba..76d923f 100644
--- a/gateway-test/src/test/java/org/apache/hadoop/gateway/deploy/DeploymentFactoryFuncTest.java
+++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/deploy/DeploymentFactoryFuncTest.java
@@ -17,50 +17,56 @@
  */
 package org.apache.hadoop.gateway.deploy;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
 import org.apache.commons.io.FileUtils;
 import org.apache.hadoop.gateway.GatewayTestConfig;
 import org.apache.hadoop.gateway.config.GatewayConfig;
+import org.apache.hadoop.gateway.filter.XForwardedHeaderFilter;
+import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteServletFilter;
 import org.apache.hadoop.gateway.services.DefaultGatewayServices;
 import org.apache.hadoop.gateway.services.ServiceLifecycleException;
+import org.apache.hadoop.gateway.topology.Application;
 import org.apache.hadoop.gateway.topology.Param;
 import org.apache.hadoop.gateway.topology.Provider;
 import org.apache.hadoop.gateway.topology.Service;
 import org.apache.hadoop.gateway.topology.Topology;
+import org.apache.hadoop.test.TestUtils;
 import org.apache.hadoop.test.log.NoOpAppender;
 import org.apache.log4j.Appender;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ArchivePath;
+import org.jboss.shrinkwrap.api.spec.EnterpriseArchive;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.junit.Test;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
-import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.net.URISyntaxException;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
 import static org.apache.hadoop.test.TestUtils.LOG_ENTER;
 import static org.apache.hadoop.test.TestUtils.LOG_EXIT;
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsEqual.equalTo;
@@ -71,10 +77,10 @@ import static org.junit.Assert.fail;
 public class DeploymentFactoryFuncTest {
 
   private static final long SHORT_TIMEOUT = 1000L;
-  private static final long MEDIUM_TIMEOUT = 10 * SHORT_TIMEOUT;
+  private static final long MEDIUM_TIMEOUT = 5 * SHORT_TIMEOUT;
   private static final long LONG_TIMEOUT = 10 * MEDIUM_TIMEOUT;
 
-  @Test( timeout = SHORT_TIMEOUT )
+  @Test( timeout = MEDIUM_TIMEOUT )
   public void testGenericProviderDeploymentContributor() throws ParserConfigurationException, SAXException, IOException, TransformerException {
     LOG_ENTER();
     GatewayConfig config = new GatewayTestConfig();
@@ -122,9 +128,9 @@ public class DeploymentFactoryFuncTest {
     provider.addParam( param );
     topology.addProvider( provider );
 
-    WebArchive war = DeploymentFactory.createDeployment( config, topology );
+    EnterpriseArchive war = DeploymentFactory.createDeployment( config, topology );
 
-    Document gateway = parse( war.get( "WEB-INF/gateway.xml" ).getAsset().openStream() );
+    Document gateway = TestUtils.parseXml( war.get( "%2F/WEB-INF/gateway.xml" ).getAsset().openStream() );
     //dump( gateway );
 
     //by default the first filter will be the X-Forwarded header filter
@@ -200,7 +206,7 @@ public class DeploymentFactoryFuncTest {
   }
 
   @Test( timeout = MEDIUM_TIMEOUT )
-  public void testSimpleTopology() throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+  public void testSimpleTopology() throws IOException, SAXException, ParserConfigurationException, URISyntaxException, TransformerException {
     LOG_ENTER();
     GatewayConfig config = new GatewayTestConfig();
     //Testing without x-forwarded headers filter
@@ -250,19 +256,23 @@ public class DeploymentFactoryFuncTest {
     authorizer.setEnabled( true );
     topology.addProvider( authorizer );
 
-    WebArchive war = DeploymentFactory.createDeployment( config, topology );
+    EnterpriseArchive war = DeploymentFactory.createDeployment( config, topology );
 //    File dir = new File( System.getProperty( "user.dir" ) );
 //    File file = war.as( ExplodedExporter.class ).exportExploded( dir, "test-cluster.war" );
 
-    Document web = parse( war.get( "WEB-INF/web.xml" ).getAsset().openStream() );
-    assertThat( web, hasXPath( "/web-app/servlet/servlet-name", equalTo( "test-cluster" ) ) );
+    Document web = TestUtils.parseXml( war.get( "%2F/WEB-INF/web.xml" ).getAsset().openStream() );
+    //TestUtils.dumpXml( web );
+    assertThat( web, hasXPath( "/web-app" ) );
+    assertThat( web, hasXPath( "/web-app/servlet" ) );
+    assertThat( web, hasXPath( "/web-app/servlet/servlet-name" ) );
+    assertThat( web, hasXPath( "/web-app/servlet/servlet-name", equalTo( "test-cluster-knox-gateway-servlet" ) ) );
     assertThat( web, hasXPath( "/web-app/servlet/servlet-class", equalTo( "org.apache.hadoop.gateway.GatewayServlet" ) ) );
     assertThat( web, hasXPath( "/web-app/servlet/init-param/param-name", equalTo( "gatewayDescriptorLocation" ) ) );
-    assertThat( web, hasXPath( "/web-app/servlet/init-param/param-value", equalTo( "gateway.xml" ) ) );
-    assertThat( web, hasXPath( "/web-app/servlet-mapping/servlet-name", equalTo( "test-cluster" ) ) );
+    assertThat( web, hasXPath( "/web-app/servlet/init-param/param-value", equalTo( "/WEB-INF/gateway.xml" ) ) );
+    assertThat( web, hasXPath( "/web-app/servlet-mapping/servlet-name", equalTo( "test-cluster-knox-gateway-servlet" ) ) );
     assertThat( web, hasXPath( "/web-app/servlet-mapping/url-pattern", equalTo( "/*" ) ) );
 
-    Document gateway = parse( war.get( "WEB-INF/gateway.xml" ).getAsset().openStream() );
+    Document gateway = TestUtils.parseXml( war.get( "%2F/WEB-INF/gateway.xml" ).getAsset().openStream() );
 
     assertThat( gateway, hasXPath( "/gateway/resource[1]/pattern", equalTo( "/webhdfs/v1/?**" ) ) );
     //assertThat( gateway, hasXPath( "/gateway/resource[1]/target", equalTo( "http://localhost:50070/webhdfs/v1/?{**}" ) ) );
@@ -373,7 +383,7 @@ public class DeploymentFactoryFuncTest {
       ha.setEnabled(true);
       topology.addProvider(ha);
 
-      for (int i = 0; i < 100; i++) {
+      for (int i = 0; i < 10; i++) {
          createAndTestDeployment(config, topology);
       }
       LOG_EXIT();
@@ -381,15 +391,15 @@ public class DeploymentFactoryFuncTest {
 
    private void createAndTestDeployment(GatewayConfig config, Topology topology) throws IOException, SAXException, ParserConfigurationException {
 
-      WebArchive war = DeploymentFactory.createDeployment(config, topology);
+      EnterpriseArchive war = DeploymentFactory.createDeployment(config, topology);
 //      File dir = new File( System.getProperty( "user.dir" ) );
 //      File file = war.as( ExplodedExporter.class ).exportExploded( dir, "test-cluster.war" );
 
-      Document web = parse(war.get("WEB-INF/web.xml").getAsset().openStream());
+      Document web = TestUtils.parseXml(war.get("%2F/WEB-INF/web.xml").getAsset().openStream());
       assertThat(web, hasXPath("/web-app/servlet/servlet-class", equalTo("org.apache.hadoop.gateway.GatewayServlet")));
       assertThat(web, hasXPath("/web-app/servlet/init-param/param-name", equalTo("gatewayDescriptorLocation")));
-      assertThat(web, hasXPath("/web-app/servlet/init-param/param-value", equalTo("gateway.xml")));
-      assertThat(web, hasXPath("/web-app/servlet-mapping/servlet-name", equalTo("test-cluster")));
+      assertThat(web, hasXPath("/web-app/servlet/init-param/param-value", equalTo("/WEB-INF/gateway.xml")));
+      assertThat(web, hasXPath("/web-app/servlet-mapping/servlet-name", equalTo("test-cluster-knox-gateway-servlet")));
       assertThat(web, hasXPath("/web-app/servlet-mapping/url-pattern", equalTo("/*")));
       //testing the order of listener classes generated
       assertThat(web, hasXPath("/web-app/listener[2]/listener-class", equalTo("org.apache.hadoop.gateway.services.GatewayServicesContextListener")));
@@ -397,7 +407,7 @@ public class DeploymentFactoryFuncTest {
       assertThat(web, hasXPath("/web-app/listener[4]/listener-class", equalTo("org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteServletContextListener")));
    }
 
-  @Test( timeout = MEDIUM_TIMEOUT )
+  @Test( timeout = LONG_TIMEOUT )
   public void testDeploymentWithServiceParams() throws Exception {
     LOG_ENTER();
     GatewayConfig config = new GatewayTestConfig();
@@ -452,8 +462,8 @@ public class DeploymentFactoryFuncTest {
     service.addParam( param );
     topology.addService( service );
 
-    WebArchive war = DeploymentFactory.createDeployment( config, topology );
-    Document doc = parse( war.get( "WEB-INF/gateway.xml" ).getAsset().openStream() );
+    EnterpriseArchive war = DeploymentFactory.createDeployment( config, topology );
+    Document doc = TestUtils.parseXml( war.get( "%2F/WEB-INF/gateway.xml" ).getAsset().openStream() );
 //    dump( doc );
 
     Node resourceNode, filterNode, paramNode;
@@ -488,11 +498,145 @@ public class DeploymentFactoryFuncTest {
     LOG_EXIT();
   }
 
-  private Document parse( InputStream stream ) throws IOException, SAXException, ParserConfigurationException {
-    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-    DocumentBuilder builder = factory.newDocumentBuilder();
-    InputSource source = new InputSource( stream );
-    return builder.parse( source );
+  @Test( timeout = MEDIUM_TIMEOUT )
+  public void testDeploymentWithApplication() throws Exception {
+    LOG_ENTER();
+    GatewayConfig config = new GatewayTestConfig();
+    File targetDir = new File(System.getProperty("user.dir"), "target");
+    File gatewayDir = new File(targetDir, "gateway-home-" + UUID.randomUUID());
+    gatewayDir.mkdirs();
+    ((GatewayTestConfig) config).setGatewayHomeDir(gatewayDir.getAbsolutePath());
+    File deployDir = new File(config.getGatewayDeploymentDir());
+    deployDir.mkdirs();
+    addStacksDir(config, targetDir);
+    URL serviceUrl = TestUtils.getResourceUrl( DeploymentFactoryFuncTest.class, "test-apps/minimal-test-app/service.xml" );
+    File serviceFile = new File( serviceUrl.toURI() );
+    File appsDir = serviceFile.getParentFile().getParentFile();
+    ((GatewayTestConfig)config).setGatewayApplicationsDir(appsDir.getAbsolutePath());
+
+    DefaultGatewayServices srvcs = new DefaultGatewayServices();
+    Map<String, String> options = new HashMap<String, String>();
+    options.put("persist-master", "false");
+    options.put("master", "password");
+    try {
+      DeploymentFactory.setGatewayServices(srvcs);
+      srvcs.init(config, options);
+    } catch (ServiceLifecycleException e) {
+      e.printStackTrace(); // I18N not required.
+    }
+
+    Topology topology = new Topology();
+    topology.setName( "test-topology" );
+
+    Application app;
+
+    app = new Application();
+    app.setName( "minimal-test-app" );
+    app.addUrl( "/minimal-test-app-path" );
+    topology.addApplication( app );
+
+    EnterpriseArchive archive = DeploymentFactory.createDeployment( config, topology );
+    assertThat( archive, notNullValue() );
+
+    Document doc;
+
+    doc = TestUtils.parseXml( archive.get( "META-INF/topology.xml" ).getAsset().openStream() );
+    assertThat( doc, notNullValue() );
+
+    doc = TestUtils.parseXml( archive.get( "%2Fminimal-test-app-path/WEB-INF/gateway.xml" ).getAsset().openStream() );
+    assertThat( doc, notNullValue() );
+    //dump( doc );
+    assertThat( doc, hasXPath("/gateway/resource/pattern", equalTo("/**?**")));
+    assertThat( doc, hasXPath("/gateway/resource/filter[1]/role", equalTo("xforwardedheaders")));
+    assertThat( doc, hasXPath("/gateway/resource/filter[1]/name", equalTo("XForwardedHeaderFilter")));
+    assertThat( doc, hasXPath("/gateway/resource/filter[1]/class", equalTo(XForwardedHeaderFilter.class.getName())));
+    assertThat( doc, hasXPath("/gateway/resource/filter[2]/role", equalTo("rewrite")));
+    assertThat( doc, hasXPath("/gateway/resource/filter[2]/name", equalTo("url-rewrite")));
+    assertThat( doc, hasXPath("/gateway/resource/filter[2]/class", equalTo(UrlRewriteServletFilter.class.getName())));
+
+    LOG_EXIT();
+  }
+
+  @Test( timeout = MEDIUM_TIMEOUT )
+  public void testDeploymentWithServicesAndApplications() throws Exception {
+    LOG_ENTER();
+    GatewayConfig config = new GatewayTestConfig();
+    File targetDir = new File(System.getProperty("user.dir"), "target");
+    File gatewayDir = new File(targetDir, "gateway-home-" + UUID.randomUUID());
+    gatewayDir.mkdirs();
+    ((GatewayTestConfig) config).setGatewayHomeDir(gatewayDir.getAbsolutePath());
+    File deployDir = new File(config.getGatewayDeploymentDir());
+    deployDir.mkdirs();
+    addStacksDir(config, targetDir);
+    URL serviceUrl = TestUtils.getResourceUrl( DeploymentFactoryFuncTest.class, "test-apps/minimal-test-app/service.xml" );
+    File serviceFile = new File( serviceUrl.toURI() );
+    File appsDir = serviceFile.getParentFile().getParentFile();
+    ((GatewayTestConfig)config).setGatewayApplicationsDir(appsDir.getAbsolutePath());
+
+    DefaultGatewayServices srvcs = new DefaultGatewayServices();
+    Map<String, String> options = new HashMap<String, String>();
+    options.put("persist-master", "false");
+    options.put("master", "password");
+    try {
+      DeploymentFactory.setGatewayServices(srvcs);
+      srvcs.init(config, options);
+    } catch (ServiceLifecycleException e) {
+      e.printStackTrace(); // I18N not required.
+    }
+
+    Topology topology = new Topology();
+    topology.setName( "test-topology" );
+
+    Application app;
+
+    topology.setName( "test-cluster" );
+    Service service = new Service();
+    service.setRole( "WEBHDFS" );
+    service.addUrl( "http://localhost:50070/test-service-url" );
+    topology.addService( service );
+
+    app = new Application();
+    app.setName( "minimal-test-app" );
+    app.addUrl( "/minimal-test-app-path-one" );
+    topology.addApplication( app );
+
+    app.setName( "minimal-test-app" );
+    app.addUrl( "/minimal-test-app-path-two" );
+    topology.addApplication( app );
+
+    EnterpriseArchive archive = DeploymentFactory.createDeployment( config, topology );
+    assertThat( archive, notNullValue() );
+
+    Document doc;
+    org.jboss.shrinkwrap.api.Node node;
+
+    node = archive.get( "META-INF/topology.xml" );
+    assertThat( "Find META-INF/topology.xml", node, notNullValue() );
+    doc = TestUtils.parseXml( node.getAsset().openStream() );
+    assertThat( "Parse META-INF/topology.xml", doc, notNullValue() );
+
+    node = archive.get( "%2F" );
+    assertThat( "Find %2F", node, notNullValue() );
+    node = archive.get( "%2F/WEB-INF/gateway.xml" );
+    assertThat( "Find %2F/WEB-INF/gateway.xml", node, notNullValue() );
+    doc = TestUtils.parseXml( node.getAsset().openStream() );
+    assertThat( "Parse %2F/WEB-INF/gateway.xml", doc, notNullValue() );
+
+    WebArchive war = archive.getAsType( WebArchive.class, "%2Fminimal-test-app-path-one" );
+    assertThat( "Find %2Fminimal-test-app-path-one", war, notNullValue() );
+    node = war.get( "/WEB-INF/gateway.xml" );
+    assertThat( "Find %2Fminimal-test-app-path-one/WEB-INF/gateway.xml", node, notNullValue() );
+    doc = TestUtils.parseXml( node.getAsset().openStream() );
+    assertThat( "Parse %2Fminimal-test-app-path-one/WEB-INF/gateway.xml", doc, notNullValue() );
+
+    war = archive.getAsType( WebArchive.class, "%2Fminimal-test-app-path-two" );
+    assertThat( "Find %2Fminimal-test-app-path-two", war, notNullValue() );
+    node = war.get( "/WEB-INF/gateway.xml" );
+    assertThat( "Find %2Fminimal-test-app-path-two/WEB-INF/gateway.xml", node, notNullValue() );
+    doc = TestUtils.parseXml( node.getAsset().openStream() );
+    assertThat( "Parse %2Fminimal-test-app-path-two/WEB-INF/gateway.xml", doc, notNullValue() );
+
+    LOG_EXIT();
   }
 
   private void addStacksDir(GatewayConfig config, File targetDir) {
@@ -532,4 +676,21 @@ public class DeploymentFactoryFuncTest {
     return XPathFactory.newInstance().newXPath().compile( expression ).evaluate( scope );
   }
 
+  private static void dump( org.jboss.shrinkwrap.api.Node node, String prefix ) {
+    System.out.println( prefix + ": " + node.getPath() );
+    Set<org.jboss.shrinkwrap.api.Node> children = node.getChildren();
+    if( children != null && !children.isEmpty() ) {
+      for( org.jboss.shrinkwrap.api.Node child : children ) {
+        dump( child, prefix + "    " );
+      }
+    }
+  }
+
+  private static void dump( Archive archive ) {
+    Map<ArchivePath,org.jboss.shrinkwrap.api.Node> content = archive.getContent();
+    for( Map.Entry<ArchivePath,org.jboss.shrinkwrap.api.Node> entry : content.entrySet() ) {
+      dump( entry.getValue(), "    " );
+    }
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/dynamic-app/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/dynamic-app/WEB-INF/web.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/dynamic-app/WEB-INF/web.xml
new file mode 100644
index 0000000..730d1e6
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/dynamic-app/WEB-INF/web.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<web-app
+        xmlns="http://java.sun.com/xml/ns/javaee"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+        version="3.0">
+    <servlet>
+        <servlet-name>dynamic-app-servlet</servlet-name>
+        <servlet-class>org.apache.hadoop.gateway.TestServlet</servlet-class>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>dynamic-app-servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/readme.txt
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/readme.txt b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/readme.txt
new file mode 100644
index 0000000..cd2eef8
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/readme.txt
@@ -0,0 +1,18 @@
+##########################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##########################################################################
+This file is here to help the tests find the parent directory.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-hello-app/index.html
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-hello-app/index.html b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-hello-app/index.html
new file mode 100644
index 0000000..27be025
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-hello-app/index.html
@@ -0,0 +1,24 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<html>
+    <head>
+        <title>Static Hello Application</title>
+    </head>
+    <body>
+        <p>Static Hello Application</p>
+    </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/one.json
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/one.json b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/one.json
new file mode 100644
index 0000000..185a2a8
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/one.json
@@ -0,0 +1,3 @@
+{
+  "test-name-one":"test-value-one"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/rewrite.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/rewrite.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/rewrite.xml
new file mode 100644
index 0000000..656e229
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/rewrite.xml
@@ -0,0 +1,17 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<rules/>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/service.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/service.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/service.xml
new file mode 100644
index 0000000..4fda1cc
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-json-app/service.xml
@@ -0,0 +1,21 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<service role="STATIC-JSON-APP" name="static-json-app" version="1.0.0">
+    <routes>
+        <route path="/**?**"/>
+    </routes>
+</service>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-xml-app/test.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-xml-app/test.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-xml-app/test.xml
new file mode 100644
index 0000000..f637baf
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-apps/static-xml-app/test.xml
@@ -0,0 +1,17 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<test/>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-default-app-name-topology.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-default-app-name-topology.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-default-app-name-topology.xml
new file mode 100644
index 0000000..019c88d
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-default-app-name-topology.xml
@@ -0,0 +1,53 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<topology>
+    <gateway>
+        <provider>
+            <role>authentication</role>
+            <name>ShiroProvider</name>
+            <enabled>true</enabled>
+            <param>
+                <name>main.ldapRealm</name>
+                <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.userDnTemplate</name>
+                <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.contextFactory.url</name>
+                <value>$LDAP_URL</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.contextFactory.authenticationMechanism</name>
+                <value>simple</value>
+            </param>
+            <param>
+                <name>urls./**</name>
+                <value>authcBasic</value>
+            </param>
+        </provider>
+        <provider>
+            <role>identity-assertion</role>
+            <name>Default</name>
+            <enabled>true</enabled>
+        </provider>
+    </gateway>
+    <application>
+        <name>dynamic-app</name>
+    </application>
+</topology>

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-dynamic-app-topology.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-dynamic-app-topology.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-dynamic-app-topology.xml
new file mode 100644
index 0000000..f25d343
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-dynamic-app-topology.xml
@@ -0,0 +1,54 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<topology>
+    <gateway>
+        <provider>
+            <role>authentication</role>
+            <name>ShiroProvider</name>
+            <enabled>true</enabled>
+            <param>
+                <name>main.ldapRealm</name>
+                <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.userDnTemplate</name>
+                <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.contextFactory.url</name>
+                <value>$LDAP_URL</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.contextFactory.authenticationMechanism</name>
+                <value>simple</value>
+            </param>
+            <param>
+                <name>urls./**</name>
+                <value>authcBasic</value>
+            </param>
+        </provider>
+        <provider>
+            <role>identity-assertion</role>
+            <name>Default</name>
+            <enabled>true</enabled>
+        </provider>
+    </gateway>
+    <application>
+        <name>dynamic-app</name>
+        <url>dynamic-app-path</url>
+    </application>
+</topology>

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-multi-apps-topology.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-multi-apps-topology.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-multi-apps-topology.xml
new file mode 100644
index 0000000..dab8738
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-multi-apps-topology.xml
@@ -0,0 +1,65 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<topology>
+    <gateway>
+        <provider>
+            <role>authentication</role>
+            <name>ShiroProvider</name>
+            <enabled>true</enabled>
+            <param>
+                <name>main.ldapRealm</name>
+                <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.userDnTemplate</name>
+                <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.contextFactory.url</name>
+                <value>$LDAP_URL</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.contextFactory.authenticationMechanism</name>
+                <value>simple</value>
+            </param>
+            <param>
+                <name>urls./**</name>
+                <value>authcBasic</value>
+            </param>
+        </provider>
+        <provider>
+            <role>identity-assertion</role>
+            <name>Default</name>
+            <enabled>true</enabled>
+        </provider>
+    </gateway>
+    <application>
+        <name>static-hello-app</name>
+        <url>static-hello-app-path</url>
+    </application>
+    <application>
+        <name>dynamic-app</name>
+        <url>/dynamic-app-path</url>
+    </application>
+    <application>
+        <name>static-json-app</name>
+    </application>
+    <application>
+        <name>static-xml-app</name>
+        <url>/</url>
+    </application>
+</topology>

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-naked-app-topology.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-naked-app-topology.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-naked-app-topology.xml
new file mode 100644
index 0000000..9e98d5b
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-naked-app-topology.xml
@@ -0,0 +1,33 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<topology>
+    <gateway>
+        <provider>
+            <role>authentication</role>
+            <name>Anonymous</name>
+            <enabled>true</enabled>
+        </provider>
+        <provider>
+            <role>identity-assertion</role>
+            <name>Default</name>
+            <enabled>true</enabled>
+        </provider>
+    </gateway>
+    <application>
+        <name>dynamic-app</name>
+    </application>
+</topology>

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-static-hello-topology.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-static-hello-topology.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-static-hello-topology.xml
new file mode 100644
index 0000000..b212825
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-static-hello-topology.xml
@@ -0,0 +1,54 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<topology>
+    <gateway>
+        <provider>
+            <role>authentication</role>
+            <name>ShiroProvider</name>
+            <enabled>true</enabled>
+            <param>
+                <name>main.ldapRealm</name>
+                <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.userDnTemplate</name>
+                <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.contextFactory.url</name>
+                <value>$LDAP_URL</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.contextFactory.authenticationMechanism</name>
+                <value>simple</value>
+            </param>
+            <param>
+                <name>urls./**</name>
+                <value>authcBasic</value>
+            </param>
+        </provider>
+        <provider>
+            <role>identity-assertion</role>
+            <name>Default</name>
+            <enabled>true</enabled>
+        </provider>
+    </gateway>
+    <application>
+        <name>static-hello-app</name>
+        <url>static-hello-app-path</url>
+    </application>
+</topology>

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs-and-apps-topology.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs-and-apps-topology.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs-and-apps-topology.xml
new file mode 100644
index 0000000..b18595b
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs-and-apps-topology.xml
@@ -0,0 +1,62 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<topology>
+    <gateway>
+        <provider>
+            <role>authentication</role>
+            <name>ShiroProvider</name>
+            <enabled>true</enabled>
+            <param>
+                <name>main.ldapRealm</name>
+                <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.userDnTemplate</name>
+                <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.contextFactory.url</name>
+                <value>$LDAP_URL</value>
+            </param>
+            <param>
+                <name>main.ldapRealm.contextFactory.authenticationMechanism</name>
+                <value>simple</value>
+            </param>
+            <param>
+                <name>urls./**</name>
+                <value>authcBasic</value>
+            </param>
+        </provider>
+        <provider>
+            <role>identity-assertion</role>
+            <name>Default</name>
+            <enabled>true</enabled>
+        </provider>
+    </gateway>
+    <service>
+        <role>WEBHDFS</role>
+        <url>$WEBHDFS_URL</url>
+    </service>
+    <application>
+        <name>static-xml-app</name>
+        <url>static-xml-app</url>
+    </application>
+    <application>
+        <name>static-json-app</name>
+        <url>/app-two</url>
+    </application>
+</topology>

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/readme.txt
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/readme.txt b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/readme.txt
new file mode 100644
index 0000000..cd2eef8
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/readme.txt
@@ -0,0 +1,18 @@
+##########################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##########################################################################
+This file is here to help the tests find the parent directory.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/webhdfs/2.4.0/rewrite.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/webhdfs/2.4.0/rewrite.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/webhdfs/2.4.0/rewrite.xml
new file mode 100644
index 0000000..efbd93d
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/webhdfs/2.4.0/rewrite.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<rules>
+
+    <rule dir="OUT" name="WEBHDFS/webhdfs/outbound" pattern="hdfs://*:*/{path=**}?{**}">
+        <rewrite template="{$frontend[url]}/webhdfs/v1/{path=**}?{**}"/>
+    </rule>
+
+    <rule dir="OUT" name="WEBHDFS/webhdfs/outbound" pattern="webhdfs://*:*/{path=**}?{**}">
+        <rewrite template="{$frontend[url]}/webhdfs/v1/{path=**}?{**}"/>
+    </rule>
+
+    <rule dir="OUT" name="WEBHDFS/webhdfs/outbound/namenode/headers/location">
+        <match pattern="{scheme}://{host}:{port}/{path=**}?{**}"/>
+        <rewrite template="{$frontend[url]}/webhdfs/data/v1/{path=**}?{scheme}?host={$hostmap(host)}?{port}?{**}"/>
+        <encrypt-query/>
+    </rule>
+
+    <rule dir="IN" name="WEBHDFS/webhdfs/inbound/hdfs" pattern="hdfs:/{path=**}?{**}">
+        <rewrite template="{$serviceMappedUrl[NAMENODE]}/{path=**}?{**}"/>
+    </rule>
+
+    <rule dir="IN" name="WEBHDFS/webhdfs/inbound/webhdfs" pattern="webhdfs:/{path=**}?{**}">
+        <rewrite template="{$serviceUrl[WEBHDFS]}/{path=**}?{**}"/>
+    </rule>
+
+    <rule dir="IN" name="WEBHDFS/webhdfs/inbound/namenode/root" pattern="*://*:*/**/webhdfs/{version}/?{**}">
+        <rewrite template="{$serviceUrl[WEBHDFS]}/{version}/?{**}"/>
+    </rule>
+
+    <rule dir="IN" name="WEBHDFS/webhdfs/inbound/namenode/file" pattern="*://*:*/**/webhdfs/{version}/{path=**}?{**}">
+        <rewrite template="{$serviceUrl[WEBHDFS]}/{version}/{path=**}?{**}"/>
+    </rule>
+
+    <rule dir="IN" name="WEBHDFS/webhdfs/inbound/namenode/home" pattern="*://*:*/**/webhdfs/{version}/~?{**}">
+        <rewrite template="{$serviceUrl[WEBHDFS]}/{version}/user/{$username}?{**}"/>
+    </rule>
+
+    <rule dir="IN" name="WEBHDFS/webhdfs/inbound/namenode/home/file" pattern="*://*:*/**/webhdfs/{version}/~/{path=**}?{**}">
+        <rewrite template="{$serviceUrl[WEBHDFS]}/{version}/user/{$username}/{path=**}?{**}"/>
+    </rule>
+
+    <rule dir="IN" name="WEBHDFS/webhdfs/inbound/datanode">
+        <decrypt-query/>
+        <match pattern="*://*:*/**/webhdfs/data/*/{path=**}?{scheme}?{host}?{port}?{**}"/>
+        <rewrite template="{scheme}://{host}:{port}/{path=**}?{**}"/>
+    </rule>
+
+    <filter name="WEBHDFS/webhdfs/outbound/namenode/headers">
+        <content type="application/x-http-headers">
+            <apply path="Location" rule="WEBHDFS/webhdfs/outbound/namenode/headers/location"/>
+        </content>
+    </filter>
+
+</rules>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/webhdfs/2.4.0/service.xml
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/webhdfs/2.4.0/service.xml b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/webhdfs/2.4.0/service.xml
new file mode 100644
index 0000000..9d39a32
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/test-svcs/webhdfs/2.4.0/service.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<service role="WEBHDFS" name="webhdfs" version="2.4.0">
+    <routes>
+        <route path="/webhdfs/v1/?**">
+            <rewrite apply="WEBHDFS/webhdfs/inbound/namenode/root" to="request.url"/>
+        </route>
+        <route path="/webhdfs/v1/**?**">
+            <rewrite apply="WEBHDFS/webhdfs/inbound/namenode/file" to="request.url"/>
+            <rewrite apply="WEBHDFS/webhdfs/outbound/namenode/headers" to="response.headers"/>
+        </route>
+        <route path="/webhdfs/v1/~?**">
+            <rewrite apply="WEBHDFS/webhdfs/inbound/namenode/home" to="request.url"/>
+        </route>
+        <route path="/webhdfs/v1/~/**?**">
+            <rewrite apply="WEBHDFS/webhdfs/inbound/namenode/home/file" to="request.url"/>
+            <rewrite apply="WEBHDFS/webhdfs/outbound/namenode/headers" to="response.headers"/>
+        </route>
+        <route path="/webhdfs/data/v1/**?**">
+            <rewrite apply="WEBHDFS/webhdfs/inbound/datanode" to="request.url"/>
+            <dispatch contributor-name="http-client" />
+        </route>
+    </routes>
+    <dispatch classname="org.apache.hadoop.gateway.hdfs.dispatch.HdfsHttpClientDispatch" ha-classname="org.apache.hadoop.gateway.hdfs.dispatch.WebHdfsHaDispatch"/>
+    <testURLs>
+        <testURL>/webhdfs/v1/?op=LISTSTATUS</testURL>
+    </testURLs>
+</service>

http://git-wip-us.apache.org/repos/asf/knox/blob/a70a3b56/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/users.ldif
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/users.ldif b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/users.ldif
new file mode 100644
index 0000000..b982cb3
--- /dev/null
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayAppFuncTest/users.ldif
@@ -0,0 +1,42 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+version: 1
+
+dn: dc=hadoop,dc=apache,dc=org
+objectclass: organization
+objectclass: dcObject
+o: Hadoop
+dc: hadoop
+
+# entry for a sample people container
+# please replace with site specific values
+dn: ou=people,dc=hadoop,dc=apache,dc=org
+objectclass:top
+objectclass:organizationalUnit
+ou: people
+
+# entry for a sample end user
+# please replace with site specific values
+dn: uid=guest,ou=people,dc=hadoop,dc=apache,dc=org
+objectclass:top
+objectclass:person
+objectclass:organizationalPerson
+objectclass:inetOrgPerson
+cn: Guest
+sn: User
+uid: guest
+userPassword:guest-password
\ No newline at end of file