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 2013/09/20 05:58:51 UTC

[2/3] KNOX-127: Use hostmap in some service registry rewrite functions.

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceMappedHostFunctionProcessorTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceMappedHostFunctionProcessorTest.java b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceMappedHostFunctionProcessorTest.java
new file mode 100644
index 0000000..e38442a
--- /dev/null
+++ b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceMappedHostFunctionProcessorTest.java
@@ -0,0 +1,148 @@
+/**
+ * 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.svcregfunc.impl;
+
+import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteEnvironment;
+import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteContext;
+import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
+import org.apache.hadoop.gateway.services.GatewayServices;
+import org.apache.hadoop.gateway.services.hostmap.HostMapper;
+import org.apache.hadoop.gateway.services.hostmap.HostMapperService;
+import org.apache.hadoop.gateway.services.registry.ServiceRegistry;
+import org.apache.hadoop.gateway.svcregfunc.api.ServiceMappedHostFunctionDescriptor;
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
+import static org.junit.Assert.fail;
+
+public class ServiceMappedHostFunctionProcessorTest {
+
+  HostMapperService hms;
+  HostMapper hm;
+  ServiceRegistry reg;
+  GatewayServices svc;
+  UrlRewriteEnvironment env;
+  UrlRewriteContext ctx;
+  ServiceMappedHostFunctionDescriptor desc;
+
+  @Before
+  public void setUp() {
+    hm = EasyMock.createNiceMock( HostMapper.class );
+    EasyMock.expect( hm.resolveInboundHostName( "test-host" ) ).andReturn( "test-internal-host" ).anyTimes();
+
+    hms = EasyMock.createNiceMock( HostMapperService.class );
+    EasyMock.expect( hms.getHostMapper( "test-cluster" ) ).andReturn( hm ).anyTimes();
+
+    reg = EasyMock.createNiceMock( ServiceRegistry.class );
+    EasyMock.expect( reg.lookupServiceURL( "test-cluster", "test-service" ) ).andReturn( "test-scheme://test-host:777/test-path" ).anyTimes();
+
+    svc = EasyMock.createNiceMock( GatewayServices.class );
+    EasyMock.expect( svc.getService( GatewayServices.SERVICE_REGISTRY_SERVICE ) ).andReturn( reg ).anyTimes();
+    EasyMock.expect( svc.getService( GatewayServices.HOST_MAPPING_SERVICE ) ).andReturn( hms ).anyTimes();
+
+    env = EasyMock.createNiceMock( UrlRewriteEnvironment.class );
+    EasyMock.expect( env.getAttribute( GatewayServices.GATEWAY_SERVICES_ATTRIBUTE ) ).andReturn( svc ).anyTimes();
+    EasyMock.expect( env.getAttribute( GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE ) ).andReturn( "test-cluster" ).anyTimes();
+
+    ctx = EasyMock.createNiceMock( UrlRewriteContext.class );
+    EasyMock.expect( ctx.getDirection() ).andReturn( UrlRewriter.Direction.IN ).anyTimes();
+
+    desc = EasyMock.createNiceMock( ServiceMappedHostFunctionDescriptor.class );
+
+    EasyMock.replay( hm, hms, reg, svc, env, desc, ctx );
+  }
+
+  @Test
+  public void testServiceLoader() throws Exception {
+    ServiceLoader loader = ServiceLoader.load( UrlRewriteFunctionProcessor.class );
+    Iterator iterator = loader.iterator();
+    assertThat( "Service iterator empty.", iterator.hasNext() );
+    while( iterator.hasNext() ) {
+      Object object = iterator.next();
+      if( object instanceof ServiceMappedHostFunctionProcessor ) {
+        return;
+      }
+    }
+    fail( "Failed to find " + ServiceMappedHostFunctionProcessor.class.getName() + " via service loader." );
+  }
+
+  @Test
+  public void testName() throws Exception {
+    ServiceMappedHostFunctionProcessor func = new ServiceMappedHostFunctionProcessor();
+    assertThat( func.name(), is( "serviceMappedHost" ) );
+  }
+
+  @Test
+  public void testInitialize() throws Exception {
+    ServiceMappedHostFunctionProcessor func = new ServiceMappedHostFunctionProcessor();
+    try {
+      func.initialize( null, desc );
+      fail( "Should have thrown an IllegalArgumentException" );
+    } catch( IllegalArgumentException e ) {
+      assertThat( e.getMessage(), containsString( "environment" ) );
+    }
+
+    func = new ServiceMappedHostFunctionProcessor();
+    try {
+      func.initialize( env, null );
+    } catch( Exception e ) {
+      e.printStackTrace();
+      fail( "Should not have thrown an exception" );
+    }
+
+    func.initialize( env, desc );
+
+    assertThat( func.cluster(), is( "test-cluster" ) );
+    assertThat( func.registry(), sameInstance( reg ) );
+  }
+
+  @Test
+  public void testDestroy() throws Exception {
+    ServiceMappedHostFunctionProcessor func = new ServiceMappedHostFunctionProcessor();
+    func.initialize( env, desc );
+    func.destroy();
+
+    assertThat( func.cluster(), nullValue() );
+    assertThat( func.registry(), nullValue() );
+  }
+
+  @Test
+  public void testResolve() throws Exception {
+    ServiceMappedHostFunctionProcessor func = new ServiceMappedHostFunctionProcessor();
+    func.initialize( env, desc );
+
+    assertThat( func.resolve( ctx, Arrays.asList( "test-service" ) ), contains( "test-internal-host" ) );
+    assertThat( func.resolve( ctx, Arrays.asList( "invalid-test-service" ) ), contains( "invalid-test-service" ) );
+    assertThat( func.resolve( ctx, null ), nullValue() );
+
+    func.destroy();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceMappedUrlFunctionProcessorTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceMappedUrlFunctionProcessorTest.java b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceMappedUrlFunctionProcessorTest.java
new file mode 100644
index 0000000..5d01f5d
--- /dev/null
+++ b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceMappedUrlFunctionProcessorTest.java
@@ -0,0 +1,148 @@
+/**
+ * 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.svcregfunc.impl;
+
+import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteEnvironment;
+import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteContext;
+import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
+import org.apache.hadoop.gateway.services.GatewayServices;
+import org.apache.hadoop.gateway.services.hostmap.HostMapper;
+import org.apache.hadoop.gateway.services.hostmap.HostMapperService;
+import org.apache.hadoop.gateway.services.registry.ServiceRegistry;
+import org.apache.hadoop.gateway.svcregfunc.api.ServiceMappedUrlFunctionDescriptor;
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
+import static org.junit.Assert.fail;
+
+public class ServiceMappedUrlFunctionProcessorTest {
+
+  HostMapperService hms;
+  HostMapper hm;
+  ServiceRegistry reg;
+  GatewayServices svc;
+  UrlRewriteEnvironment env;
+  UrlRewriteContext ctx;
+  ServiceMappedUrlFunctionDescriptor desc;
+
+  @Before
+  public void setUp() {
+    hm = EasyMock.createNiceMock( HostMapper.class );
+    EasyMock.expect( hm.resolveInboundHostName( "test-host" ) ).andReturn( "test-internal-host" ).anyTimes();
+
+    hms = EasyMock.createNiceMock( HostMapperService.class );
+    EasyMock.expect( hms.getHostMapper( "test-cluster" ) ).andReturn( hm ).anyTimes();
+
+    reg = EasyMock.createNiceMock( ServiceRegistry.class );
+    EasyMock.expect( reg.lookupServiceURL( "test-cluster", "test-service" ) ).andReturn( "test-scheme://test-host:777/test-path" ).anyTimes();
+
+    svc = EasyMock.createNiceMock( GatewayServices.class );
+    EasyMock.expect( svc.getService( GatewayServices.SERVICE_REGISTRY_SERVICE ) ).andReturn( reg ).anyTimes();
+    EasyMock.expect( svc.getService( GatewayServices.HOST_MAPPING_SERVICE ) ).andReturn( hms ).anyTimes();
+
+    env = EasyMock.createNiceMock( UrlRewriteEnvironment.class );
+    EasyMock.expect( env.getAttribute( GatewayServices.GATEWAY_SERVICES_ATTRIBUTE ) ).andReturn( svc ).anyTimes();
+    EasyMock.expect( env.getAttribute( GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE ) ).andReturn( "test-cluster" ).anyTimes();
+
+    ctx = EasyMock.createNiceMock( UrlRewriteContext.class );
+    EasyMock.expect( ctx.getDirection() ).andReturn( UrlRewriter.Direction.IN ).anyTimes();
+
+    desc = EasyMock.createNiceMock( ServiceMappedUrlFunctionDescriptor.class );
+
+    EasyMock.replay( hm, hms, reg, svc, env, desc, ctx );
+  }
+
+  @Test
+  public void testServiceLoader() throws Exception {
+    ServiceLoader loader = ServiceLoader.load( UrlRewriteFunctionProcessor.class );
+    Iterator iterator = loader.iterator();
+    assertThat( "Service iterator empty.", iterator.hasNext() );
+    while( iterator.hasNext() ) {
+      Object object = iterator.next();
+      if( object instanceof ServiceMappedUrlFunctionProcessor ) {
+        return;
+      }
+    }
+    fail( "Failed to find " + ServiceMappedUrlFunctionProcessor.class.getName() + " via service loader." );
+  }
+
+  @Test
+  public void testName() throws Exception {
+    ServiceMappedUrlFunctionProcessor func = new ServiceMappedUrlFunctionProcessor();
+    assertThat( func.name(), is( "serviceMappedUrl" ) );
+  }
+
+  @Test
+  public void testInitialize() throws Exception {
+    ServiceMappedUrlFunctionProcessor func = new ServiceMappedUrlFunctionProcessor();
+    try {
+      func.initialize( null, desc );
+      fail( "Should have thrown an IllegalArgumentException" );
+    } catch( IllegalArgumentException e ) {
+      assertThat( e.getMessage(), containsString( "environment" ) );
+    }
+
+    func = new ServiceMappedUrlFunctionProcessor();
+    try {
+      func.initialize( env, null );
+    } catch( Exception e ) {
+      e.printStackTrace(); //NOTE: OK to use e.printStackTrace for a test failure.
+      fail( "Should not have thrown an exception" );
+    }
+
+    func.initialize( env, desc );
+
+    assertThat( func.cluster(), is( "test-cluster" ) );
+    assertThat( func.registry(), sameInstance( reg ) );
+  }
+
+  @Test
+  public void testDestroy() throws Exception {
+    ServiceMappedUrlFunctionProcessor func = new ServiceMappedUrlFunctionProcessor();
+    func.initialize( env, desc );
+    func.destroy();
+
+    assertThat( func.cluster(), nullValue() );
+    assertThat( func.registry(), nullValue() );
+  }
+
+  @Test
+  public void testResolve() throws Exception {
+    ServiceMappedUrlFunctionProcessor func = new ServiceMappedUrlFunctionProcessor();
+    func.initialize( env, desc );
+
+    assertThat( func.resolve( ctx, Arrays.asList( "test-service" ) ), contains( "test-scheme://test-internal-host:777/test-path" ) );
+    assertThat( func.resolve( ctx, Arrays.asList( "invalid-test-service" ) ), contains( "invalid-test-service" ) );
+    assertThat( func.resolve( ctx, null ), nullValue() );
+
+    func.destroy();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServicePathFunctionProcessorTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServicePathFunctionProcessorTest.java b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServicePathFunctionProcessorTest.java
index ce554ad..fc344ae 100644
--- a/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServicePathFunctionProcessorTest.java
+++ b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServicePathFunctionProcessorTest.java
@@ -22,13 +22,12 @@ import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteContext;
 import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
 import org.apache.hadoop.gateway.services.GatewayServices;
 import org.apache.hadoop.gateway.services.registry.ServiceRegistry;
-import org.apache.hadoop.gateway.svcregfunc.api.ServiceAddressFunctionDescriptor;
 import org.apache.hadoop.gateway.svcregfunc.api.ServicePathFunctionDescriptor;
-import org.apache.hadoop.gateway.svcregfunc.api.ServiceSchemeFunctionDescriptor;
 import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.ServiceLoader;
 
@@ -37,6 +36,7 @@ import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.CoreMatchers.sameInstance;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
 import static org.junit.Assert.fail;
 
 public class ServicePathFunctionProcessorTest {
@@ -126,7 +126,7 @@ public class ServicePathFunctionProcessorTest {
     func.initialize( env, desc );
 
 //    assertThat( func.resolve( ctx, "test-service" ), is( "/test-path" ) );
-    assertThat( func.resolve( ctx, "invalid-test-service" ), is( "invalid-test-service" ) );
+    assertThat( func.resolve( ctx, Arrays.asList( "invalid-test-service" ) ), contains( "invalid-test-service" ) );
 //    assertThat( func.resolve( ctx, null ), nullValue() );
 
     func.destroy();

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServicePortFunctionProcessorTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServicePortFunctionProcessorTest.java b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServicePortFunctionProcessorTest.java
index 29c993d..60ee9d7 100644
--- a/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServicePortFunctionProcessorTest.java
+++ b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServicePortFunctionProcessorTest.java
@@ -22,13 +22,12 @@ import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteContext;
 import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
 import org.apache.hadoop.gateway.services.GatewayServices;
 import org.apache.hadoop.gateway.services.registry.ServiceRegistry;
-import org.apache.hadoop.gateway.svcregfunc.api.ServiceAddressFunctionDescriptor;
 import org.apache.hadoop.gateway.svcregfunc.api.ServicePortFunctionDescriptor;
-import org.apache.hadoop.gateway.svcregfunc.api.ServiceSchemeFunctionDescriptor;
 import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.ServiceLoader;
 
@@ -37,6 +36,7 @@ import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.CoreMatchers.sameInstance;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
 import static org.junit.Assert.fail;
 
 public class ServicePortFunctionProcessorTest {
@@ -125,8 +125,8 @@ public class ServicePortFunctionProcessorTest {
     ServicePortFunctionProcessor func = new ServicePortFunctionProcessor();
     func.initialize( env, desc );
 
-    assertThat( func.resolve( ctx, "test-service" ), is( "777" ) );
-    assertThat( func.resolve( ctx, "invalid-test-service" ), is( "invalid-test-service" ) );
+    assertThat( func.resolve( ctx, Arrays.asList( "test-service" ) ), contains( "777" ) );
+    assertThat( func.resolve( ctx, Arrays.asList( "invalid-test-service" ) ), contains( "invalid-test-service" ) );
     assertThat( func.resolve( ctx, null ), nullValue() );
 
     func.destroy();

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceSchemeFunctionProcessorTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceSchemeFunctionProcessorTest.java b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceSchemeFunctionProcessorTest.java
index 021455a..f99e478 100644
--- a/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceSchemeFunctionProcessorTest.java
+++ b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceSchemeFunctionProcessorTest.java
@@ -22,12 +22,12 @@ import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteContext;
 import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
 import org.apache.hadoop.gateway.services.GatewayServices;
 import org.apache.hadoop.gateway.services.registry.ServiceRegistry;
-import org.apache.hadoop.gateway.svcregfunc.api.ServiceAddressFunctionDescriptor;
 import org.apache.hadoop.gateway.svcregfunc.api.ServiceSchemeFunctionDescriptor;
 import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.ServiceLoader;
 
@@ -36,6 +36,7 @@ import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.CoreMatchers.sameInstance;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
 import static org.junit.Assert.fail;
 
 public class ServiceSchemeFunctionProcessorTest {
@@ -124,8 +125,8 @@ public class ServiceSchemeFunctionProcessorTest {
     ServiceSchemeFunctionProcessor func = new ServiceSchemeFunctionProcessor();
     func.initialize( env, desc );
 
-    assertThat( func.resolve( ctx, "test-service" ), is( "test-scheme" ) );
-    assertThat( func.resolve( ctx, "invalid-test-service" ), is( "invalid-test-service" ) );
+    assertThat( func.resolve( ctx, Arrays.asList( "test-service" ) ), contains( "test-scheme" ) );
+    assertThat( func.resolve( ctx, Arrays.asList( "invalid-test-service" ) ), contains( "invalid-test-service" ) );
     assertThat( func.resolve( ctx, null ), nullValue() );
 
     func.destroy();

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceUrlFunctionProcessorTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceUrlFunctionProcessorTest.java b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceUrlFunctionProcessorTest.java
index b7e1dfe..b04c2c1 100644
--- a/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceUrlFunctionProcessorTest.java
+++ b/gateway-provider-rewrite-func-service-registry/src/test/java/org/apache/hadoop/gateway/svcregfunc/impl/ServiceUrlFunctionProcessorTest.java
@@ -18,29 +18,34 @@
 package org.apache.hadoop.gateway.svcregfunc.impl;
 
 import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteEnvironment;
+import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriter;
 import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteContext;
 import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
 import org.apache.hadoop.gateway.services.GatewayServices;
+import org.apache.hadoop.gateway.services.hostmap.HostMapper;
+import org.apache.hadoop.gateway.services.hostmap.HostMapperService;
 import org.apache.hadoop.gateway.services.registry.ServiceRegistry;
-import org.apache.hadoop.gateway.svcregfunc.api.ServiceAddressFunctionDescriptor;
-import org.apache.hadoop.gateway.svcregfunc.api.ServiceSchemeFunctionDescriptor;
 import org.apache.hadoop.gateway.svcregfunc.api.ServiceUrlFunctionDescriptor;
 import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.ServiceLoader;
 
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.CoreMatchers.sameInstance;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
 import static org.junit.Assert.fail;
 
 public class ServiceUrlFunctionProcessorTest {
 
+  HostMapperService hms;
+  HostMapper hm;
   ServiceRegistry reg;
   GatewayServices svc;
   UrlRewriteEnvironment env;
@@ -49,21 +54,29 @@ public class ServiceUrlFunctionProcessorTest {
 
   @Before
   public void setUp() {
+    hm = EasyMock.createNiceMock( HostMapper.class );
+    EasyMock.expect( hm.resolveInboundHostName( "test-host" ) ).andReturn( "test-internal-host" ).anyTimes();
+
+    hms = EasyMock.createNiceMock( HostMapperService.class );
+    EasyMock.expect( hms.getHostMapper( "test-cluster" ) ).andReturn( hm ).anyTimes();
+
     reg = EasyMock.createNiceMock( ServiceRegistry.class );
     EasyMock.expect( reg.lookupServiceURL( "test-cluster", "test-service" ) ).andReturn( "test-scheme://test-host:777/test-path" ).anyTimes();
 
     svc = EasyMock.createNiceMock( GatewayServices.class );
     EasyMock.expect( svc.getService( GatewayServices.SERVICE_REGISTRY_SERVICE ) ).andReturn( reg ).anyTimes();
+    EasyMock.expect( svc.getService( GatewayServices.HOST_MAPPING_SERVICE ) ).andReturn( hms ).anyTimes();
 
     env = EasyMock.createNiceMock( UrlRewriteEnvironment.class );
     EasyMock.expect( env.getAttribute( GatewayServices.GATEWAY_SERVICES_ATTRIBUTE ) ).andReturn( svc ).anyTimes();
     EasyMock.expect( env.getAttribute( GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE ) ).andReturn( "test-cluster" ).anyTimes();
 
     ctx = EasyMock.createNiceMock( UrlRewriteContext.class );
+    EasyMock.expect( ctx.getDirection() ).andReturn( UrlRewriter.Direction.IN ).anyTimes();
 
     desc = EasyMock.createNiceMock( ServiceUrlFunctionDescriptor.class );
 
-    EasyMock.replay( reg, svc, env, desc, ctx );
+    EasyMock.replay( hm, hms, reg, svc, env, desc, ctx );
   }
 
   @Test
@@ -125,8 +138,8 @@ public class ServiceUrlFunctionProcessorTest {
     ServiceUrlFunctionProcessor func = new ServiceUrlFunctionProcessor();
     func.initialize( env, desc );
 
-    assertThat( func.resolve( ctx, "test-service" ), is( "test-scheme://test-host:777/test-path" ) );
-    assertThat( func.resolve( ctx, "invalid-test-service" ), is( "invalid-test-service" ) );
+    assertThat( func.resolve( ctx, Arrays.asList( "test-service" ) ), contains( "test-scheme://test-host:777/test-path" ) );
+    assertThat( func.resolve( ctx, Arrays.asList( "invalid-test-service" ) ), contains( "invalid-test-service" ) );
     assertThat( func.resolve( ctx, null ), nullValue() );
 
     func.destroy();

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/api/UrlRewriteProcessor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/api/UrlRewriteProcessor.java b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/api/UrlRewriteProcessor.java
index a6efaf0..0b7c62c 100644
--- a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/api/UrlRewriteProcessor.java
+++ b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/api/UrlRewriteProcessor.java
@@ -20,7 +20,6 @@ package org.apache.hadoop.gateway.filter.rewrite.api;
 import org.apache.hadoop.gateway.filter.rewrite.i18n.UrlRewriteMessages;
 import org.apache.hadoop.gateway.filter.rewrite.impl.UrlRewriteContextImpl;
 import org.apache.hadoop.gateway.filter.rewrite.impl.UrlRewriteFunctionProcessorFactory;
-import org.apache.hadoop.gateway.filter.rewrite.impl.UrlRewriteFunctionResolver;
 import org.apache.hadoop.gateway.filter.rewrite.impl.UrlRewriteStepProcessorHolder;
 import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteContext;
 import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
@@ -30,10 +29,8 @@ import org.apache.hadoop.gateway.util.urltemplate.Matcher;
 import org.apache.hadoop.gateway.util.urltemplate.Resolver;
 import org.apache.hadoop.gateway.util.urltemplate.Template;
 
-import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 import static org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriter.Direction.IN;
@@ -124,8 +121,7 @@ public class UrlRewriteProcessor implements UrlRewriter {
   }
 
   @Override
-  public Template rewrite(
-      Resolver resolver, Template inputUri, Direction direction, String ruleName ) {
+  public Template rewrite( Resolver resolver, Template inputUri, Direction direction, String ruleName ) {
     Template outputUri = inputUri;
     UrlRewriteStepProcessorHolder stepHolder = null;
     if( ruleName == null || "*".equals( ruleName ) ) {
@@ -145,8 +141,7 @@ public class UrlRewriteProcessor implements UrlRewriter {
       stepHolder = rules.get( ruleName );
     }
     if( stepHolder != null ) {
-      UrlRewriteFunctionResolver function = new UrlRewriteFunctionResolver( functions, resolver );
-      UrlRewriteContext context = new UrlRewriteContextImpl( environment, function, direction, inputUri );
+      UrlRewriteContext context = new UrlRewriteContextImpl( environment, resolver, functions, direction, inputUri );
       try {
         UrlRewriteStepStatus stepStatus = stepHolder.process( context );
         if( UrlRewriteStepStatus.SUCCESS == stepStatus ) {

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/ext/UrlRewriteActionRewriteProcessorExt.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/ext/UrlRewriteActionRewriteProcessorExt.java b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/ext/UrlRewriteActionRewriteProcessorExt.java
index e1ca859..d0785a6 100644
--- a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/ext/UrlRewriteActionRewriteProcessorExt.java
+++ b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/ext/UrlRewriteActionRewriteProcessorExt.java
@@ -44,7 +44,7 @@ public class UrlRewriteActionRewriteProcessorExt
 
   @Override
   public UrlRewriteStepStatus process( UrlRewriteContext context ) throws Exception {
-    Template rewritten = expander.expandToTemplate( template, context.getParameters() );
+    Template rewritten = expander.expandToTemplate( template, context.getParameters(), context.getEvaluator() );
     context.setCurrentUrl( rewritten );
     return UrlRewriteStepStatus.SUCCESS;
   }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteContextImpl.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteContextImpl.java b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteContextImpl.java
index 75963eb..57e54c0 100644
--- a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteContextImpl.java
+++ b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteContextImpl.java
@@ -21,12 +21,13 @@ import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteEnvironment;
 import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriter;
 import org.apache.hadoop.gateway.filter.rewrite.i18n.UrlRewriteMessages;
 import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteContext;
-import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteResolver;
+import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
+import org.apache.hadoop.gateway.util.urltemplate.Evaluator;
 import org.apache.hadoop.gateway.util.urltemplate.Params;
+import org.apache.hadoop.gateway.util.urltemplate.Resolver;
 import org.apache.hadoop.gateway.util.urltemplate.Template;
 
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -37,7 +38,9 @@ public class UrlRewriteContextImpl implements UrlRewriteContext {
   private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
 
   private UrlRewriteEnvironment environment;
-  private UrlRewriteResolver resolver;
+  private Resolver resolver;
+  private Evaluator evaluator;
+  private Map<String,UrlRewriteFunctionProcessor> functions;
   private ContextParameters params;
   private UrlRewriter.Direction direction;
   private Template originalUrl;
@@ -45,12 +48,15 @@ public class UrlRewriteContextImpl implements UrlRewriteContext {
 
   public UrlRewriteContextImpl(
       UrlRewriteEnvironment environment,
-      UrlRewriteResolver resolver,
+      Resolver resolver,
+      Map<String,UrlRewriteFunctionProcessor> functions,
       UrlRewriter.Direction direction,
       Template url ) {
     this.environment = environment;
     this.resolver = resolver;
+    this.functions = functions;
     this.params = new ContextParameters();
+    this.evaluator = new ContextEvaluator();
     this.direction = direction;
     this.originalUrl = url;
     this.currentUrl = url;
@@ -86,6 +92,11 @@ public class UrlRewriteContextImpl implements UrlRewriteContext {
     return params;
   }
 
+  @Override
+  public Evaluator getEvaluator() {
+    return evaluator;
+  }
+
   private class ContextParameters implements Params {
 
     Map<String,List<String>> map = new HashMap<String,List<String>>();
@@ -97,14 +108,12 @@ public class UrlRewriteContextImpl implements UrlRewriteContext {
 
     @Override
     public List<String> resolve( String name ) {
-      List<String> values = map.get( name ); // Try to fine the name in the context map.
+      List<String> values = map.get( name ); // Try to find the name in the context map.
       if( values == null ) {
         try {
-          String value = resolver.resolve( UrlRewriteContextImpl.this, name );
-          if( value != null ) {
-            values = Arrays.asList( value ); // Try to fine the name in the resolver chain.
-          } else {
-            values = environment.resolve( name ); // Try to fine the name in the environment.
+          values = resolver.resolve( name );
+          if( values == null ) {
+            values = environment.resolve( name ); // Try to find the name in the environment.
           }
         } catch( Exception e ) {
           LOG.failedToFindValuesByParameter( name, e );
@@ -122,4 +131,22 @@ public class UrlRewriteContextImpl implements UrlRewriteContext {
 
   }
 
+  private class ContextEvaluator implements Evaluator {
+
+    @Override
+    public List<String> evaluate( String function, List<String> parameters ) {
+      List<String> results = null;
+      UrlRewriteFunctionProcessor processor = functions.get( function );
+      if( processor != null ) {
+        try {
+          results = processor.resolve( UrlRewriteContextImpl.this, parameters );
+        } catch( Exception e ) {
+          LOG.failedToInvokeRewriteFunction( function, e );
+          results = null;
+        }
+      }
+      return results;
+    }
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteFunctionResolver.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteFunctionResolver.java b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteFunctionResolver.java
deleted file mode 100644
index be65591..0000000
--- a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteFunctionResolver.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * 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.filter.rewrite.impl;
-
-import org.apache.hadoop.gateway.filter.rewrite.i18n.UrlRewriteMessages;
-import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteContext;
-import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
-import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteResolver;
-import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
-import org.apache.hadoop.gateway.util.urltemplate.Resolver;
-
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-
-public class UrlRewriteFunctionResolver implements UrlRewriteResolver {
-
-  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
-
-  private Map<String,UrlRewriteFunctionProcessor> functions;
-  private Resolver delegate;
-  private enum TokenType { FUNCTION, DIRECT_PARAMETER, INDIRECT_PARAMETER}
-
-  public UrlRewriteFunctionResolver( Map<String,UrlRewriteFunctionProcessor> functions, Resolver delegate ) {
-    this.functions = functions;
-    this.delegate = delegate;
-  }
-
-  @Override
-  public String resolve( UrlRewriteContext context, String parameter ) throws Exception {
-//    System.out.println( "RESOLVE: " + parameter );
-    String value = null;
-    String function = null;
-    if( parameter != null && parameter.startsWith( "$" ) ) {
-      StringTokenizer parser = new StringTokenizer( parameter, "$()[]", true );
-      parser.nextToken(); // Consume the $
-      TokenType tokenType = TokenType.FUNCTION;
-      while( parser.hasMoreTokens() ) {
-        String token = parser.nextToken();
-        //Note: Currently () implies a variable parameter and [] a constant parameter.
-        if( "(".equals( token ) ) {
-          tokenType = TokenType.INDIRECT_PARAMETER;
-        } else if( "[".equals( token ) ) {
-          tokenType = TokenType.DIRECT_PARAMETER;
-        } else if ( ")".equals( token ) ) {
-          break;
-        } else if( "]".equals( token ) ) {
-          break;
-        } else if( tokenType.equals( TokenType.FUNCTION ) ) {
-          function = token;
-        } else {
-          parameter = token;
-        }
-      }
-      if( tokenType.equals( TokenType.INDIRECT_PARAMETER ) ) {
-        value = getFirstValue( context.getParameters().resolve( parameter ) );
-        if( value != null ) {
-          parameter = value;
-        } else {
-          parameter = invokeDelegate( parameter );
-        }
-      }
-      value = invokeFunction( context, function, parameter );
-    } else {
-      value = getFirstValue( delegate.resolve( parameter ) );
-    }
-    return value;
-  }
-
-  private String invokeFunction( UrlRewriteContext context, String function, String parameter ) {
-    String value = parameter;
-    if( function != null ) {
-      UrlRewriteResolver resolver = functions.get( function );
-      if( resolver != null ) {
-        try {
-          value = resolver.resolve( context, parameter );
-        } catch( Exception e ) {
-          LOG.failedToInvokeRewriteFunction( function, e );
-          // Ignore it and use the original parameter values.
-        }
-      }
-    }
-    return value;
-  }
-
-  private String invokeDelegate( String parameter ) {
-    List<String> values = delegate.resolve( parameter );
-    String value = getFirstValue( values );
-    return value;
-  }
-
-  private static String getFirstValue( List<String> values ) {
-    String value = null;
-    if( values != null && !values.isEmpty() ) {
-      value = values.get( 0 );
-    }
-    return value;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/spi/UrlRewriteContext.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/spi/UrlRewriteContext.java b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/spi/UrlRewriteContext.java
index 193d1b3..5fe99a4 100644
--- a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/spi/UrlRewriteContext.java
+++ b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/spi/UrlRewriteContext.java
@@ -18,6 +18,7 @@
 package org.apache.hadoop.gateway.filter.rewrite.spi;
 
 import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.hadoop.gateway.util.urltemplate.Evaluator;
 import org.apache.hadoop.gateway.util.urltemplate.Params;
 import org.apache.hadoop.gateway.util.urltemplate.Template;
 
@@ -35,4 +36,6 @@ public interface UrlRewriteContext {
 
   Params getParameters();
 
+  Evaluator getEvaluator();
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/spi/UrlRewriteResolver.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/spi/UrlRewriteResolver.java b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/spi/UrlRewriteResolver.java
index 8b51dc3..ec07b5b 100644
--- a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/spi/UrlRewriteResolver.java
+++ b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/spi/UrlRewriteResolver.java
@@ -17,8 +17,10 @@
  */
 package org.apache.hadoop.gateway.filter.rewrite.spi;
 
+import java.util.List;
+
 public interface UrlRewriteResolver {
 
-  String resolve( UrlRewriteContext context, String parameter ) throws Exception;
+  List<String> resolve( UrlRewriteContext context, List<String> parameter ) throws Exception;
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-provider-rewrite/src/test/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteContextImplTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/test/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteContextImplTest.java b/gateway-provider-rewrite/src/test/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteContextImplTest.java
index b743f69..88587ea 100644
--- a/gateway-provider-rewrite/src/test/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteContextImplTest.java
+++ b/gateway-provider-rewrite/src/test/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteContextImplTest.java
@@ -19,16 +19,18 @@ package org.apache.hadoop.gateway.filter.rewrite.impl;
 
 import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteEnvironment;
 import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriter;
-import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteContext;
-import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteResolver;
+import org.apache.hadoop.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
 import org.apache.hadoop.gateway.util.urltemplate.Params;
 import org.apache.hadoop.gateway.util.urltemplate.Parser;
+import org.apache.hadoop.gateway.util.urltemplate.Resolver;
 import org.apache.hadoop.gateway.util.urltemplate.Template;
 import org.easymock.EasyMock;
 import org.junit.Test;
 
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
@@ -42,15 +44,17 @@ public class UrlRewriteContextImplTest {
     UrlRewriteEnvironment environment = EasyMock.createNiceMock( UrlRewriteEnvironment.class );
     EasyMock.expect( environment.resolve( "test-env-param-name" ) ).andReturn( Arrays.asList( "test-env-param-value" ) ).anyTimes();
 
-    UrlRewriteResolver resolver = EasyMock.createNiceMock( UrlRewriteResolver.class );
-    EasyMock.expect( resolver.resolve( EasyMock.anyObject(UrlRewriteContext.class), EasyMock.eq( "test-ctx-param-name" ) ) ).andReturn( "test-ctx-param-value" );
+    Resolver resolver = EasyMock.createNiceMock( Resolver.class );
+    EasyMock.expect( resolver.resolve( "test-ctx-param-name" ) ).andReturn( Arrays.asList( "test-ctx-param-value" ) );
 
     EasyMock.replay( environment, resolver );
 
+    Map<String,UrlRewriteFunctionProcessor> functions = new HashMap<String,UrlRewriteFunctionProcessor>();
+
     UrlRewriter.Direction direction = UrlRewriter.Direction.OUT;
     Template template = Parser.parse( "scheme://host:port/dir/file" );
 
-    UrlRewriteContextImpl context = new UrlRewriteContextImpl( environment, resolver, direction, template );
+    UrlRewriteContextImpl context = new UrlRewriteContextImpl( environment, resolver, functions, direction, template );
 
     Params params = context.getParameters();
     List<String> values = params.resolve( "test-env-param-name" );

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-release/home/deployments/sample.xml
----------------------------------------------------------------------
diff --git a/gateway-release/home/deployments/sample.xml b/gateway-release/home/deployments/sample.xml
index bb0c1e7..4474288 100644
--- a/gateway-release/home/deployments/sample.xml
+++ b/gateway-release/home/deployments/sample.xml
@@ -58,11 +58,11 @@
 
     <service>
         <role>NAMENODE</role>
-        <url>hdfs://sandbox.hortonworks.com:8020</url>
+        <url>hdfs://localhost:8020</url>
     </service>
     <service>
         <role>JOBTRACKER</role>
-        <url>rpc://sandbox.hortonworks.com:8050</url>
+        <url>rpc://localhost:8050</url>
     </service>
     <service>
         <role>WEBHDFS</role>

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java
index 76e01ef..ca468e1 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java
@@ -153,15 +153,6 @@ public interface GatewayMessages {
   @Message( level = MessageLevel.DEBUG, text = "Apache Knox Gateway {0} ({1})" )
   void gatewayVersionMessage( String version, String hash );
 
-  @Message( level = MessageLevel.DEBUG, text = "Loading from persistent master: {0}" )
-  void loadingFromPersistentMaster( String tag );
-
-  @Message( level = MessageLevel.DEBUG, text = "ALIAS: {0}" )
-  void printClusterAlias( String alias );
-
-  @Message( level = MessageLevel.DEBUG, text = "MASTER SERVICE == NULL: {0}" )
-  void printMasterServiceIsNull( boolean masterServiceIsNull );
-
   @Message( level = MessageLevel.ERROR, text = "Failed to inject service {0}: {1}" )
   void failedToInjectService( String serviceName, @StackTrace( level = MessageLevel.DEBUG ) Exception e );
 

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/UrlConnectionDispatch.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/UrlConnectionDispatch.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/UrlConnectionDispatch.java
index b315048..8e2145b 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/UrlConnectionDispatch.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/UrlConnectionDispatch.java
@@ -60,7 +60,7 @@ public class UrlConnectionDispatch extends AbstractGatewayDispatch {
 
     Resolver resolver = new DispatchParamResolver( getConfig(), request );
     URI sourceUri = new URI( sourcePathInfo );
-    URI targetUri = Rewriter.rewrite( sourceUri, sourceTemplate, targetTemplate, resolver );
+    URI targetUri = Rewriter.rewrite( sourceUri, sourceTemplate, targetTemplate, resolver, null );
 
 //    //TODO: This should be more at filter init.
 //    Pattern sourceRegex = UrlRewriter.compileUrlRegex( sourcePattern );

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-server/src/main/java/org/apache/hadoop/gateway/services/DefaultGatewayServices.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/DefaultGatewayServices.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/DefaultGatewayServices.java
index b5687c1..b9055f2 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/DefaultGatewayServices.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/DefaultGatewayServices.java
@@ -23,7 +23,7 @@ import org.apache.hadoop.gateway.deploy.DeploymentContext;
 import org.apache.hadoop.gateway.descriptor.FilterParamDescriptor;
 import org.apache.hadoop.gateway.descriptor.ResourceDescriptor;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
-import org.apache.hadoop.gateway.services.hostmap.impl.DefaultHostMappingService;
+import org.apache.hadoop.gateway.services.hostmap.impl.DefaultHostMapperService;
 import org.apache.hadoop.gateway.services.registry.impl.DefaultServiceRegistryService;
 import org.apache.hadoop.gateway.services.security.KeystoreServiceException;
 import org.apache.hadoop.gateway.services.security.SSLService;
@@ -89,7 +89,7 @@ public class DefaultGatewayServices implements GatewayServices {
     sr.init( config, options );
     services.put( SERVICE_REGISTRY_SERVICE, sr );
 
-    DefaultHostMappingService hm = new DefaultHostMappingService();
+    DefaultHostMapperService hm = new DefaultHostMapperService();
     hm.init( config, options );
     services.put( HOST_MAPPING_SERVICE, hm );
   }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-server/src/main/java/org/apache/hadoop/gateway/services/HssoGatewayServices.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/HssoGatewayServices.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/HssoGatewayServices.java
index 589be1b..6992429 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/HssoGatewayServices.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/HssoGatewayServices.java
@@ -17,21 +17,13 @@
  */
 package org.apache.hadoop.gateway.services;
 
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 import org.apache.hadoop.gateway.GatewayMessages;
 import org.apache.hadoop.gateway.config.GatewayConfig;
 import org.apache.hadoop.gateway.deploy.DeploymentContext;
 import org.apache.hadoop.gateway.descriptor.FilterParamDescriptor;
 import org.apache.hadoop.gateway.descriptor.ResourceDescriptor;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
-import org.apache.hadoop.gateway.services.GatewayServices;
-import org.apache.hadoop.gateway.services.Service;
-import org.apache.hadoop.gateway.services.ServiceLifecycleException;
-import org.apache.hadoop.gateway.services.hostmap.impl.DefaultHostMappingService;
+import org.apache.hadoop.gateway.services.hostmap.impl.DefaultHostMapperService;
 import org.apache.hadoop.gateway.services.registry.impl.DefaultServiceRegistryService;
 import org.apache.hadoop.gateway.services.security.KeystoreServiceException;
 import org.apache.hadoop.gateway.services.security.SSLService;
@@ -43,6 +35,11 @@ import org.apache.hadoop.gateway.services.security.impl.JettySSLService;
 import org.apache.hadoop.gateway.services.token.impl.DefaultTokenAuthorityService;
 import org.apache.hadoop.gateway.topology.Provider;
 
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 public class HssoGatewayServices implements GatewayServices {
 
   private static GatewayMessages log = MessagesFactory.get( GatewayMessages.class );
@@ -92,7 +89,7 @@ public class HssoGatewayServices implements GatewayServices {
     ssl.init(config, options);
     services.put(SSL_SERVICE, ssl);
     
-    DefaultHostMappingService hm = new DefaultHostMappingService();
+    DefaultHostMapperService hm = new DefaultHostMapperService();
     hm.init( config, options );
     services.put( HOST_MAPPING_SERVICE, hm );
   }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-server/src/main/java/org/apache/hadoop/gateway/services/hostmap/impl/DefaultHostMapperService.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/hostmap/impl/DefaultHostMapperService.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/hostmap/impl/DefaultHostMapperService.java
new file mode 100644
index 0000000..ba6fd67
--- /dev/null
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/hostmap/impl/DefaultHostMapperService.java
@@ -0,0 +1,81 @@
+/**
+ * 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.services.hostmap.impl;
+
+import org.apache.hadoop.gateway.config.GatewayConfig;
+import org.apache.hadoop.gateway.services.ServiceLifecycleException;
+import org.apache.hadoop.gateway.services.hostmap.HostMapper;
+import org.apache.hadoop.gateway.services.hostmap.HostMapperService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ *
+ *
+ */
+public class DefaultHostMapperService implements HostMapperService {
+
+  private ConcurrentHashMap<String, HostMapper> map = new ConcurrentHashMap<String, HostMapper>();
+
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.services.Service#init(org.apache.hadoop.gateway.config.GatewayConfig, java.util.Map)
+   */
+  @Override
+  public void init( GatewayConfig config, Map<String, String> options ) throws ServiceLifecycleException {
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.services.Service#start()
+   */
+  @Override
+  public void start() throws ServiceLifecycleException {
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.services.Service#stop()
+   */
+  @Override
+  public void stop() throws ServiceLifecycleException {
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.services.hostmap.HostMappingService#getHostMapper(java.lang.String)
+   */
+  @Override
+  public HostMapper getHostMapper( String clusterName ) {
+    return map.get( clusterName );
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.services.hostmap.HostMappingService#registerHostMapperForCluster(java.lang.String, org.apache.hadoop.gateway.services.hostmap.HostMapper)
+   */
+  @Override
+  public void registerHostMapperForCluster( String clusterName, HostMapper hostMapper ) {
+    map.put( clusterName, hostMapper );
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.hadoop.gateway.services.hostmap.HostMappingService#removeHostMapperForCluster(java.lang.String)
+   */
+  @Override
+  public void removeHostMapperForCluster( String clusterName ) {
+    map.remove( clusterName );
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-server/src/main/java/org/apache/hadoop/gateway/services/hostmap/impl/DefaultHostMappingService.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/hostmap/impl/DefaultHostMappingService.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/hostmap/impl/DefaultHostMappingService.java
deleted file mode 100644
index 5d094dd..0000000
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/hostmap/impl/DefaultHostMappingService.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * 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.services.hostmap.impl;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.hadoop.gateway.config.GatewayConfig;
-import org.apache.hadoop.gateway.services.ServiceLifecycleException;
-import org.apache.hadoop.gateway.services.hostmap.FileBasedHostMapper;
-import org.apache.hadoop.gateway.services.hostmap.HostMappingService;
-import org.apache.hadoop.gateway.services.hostmap.HostMapper;
-
-/**
- * 
- *
- */
-public class DefaultHostMappingService implements HostMappingService {
-  ConcurrentHashMap<String, FileBasedHostMapper> map = new ConcurrentHashMap<String, FileBasedHostMapper>();
-  
-  /* (non-Javadoc)
-   * @see org.apache.hadoop.gateway.services.Service#init(org.apache.hadoop.gateway.config.GatewayConfig, java.util.Map)
-   */
-  @Override
-  public void init(GatewayConfig config, Map<String, String> options)
-      throws ServiceLifecycleException {
-  }
-
-  /* (non-Javadoc)
-   * @see org.apache.hadoop.gateway.services.Service#start()
-   */
-  @Override
-  public void start() throws ServiceLifecycleException {
-  }
-
-  /* (non-Javadoc)
-   * @see org.apache.hadoop.gateway.services.Service#stop()
-   */
-  @Override
-  public void stop() throws ServiceLifecycleException {
-  }
-
-  /* (non-Javadoc)
-   * @see org.apache.hadoop.gateway.services.hostmap.HostMappingService#getHostMapper(java.lang.String)
-   */
-  @Override
-  public HostMapper getHostMapper(String clusterName) {
-    return null;
-  }
-
-  /* (non-Javadoc)
-   * @see org.apache.hadoop.gateway.services.hostmap.HostMappingService#registerHostMapperForCluster(java.lang.String, org.apache.hadoop.gateway.services.hostmap.HostMapper)
-   */
-  @Override
-  public void registerHostMapperForCluster(String clusterName,
-      FileBasedHostMapper hostMapper) {
-    map.put(clusterName, hostMapper);
-  }
-
-  /* (non-Javadoc)
-   * @see org.apache.hadoop.gateway.services.hostmap.HostMappingService#removeHostMapperForCluster(java.lang.String)
-   */
-  @Override
-  public void removeHostMapperForCluster(String clusterName) {
-    map.remove(clusterName);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-service-hdfs/src/main/resources/org/apache/hadoop/gateway/hdfs/WebHdfsDeploymentContributor/rewrite.xml
----------------------------------------------------------------------
diff --git a/gateway-service-hdfs/src/main/resources/org/apache/hadoop/gateway/hdfs/WebHdfsDeploymentContributor/rewrite.xml b/gateway-service-hdfs/src/main/resources/org/apache/hadoop/gateway/hdfs/WebHdfsDeploymentContributor/rewrite.xml
index db6069b..d0bd2e2 100644
--- a/gateway-service-hdfs/src/main/resources/org/apache/hadoop/gateway/hdfs/WebHdfsDeploymentContributor/rewrite.xml
+++ b/gateway-service-hdfs/src/main/resources/org/apache/hadoop/gateway/hdfs/WebHdfsDeploymentContributor/rewrite.xml
@@ -32,7 +32,7 @@
     </rule>
 
     <rule dir="IN" name="WEBHDFS/webhdfs/inbound/hdfs" pattern="hdfs:/{path=**}?{**}">
-        <rewrite template="{$serviceUrl[NAMENODE]}/{path=**}?{**}"/>
+        <rewrite template="{$serviceMappedUrl[NAMENODE]}/{path=**}?{**}"/>
     </rule>
 
     <rule dir="IN" name="WEBHDFS/webhdfs/inbound/webhdfs" pattern="webhdfs:/{path=**}?{**}">

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiMessages.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiMessages.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiMessages.java
index 6234ef2..669afd7 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiMessages.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/i18n/GatewaySpiMessages.java
@@ -64,4 +64,13 @@ public interface GatewaySpiMessages {
   @Message( level = MessageLevel.ERROR, text = "Failed to get key {0}: {1}" )
   void failedToGetKey(String alias, @StackTrace( level = MessageLevel.DEBUG ) Exception e);
 
+  @Message( level = MessageLevel.DEBUG, text = "Loading from persistent master: {0}" )
+  void loadingFromPersistentMaster( String tag );
+
+  @Message( level = MessageLevel.DEBUG, text = "ALIAS: {0}" )
+  void printClusterAlias( String alias );
+
+  @Message( level = MessageLevel.DEBUG, text = "MASTER SERVICE == NULL: {0}" )
+  void printMasterServiceIsNull( boolean masterServiceIsNull );
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapper.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapper.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapper.java
index 5af0744..cdfbddf 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapper.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapper.java
@@ -26,53 +26,56 @@ import java.util.HashMap;
 import java.util.Map;
 
 public class FileBasedHostMapper implements HostMapper {
+
   private Map<String, String> inbound = new HashMap<String, String>();
   private Map<String, String> outbound = new HashMap<String, String>();
-  
-  public FileBasedHostMapper(String clusterName, URL url) {
+
+  public FileBasedHostMapper( URL url ) throws IOException {
     if( url != null ) {
-      InputStream stream;
-      try {
-        stream = url.openStream();
-        BufferedReader reader = new BufferedReader( new InputStreamReader( stream ) );
-        String line = reader.readLine();
-        while( line != null ) {
-          String[] lineSplit = line.split( "=" );
-          if( lineSplit.length >= 2 ) {
-            String[] externalSplit = lineSplit[ 0 ].split( "," );
-            String[] internalSplit = lineSplit[ 1 ].split( "," );
-            if( externalSplit.length >= 1 && internalSplit.length >= 1 ) {
-              for( String external : externalSplit ) {
-                inbound.put( external, internalSplit[ 0 ] );
-              }
-              for( String internal : internalSplit ) {
-                outbound.put( internal, externalSplit[ 0 ] );
-              }
+      InputStream stream = url.openStream();
+      BufferedReader reader = new BufferedReader( new InputStreamReader( stream ) );
+      String line = reader.readLine();
+      while( line != null ) {
+        String[] lineSplit = line.split( "=" );
+        if( lineSplit.length >= 2 ) {
+          String[] externalSplit = lineSplit[ 0 ].split( "," );
+          String[] internalSplit = lineSplit[ 1 ].split( "," );
+          if( externalSplit.length >= 1 && internalSplit.length >= 1 ) {
+            for( String external : externalSplit ) {
+              inbound.put( external.trim(), internalSplit[ 0 ].trim() );
+            }
+            for( String internal : internalSplit ) {
+              outbound.put( internal.trim(), externalSplit[ 0 ].trim() );
             }
           }
-          line = reader.readLine();
         }
-        reader.close();
-      } catch (IOException e) {
-        // TODO Auto-generated catch block
-        e.printStackTrace();
+        line = reader.readLine();
       }
+      reader.close();
     }
   }
-  
+
   /* (non-Javadoc)
    * @see org.apache.hadoop.gateway.services.hostmap.HostMapper#resolveInboundHostName(java.lang.String)
    */
   @Override
-  public String resolveInboundHostName(String inboundHost) {
-    return inbound.get(inboundHost);
+  public String resolveInboundHostName( String hostName ) {
+    String resolvedHostName = inbound.get( hostName );
+    if( resolvedHostName == null ) {
+      resolvedHostName = hostName;
+    }
+    return resolvedHostName;
   }
 
   /* (non-Javadoc)
    * @see org.apache.hadoop.gateway.services.hostmap.HostMapper#resolveOutboundHostName(java.lang.String)
    */
   @Override
-  public String resolveOutboundHostName(String outboundHost) {
-    return outbound.get(outboundHost);
+  public String resolveOutboundHostName( String hostName ) {
+    String resolvedHostName = outbound.get( hostName );
+    if( resolvedHostName == null ) {
+      resolvedHostName = hostName;
+    }
+    return resolvedHostName;
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMapper.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMapper.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMapper.java
index 14ab0ac..e067371 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMapper.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMapper.java
@@ -22,8 +22,16 @@ package org.apache.hadoop.gateway.services.hostmap;
  */
 public interface HostMapper {
 
-  public abstract String resolveInboundHostName(String inboundHost);
+  /**
+   * @param inboundHost
+   * @return
+   */
+  public abstract String resolveInboundHostName( String inboundHost );
 
-  public abstract String resolveOutboundHostName(String outboundHost);
+  /**
+   * @param outboundHost
+   * @return
+   */
+  public abstract String resolveOutboundHostName( String outboundHost );
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMapperService.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMapperService.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMapperService.java
new file mode 100644
index 0000000..1e784d8
--- /dev/null
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMapperService.java
@@ -0,0 +1,41 @@
+/**
+ * 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.services.hostmap;
+
+import org.apache.hadoop.gateway.services.Service;
+
+public interface HostMapperService extends Service {
+
+  /**
+   * @param clusterName
+   * @return
+   */
+  HostMapper getHostMapper( String clusterName );
+
+  /**
+   * @param clusterName
+   * @param hostMapper
+   */
+  void registerHostMapperForCluster( String clusterName, HostMapper hostMapper );
+
+  /**
+   * @param clusterName
+   */
+  void removeHostMapperForCluster( String clusterName );
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMappingService.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMappingService.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMappingService.java
deleted file mode 100644
index a950724..0000000
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/hostmap/HostMappingService.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * 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.services.hostmap;
-
-import org.apache.hadoop.gateway.services.Service;
-
-public interface HostMappingService extends Service {
-  HostMapper getHostMapper(String clusterName);
-
-  /**
-   * @param clusterName
-   * @param hostMapper
-   */
-  void registerHostMapperForCluster(String clusterName, FileBasedHostMapper hostMapper);
-
-  /**
-   * @param clusterName
-   */
-  void removeHostMapperForCluster(String clusterName);
-}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/CMFMasterService.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/CMFMasterService.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/CMFMasterService.java
index 4d6b8dd..28d027b 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/CMFMasterService.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/CMFMasterService.java
@@ -17,13 +17,6 @@
  */
 package org.apache.hadoop.gateway.services.security.impl;
 
-import java.io.Console;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.net.ntp.TimeStamp;
@@ -32,6 +25,13 @@ import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
 import org.apache.hadoop.gateway.services.ServiceLifecycleException;
 import org.apache.hadoop.gateway.services.security.EncryptionResult;
 
+import java.io.Console;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 public class CMFMasterService {
   private static GatewaySpiMessages LOG = MessagesFactory.get( GatewaySpiMessages.class );
 
@@ -151,8 +151,7 @@ public class CMFMasterService {
       try {
         List<String> lines = FileUtils.readLines(masterFile, "UTF8");
         String tag = lines.get(0);
-        // TODO: log - if appropriate - at least at finest level
-        System.out.println("Loading from persistent master: " + tag); //TODO: I18N
+        LOG.loadingFromPersistentMaster( tag );
         String line = new String(Base64.decodeBase64(lines.get(1)));
         String[] parts = line.split("::");
         this.master = new String(aes.decrypt(Base64.decodeBase64(parts[0]), Base64.decodeBase64(parts[1]), Base64.decodeBase64(parts[2])), "UTF8").toCharArray();

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapperTest.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapperTest.java b/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapperTest.java
new file mode 100644
index 0000000..0ad867d
--- /dev/null
+++ b/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapperTest.java
@@ -0,0 +1,64 @@
+/**
+ * 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.services.hostmap;
+
+import org.apache.hadoop.test.TestUtils;
+import org.junit.Test;
+
+import java.net.URL;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class FileBasedHostMapperTest {
+
+  @Test
+  public void testEverything() throws Exception {
+    URL hostMapUrl = TestUtils.getResourceUrl( FileBasedHostMapperTest.class, "hostmap.txt" );
+    FileBasedHostMapper mapper = new FileBasedHostMapper( hostMapUrl );
+
+    assertThat( mapper.resolveInboundHostName( null ), nullValue() );
+    assertThat( mapper.resolveOutboundHostName( null ), nullValue() );
+
+    // external=internal
+    assertThat( mapper.resolveInboundHostName( "external" ), is( "internal" ) );
+    assertThat( mapper.resolveInboundHostName( "internal" ), is( "internal" ) );
+    assertThat( mapper.resolveOutboundHostName( "external" ), is( "external" ) );
+    assertThat( mapper.resolveOutboundHostName( "internal" ), is( "external" ) );
+
+    // external-space = internal-space
+    assertThat( mapper.resolveInboundHostName( "external-space" ), is( "internal-space" ) );
+    assertThat( mapper.resolveInboundHostName( "internal-space" ), is( "internal-space" ) );
+    assertThat( mapper.resolveOutboundHostName( "external-space" ), is( "external-space" ) );
+    assertThat( mapper.resolveOutboundHostName( "internal-space" ), is( "external-space" ) );
+
+//    external-list = external-list-1, external-list-2
+    assertThat( mapper.resolveInboundHostName( "external-list" ), is( "external-list-1" ) );
+
+//    internal-list-1, internal-list-2 = internal-list
+    assertThat( mapper.resolveOutboundHostName( "internal-list" ), is( "internal-list-1" ) );
+
+//    external-both-list-1, external-both-list-2 = internal-both-list-1, internal-both-list-2
+    assertThat( mapper.resolveInboundHostName( "external-both-list-1" ), is( "internal-both-list-1" ) );
+    assertThat( mapper.resolveInboundHostName( "external-both-list-2" ), is( "internal-both-list-1" ) );
+    assertThat( mapper.resolveOutboundHostName( "internal-both-list-1" ), is( "external-both-list-1" ) );
+    assertThat( mapper.resolveOutboundHostName( "internal-both-list-2" ), is( "external-both-list-1" ) );
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-spi/src/test/resources/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapperTest/hostmap.txt
----------------------------------------------------------------------
diff --git a/gateway-spi/src/test/resources/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapperTest/hostmap.txt b/gateway-spi/src/test/resources/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapperTest/hostmap.txt
new file mode 100644
index 0000000..b10a606
--- /dev/null
+++ b/gateway-spi/src/test/resources/org/apache/hadoop/gateway/services/hostmap/FileBasedHostMapperTest/hostmap.txt
@@ -0,0 +1,6 @@
+external=internal
+external-space = internal-space
+external-list = external-list-1, external-list-2
+internal-list-1, internal-list-2 = internal-list
+external-both-list-1, external-both-list-2 = internal-both-list-1, internal-both-list-2
+

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e338fe64/gateway-util-urltemplate/src/main/java/org/apache/hadoop/gateway/util/urltemplate/Evaluator.java
----------------------------------------------------------------------
diff --git a/gateway-util-urltemplate/src/main/java/org/apache/hadoop/gateway/util/urltemplate/Evaluator.java b/gateway-util-urltemplate/src/main/java/org/apache/hadoop/gateway/util/urltemplate/Evaluator.java
new file mode 100644
index 0000000..cd0b25f
--- /dev/null
+++ b/gateway-util-urltemplate/src/main/java/org/apache/hadoop/gateway/util/urltemplate/Evaluator.java
@@ -0,0 +1,26 @@
+/**
+ * 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.util.urltemplate;
+
+import java.util.List;
+
+public interface Evaluator {
+
+  List<String> evaluate( String function, List<String> parameters );
+
+}