You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by mo...@apache.org on 2017/09/01 13:17:14 UTC

[16/64] [partial] knox git commit: KNOX-998 - Refactoring save 1

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorStateTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorStateTest.java b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorStateTest.java
new file mode 100644
index 0000000..bf64905
--- /dev/null
+++ b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorStateTest.java
@@ -0,0 +1,255 @@
+/**
+ * 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.knox.gateway.filter.rewrite.impl;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteEnvironment;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFlowDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStepDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStepFlow;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteContext;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStepProcessor;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStepStatus;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class UrlRewriteStepProcessorStateTest {
+
+  @Test
+  public void testEmpty() {
+    List<UrlRewriteStepProcessorHolder> steps = new ArrayList<UrlRewriteStepProcessorHolder>();
+    UrlRewriteStepProcessorState state = new UrlRewriteStepProcessorState( steps.iterator() );
+
+    MatcherAssert
+        .assertThat( state.status(), CoreMatchers.is( UrlRewriteStepStatus.SUCCESS ) );
+    assertThat( state.hasNext(), is( false ) );
+    assertThat( state.hasNextAction(), is( false ) );
+    assertThat( state.hasNextCondition(), is( false ) );
+    assertThat( state.nextAction( UrlRewriteStepStatus.SUCCESS ), nullValue() );
+    assertThat( state.nextCondition( UrlRewriteStepStatus.SUCCESS ), nullValue() );
+  }
+
+  @Test
+  public void testNextAction() throws Exception {
+    UrlRewriteStepProcessorHolder holder;
+    List<UrlRewriteStepProcessorHolder> steps = new ArrayList<UrlRewriteStepProcessorHolder>();
+    holder = new UrlRewriteStepProcessorHolder();
+    holder.initialize( new FakeEnvironment(), new FakeActionDescriptor( "one" ), new FakeActionProcessor( "one" ) );
+    steps.add( holder );
+    holder = new UrlRewriteStepProcessorHolder();
+    holder.initialize( new FakeEnvironment(), new FakeActionDescriptor( "two" ), new FakeActionProcessor( "two" ) );
+    steps.add( holder );
+    UrlRewriteStepProcessorState state = new UrlRewriteStepProcessorState( steps.iterator() );
+    assertThat( state.hasNext(), is( true ) );
+    assertThat( state.hasNextAction(), is( true ) );
+    assertThat( state.hasNextCondition(), is( false ) );
+    assertThat( state.nextCondition( UrlRewriteStepStatus.SUCCESS ), nullValue() );
+
+    holder = state.nextAction( UrlRewriteStepStatus.SUCCESS );
+    assertThat( ((FakeActionDescriptor)holder.getDescriptor()).name, is( "one" ) );
+
+    assertThat( state.hasNext(), is( true ) );
+    assertThat( state.hasNextAction(), is( true ) );
+    assertThat( state.hasNextCondition(), is( false ) );
+    assertThat( state.nextCondition( UrlRewriteStepStatus.SUCCESS ), nullValue() );
+
+    holder = state.nextAction( UrlRewriteStepStatus.SUCCESS );
+    assertThat( ((FakeActionDescriptor)holder.getDescriptor()).name, is( "two" ) );
+
+    assertThat( state.hasNext(), is( false ) );
+    assertThat( state.hasNextAction(), is( false ) );
+    assertThat( state.hasNextCondition(), is( false ) );
+    assertThat( state.nextAction( UrlRewriteStepStatus.SUCCESS ), nullValue() );
+    assertThat( state.nextCondition( UrlRewriteStepStatus.SUCCESS ), nullValue() );
+  }
+
+  @Test
+  public void testNextCondition() throws Exception {
+    UrlRewriteStepProcessorHolder holder;
+    List<UrlRewriteStepProcessorHolder> steps = new ArrayList<UrlRewriteStepProcessorHolder>();
+    holder = new UrlRewriteStepProcessorHolder();
+    holder.initialize( new FakeEnvironment(), new FakeConditionDescriptor( "one" ), new FakeConditionProcessor( "one" ) );
+    steps.add( holder );
+    holder = new UrlRewriteStepProcessorHolder();
+    holder.initialize( new FakeEnvironment(), new FakeConditionDescriptor( "two" ), new FakeConditionProcessor( "two" ) );
+    steps.add( holder );
+    UrlRewriteStepProcessorState state = new UrlRewriteStepProcessorState( steps.iterator() );
+    assertThat( state.hasNext(), is( true ) );
+    assertThat( state.hasNextAction(), is( false ) );
+    assertThat( state.hasNextCondition(), is( true ) );
+    assertThat( state.nextAction( UrlRewriteStepStatus.SUCCESS ), nullValue() );
+
+    holder = state.nextCondition( UrlRewriteStepStatus.SUCCESS );
+    assertThat( ((FakeConditionDescriptor)holder.getDescriptor()).name, is( "one" ) );
+
+    assertThat( state.hasNext(), is( true ) );
+    assertThat( state.hasNextAction(), is( false ) );
+    assertThat( state.hasNextCondition(), is( true ) );
+    assertThat( state.nextAction( UrlRewriteStepStatus.SUCCESS ), nullValue() );
+
+    holder = state.nextCondition( UrlRewriteStepStatus.SUCCESS );
+    assertThat( ((FakeConditionDescriptor)holder.getDescriptor()).name, is( "two" ) );
+
+    assertThat( state.hasNext(), is( false ) );
+    assertThat( state.hasNextAction(), is( false ) );
+    assertThat( state.hasNextCondition(), is( false ) );
+    assertThat( state.nextAction( UrlRewriteStepStatus.SUCCESS ), nullValue() );
+    assertThat( state.nextCondition( UrlRewriteStepStatus.SUCCESS ), nullValue() );
+  }
+
+  public class FakeActionDescriptor implements UrlRewriteStepDescriptor<FakeActionDescriptor> {
+    public String name;
+
+    public FakeActionDescriptor( String name ) {
+      this.name = name;
+    }
+
+    @Override
+    public String type() {
+      return "fake-action";
+    }
+
+    @Override
+    public FakeActionDescriptor type( String type ) {
+      return null;
+    }
+
+  }
+
+  public class FakeActionProcessor implements
+      UrlRewriteStepProcessor<UrlRewriteStepDescriptor<FakeActionDescriptor>> {
+    public String name;
+
+    public FakeActionProcessor( String name ) {
+      this.name = name;
+    }
+
+    @Override
+    public String getType() {
+      return "fake-action";
+    }
+
+    @Override
+    public void initialize( UrlRewriteEnvironment environment, UrlRewriteStepDescriptor<FakeActionDescriptor> descriptor ) throws Exception {
+    }
+
+    @Override
+    public UrlRewriteStepStatus process( UrlRewriteContext context ) throws Exception {
+      return null;
+    }
+
+    @Override
+    public void destroy() throws Exception {
+    }
+  }
+
+  public class FakeConditionDescriptor implements UrlRewriteFlowDescriptor<FakeConditionDescriptor> {
+    public String name;
+
+    public FakeConditionDescriptor( String name ) {
+      this.name = name;
+    }
+
+    @Override
+    public UrlRewriteStepFlow flow() {
+      return null;
+    }
+
+    @Override
+    public FakeConditionDescriptor flow( String flow ) {
+      return null;
+    }
+
+    @Override
+    public FakeConditionDescriptor flow( UrlRewriteStepFlow flow ) {
+      return null;
+    }
+
+    @Override
+    public List<UrlRewriteStepDescriptor> steps() {
+      return null;
+    }
+
+    @Override
+    public <T extends UrlRewriteStepDescriptor<?>> T addStep( String type ) {
+      return null;
+    }
+
+    @Override
+    public String type() {
+      return "fake-condition";
+    }
+
+    @Override
+    public FakeConditionDescriptor type( String type ) {
+      return null;
+    }
+  }
+
+  public class FakeConditionProcessor implements UrlRewriteStepProcessor<FakeConditionDescriptor> {
+    public String name;
+
+    public FakeConditionProcessor( String name ) {
+      this.name = name;
+    }
+
+    @Override
+    public String getType() {
+      return "fake-condition";
+    }
+
+    @Override
+    public void initialize( UrlRewriteEnvironment environment, FakeConditionDescriptor descriptor ) throws Exception {
+    }
+
+    @Override
+    public UrlRewriteStepStatus process( UrlRewriteContext context ) throws Exception {
+      return null;
+    }
+
+    @Override
+    public void destroy() throws Exception {
+    }
+  }
+
+  private class FakeEnvironment implements UrlRewriteEnvironment {
+    @Override
+    public URL getResource( String name ) throws IOException {
+      return null;
+    }
+
+    @Override
+    public <T> T getAttribute( String name ) {
+      return null;
+    }
+
+    @Override
+    public List<String> resolve( String name ) {
+      return null;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/gateway.xml
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/gateway.xml b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/gateway.xml
new file mode 100644
index 0000000..b95b90a
--- /dev/null
+++ b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/gateway.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<gateway>
+    <resource>
+        <role>WEBHDFS</role>
+        <pattern>/webhdfs/v1/?**</pattern>
+        ...
+        <filter>
+            <role>rewrite</role>
+            <name>url-rewrite</name>
+            <class>org.apache.knox.gateway.filter.rewrite.api.UrlRewriteServletFilter</class>
+            <param><name>request.url</name><value>???????????????</value></param>
+            <param><name>request.header</name><value>???????????????</value></param>
+            <param><name>request.body</name><value>???????????????</value></param>
+        </filter>
+        ...
+    </resource>
+</gateway>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBaseTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBaseTest.java b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBaseTest.java
new file mode 100644
index 0000000..d146472
--- /dev/null
+++ b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBaseTest.java
@@ -0,0 +1,765 @@
+/**
+ * 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.knox.gateway.filter.rewrite.impl.html;
+
+import org.apache.commons.digester3.Digester;
+import org.apache.commons.digester3.ExtendedBaseRules;
+import org.apache.commons.digester3.binder.DigesterLoader;
+import org.apache.commons.io.IOUtils;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRuleDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStepDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStepFlow;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.knox.gateway.filter.rewrite.ext.UrlRewriteCheckDescriptorExt;
+import org.apache.knox.gateway.filter.rewrite.ext.UrlRewriteControlDescriptor;
+import org.apache.knox.gateway.filter.rewrite.ext.UrlRewriteMatchDescriptor;
+import org.apache.knox.gateway.filter.rewrite.ext.UrlRewriteMatchDescriptorExt;
+import org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteFilterContentDescriptorImpl;
+import org.apache.knox.gateway.filter.rewrite.impl.xml.XmlRewriteRulesDigester;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteActionDescriptorBase;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+import org.xmlmatchers.namespace.SimpleNamespaceContext;
+
+import javax.xml.namespace.QName;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+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.Matchers.contains;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.xmlmatchers.XmlMatchers.hasXPath;
+import static org.xmlmatchers.transform.XmlConverters.the;
+
+public class HtmlFilterReaderBaseTest {
+
+  public static class NoopXmlFilterReader extends HtmlFilterReaderBase {
+    public NoopXmlFilterReader( Reader reader ) throws IOException, ParserConfigurationException {
+      super( reader );
+    }
+
+    @Override
+    protected String filterText( QName elementName, String text, String ruleName ) {
+      return text;
+    }
+
+    @Override
+    protected String filterAttribute( QName elementName, QName attributeName, String attributeValue, String ruleName ) {
+      return attributeValue;
+    }
+
+    @Override
+    public String filterValueString( String name, String value, String ruleName ) {
+      return value;
+    }
+  }
+
+  public static class MapXmlFilterReader extends HtmlFilterReaderBase {
+    private Map<String,String> map;
+
+    public MapXmlFilterReader( Reader reader, Map<String,String> map ) throws IOException, ParserConfigurationException {
+      super( reader );
+      this.map = map;
+    }
+
+    @Override
+    protected String filterAttribute( QName elementName, QName attributeName, String attributeValue, String ruleName ) {
+      return map.get( attributeValue.trim() );
+    }
+
+    @Override
+    protected String filterText( QName elementName, String text, String ruleName ) {
+      return map.get( text.trim() );
+    }
+
+    @Override
+    public String filterValueString( String name, String value, String ruleName ) {
+      return map.get( value );
+    }
+  }
+
+  public static class MatchRuleXmlFilterReader extends HtmlFilterReaderBase {
+    private Map<String, Map<String,String>> rules;
+    public MatchRuleXmlFilterReader( Reader reader, Map<String, Map<String,String>> rules, UrlRewriteFilterContentDescriptor config ) throws IOException, ParserConfigurationException {
+      super( reader, config );
+      this.rules = rules;
+    }
+
+    @Override
+    protected String filterAttribute( QName elementName, QName attributeName, String attributeValue, String ruleName ) {
+      return filterValueString( attributeName.getLocalPart(), attributeValue, ruleName );
+    }
+
+    @Override
+    protected String filterText( QName elementName, String text, String ruleName ) {
+      return filterValueString( elementName.getLocalPart(), text, ruleName );
+    }
+
+    @Override
+    public String filterValueString( String name, String value, String ruleName ) {
+      Map<String, String> rule = rules.get( ruleName );
+      if ( rule == null ){
+        return value;
+      }
+      for ( Map.Entry<String, String> entry : rule.entrySet() ) {
+        if ( Pattern.compile( entry.getKey() ).matcher( value ).matches() ) {
+          return entry.getValue();
+        }
+      }
+      return value;
+    }
+  }
+
+  @Test
+  public void testSimple() throws IOException, ParserConfigurationException {
+    String inputXml = "<root/>";
+    StringReader inputReader = new StringReader( inputXml );
+    HtmlFilterReaderBase filterReader = new NoopXmlFilterReader( inputReader );
+    String outputHtml = new String( IOUtils.toCharArray( filterReader ) );
+    assertThat( the( outputHtml ), hasXPath( "/root" ) );
+  }
+
+  @Test
+  public void testSimpleNested() throws IOException, ParserConfigurationException {
+    String inputXml = "<root><child1><child11/><child12/></child1><child2><child21/><child22/></child2></root>";
+    StringReader inputReader = new StringReader( inputXml );
+    HtmlFilterReaderBase filterReader = new NoopXmlFilterReader( inputReader );
+    String outputHtml = new String( IOUtils.toCharArray( filterReader ) );
+    assertThat( the( outputHtml ), hasXPath( "/root" ) );
+    assertThat( the( outputHtml ), hasXPath( "/root/child1" ) );
+    assertThat( the( outputHtml ), hasXPath( "/root/child1/child11" ) );
+    assertThat( the( outputHtml ), hasXPath( "/root/child1/child12" ) );
+    assertThat( the( outputHtml ), hasXPath( "/root/child2" ) );
+    assertThat( the( outputHtml ), hasXPath( "/root/child2/child21" ) );
+    assertThat( the( outputHtml ), hasXPath( "/root/child2/child22" ) );
+  }
+
+  @Test
+  public void testSimpleWithNamespace() throws IOException, ParserConfigurationException {
+    String inputXml = "<ns:root xmlns:ns='http://hortonworks.com/xml/ns'></ns:root>";
+    StringReader inputReader = new StringReader( inputXml );
+    HtmlFilterReaderBase filterReader = new NoopXmlFilterReader( inputReader );
+    String outputHtml = new String( IOUtils.toCharArray( filterReader ) );
+
+    //System.out.println( outputHtml );
+    SimpleNamespaceContext ns = new SimpleNamespaceContext();
+    ns.bind( "ns", "http://hortonworks.com/xml/ns" );
+    assertThat( the( outputHtml ), hasXPath( "/ns:root", ns ) );
+  }
+
+  @Test
+  public void testSimpleTextNode() throws IOException, ParserConfigurationException {
+    String inputXml = "<root>text</root>";
+    StringReader inputReader = new StringReader( inputXml );
+    HtmlFilterReaderBase filterReader = new NoopXmlFilterReader( inputReader );
+    String outputHtml = new String( IOUtils.toCharArray( filterReader ) );
+    //System.out.println( outputHtml );
+    assertThat( the( outputHtml ), hasXPath( "/root/text()", equalTo( "text" ) ) );
+  }
+
+  @Test
+  public void testSimpleAttribute() throws IOException, ParserConfigurationException {
+    String inputXml = "<root name='value'/>";
+    StringReader inputReader = new StringReader( inputXml );
+    HtmlFilterReaderBase filterReader = new NoopXmlFilterReader( inputReader );
+    String outputHtml = new String( IOUtils.toCharArray( filterReader ) );
+    //System.out.println( outputHtml );
+    assertThat( the( outputHtml ), hasXPath( "/root/@name", equalTo( "value" ) ) );
+  }
+
+  @Test
+  public void testSimpleBooleanAttribute() throws IOException, ParserConfigurationException {
+    String inputXml = "<root name/>";
+    StringReader inputReader = new StringReader(inputXml);
+    HtmlFilterReaderBase filterReader = new NoopXmlFilterReader(inputReader);
+    String outputHtml = new String(IOUtils.toCharArray(filterReader));
+    assertEquals(inputXml, outputHtml);
+  }
+
+  @Test
+  public void testComplexBooleanAttribute() throws IOException, ParserConfigurationException {
+    String inputXml = "<root boolean non-boolean='value' empty=''/>";
+    StringReader inputReader = new StringReader(inputXml);
+    HtmlFilterReaderBase filterReader = new NoopXmlFilterReader(inputReader);
+    String outputHtml = new String(IOUtils.toCharArray(filterReader));
+    assertEquals(inputXml, outputHtml);
+  }
+
+  @Test
+  public void testMappedText() throws IOException, ParserConfigurationException {
+    Map<String,String> map = new HashMap<>();
+    map.put( "input-text", "output-text" );
+    String inputXml = "<root>input-text</root>";
+    StringReader inputReader = new StringReader( inputXml );
+    HtmlFilterReaderBase filterReader = new MapXmlFilterReader( inputReader, map );
+    String outputHtml = new String( IOUtils.toCharArray( filterReader ) );
+    //System.out.println( outputHtml );
+    assertThat( the( outputHtml ), hasXPath( "/root/text()", equalTo( "output-text" ) ) );
+  }
+
+  @Test
+  public void testMappedAttribute() throws IOException, ParserConfigurationException {
+    Map<String,String> map = new HashMap<>();
+    map.put( "input-text", "output-text" );
+    String inputXml = "<root attribute='input-text'/>";
+    StringReader inputReader = new StringReader( inputXml );
+    HtmlFilterReaderBase filterReader = new MapXmlFilterReader( inputReader, map );
+    String outputHtml = new String( IOUtils.toCharArray( filterReader ) );
+    //System.out.println( outputHtml );
+    assertThat( the( outputHtml ), hasXPath( "/root/@attribute", equalTo( "output-text" ) ) );
+  }
+
+  @Test
+  public void testCombined() throws IOException, ParserConfigurationException {
+    Map<String,String> map = new HashMap<>();
+    map.put( "attr1-input", "attr1-output" );
+    map.put( "attr2-input", "attr2-output" );
+    map.put( "attr3-input", "attr3-output" );
+    map.put( "attr4-input", "attr4-output" );
+    map.put( "attr5-input", "attr5-output" );
+    map.put( "attr6-input", "attr6-output" );
+    map.put( "attr7-input", "attr7-output" );
+    map.put( "root-input1", "root-output1" );
+    map.put( "root-input2", "root-output2" );
+    map.put( "root-input3", "root-output3" );
+    map.put( "child1-input", "child1-output" );
+    map.put( "child2-input", "child2-output" );
+
+    String inputXml =
+          "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
+            "<!-- Comment -->\n" +
+            "<ns1:root xmlns:ns1='http://hortonworks.com/xml/ns1' attr1='attr1-input' ns1:attr2='attr2-input'>\n" +
+            "  root-input1\n" +
+            "  <child1 attr3='attr3-input' ns1:attr4='attr4-input'>\n" +
+            "    child1-input\n" +
+            "  </child1>\n" +
+            "  root-input2\n" +
+            "  <ns2:child2 xmlns:ns2='http://hortonworks.com/xml/ns2' attr5='attr5-input' ns1:attr6='attr6-input' ns2:attr7='attr7-input'>\n" +
+            "    child2-input\n" +
+            "  </ns2:child2>\n" +
+            "  root-input3\n" +
+            "</ns1:root>";
+    //System.out.println( inputXml );
+
+    StringReader inputReader = new StringReader( inputXml );
+    HtmlFilterReaderBase filterReader = new MapXmlFilterReader( inputReader, map );
+    String outputXml = new String( IOUtils.toCharArray( filterReader ) );
+    //System.out.println( outputXml );
+    //System.out.flush();
+
+    SimpleNamespaceContext ns = new SimpleNamespaceContext();
+    ns.bind( "n1", "http://hortonworks.com/xml/ns1" );
+    ns.bind( "n2", "http://hortonworks.com/xml/ns2" );
+
+    assertThat( the( outputXml ), hasXPath( "/n1:root", ns ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/@attr1", ns, equalTo( "attr1-output" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/@n1:attr2", ns, equalTo( "attr2-output" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/text()[1]", ns, equalTo( "root-output1" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/text()[2]", ns, equalTo( "root-output2" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/text()[3]", ns, equalTo( "root-output3" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/child1", ns ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/child1/@attr3", ns, equalTo( "attr3-output" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/child1/@n1:attr4", ns, equalTo( "attr4-output" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/child1/text()", ns, equalTo( "child1-output" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/n2:child2", ns ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/n2:child2/@attr5", ns, equalTo( "attr5-output" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/n2:child2/@n1:attr6", ns, equalTo( "attr6-output" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/n2:child2/@n2:attr7", ns, equalTo( "attr7-output" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/n1:root/n2:child2/text()", ns, equalTo( "child2-output" ) ) );
+  }
+
+  @Test
+  public void testSimpleJavaScriptText() throws IOException, ParserConfigurationException {
+    String inputXml = "<root><script type=\"text/javascript\">input-js-text</script></root>";
+    StringReader inputReader = new StringReader( inputXml );
+    HtmlFilterReaderBase filterReader = new NoopXmlFilterReader( inputReader );
+    String outputXml = new String( IOUtils.toCharArray( filterReader ) );
+    assertThat( the( outputXml ), hasXPath( "/root/script/text()", equalTo( "input-js-text" ) ) );
+  }
+
+  @Test
+  public void testMatchedJavaScriptText() throws IOException, ParserConfigurationException {
+    Map<String, Map<String, String>> rules = new HashMap<>();
+    Map<String, String> map = new HashMap<>();
+    map.put( "(https?://[^/':,]+:[\\d]+)?/cluster/app", "https://knoxhost:8443/cluster/app" );
+    rules.put( "test-rule", map );
+    String inputXml =
+        "<root>\n" +
+        "  <script type=\"text/javascript\">\n" +
+        "    var appsTableData=[\n" +
+        "      [\"<a href='/cluster/app/application_1436831599487_0008'>application_1436831599487_0008</a>\",\"hdfs\",\"Spark Pi\",\"SPARK\",\"<a href='http://testhost:8088/cluster/app/application_1436831599487_0008'>History</a>\"],\n" +
+        "      [\"<a href='/cluster/app/application_1436831599487_0006'>application_1436831599487_0006</a>\",\"hdfs\",\"Spark Pi\",\"SPARK\",\"<a href='http://testhost:8088/cluster/app/application_1436831599487_0006'>History</a>\"],\n" +
+        "      [\"<a href='/cluster/app/application_1436831599487_0007'>application_1436831599487_0007</a>\",\"hdfs\",\"Spark Pi\",\"SPARK\",\"<a href='http://testhost:8088/cluster/app/application_1436831599487_0007'>History</a>\"]\n" +
+        "    ]\n" +
+        "  </script>\n" +
+        "</root>\n";
+    StringReader inputReader = new StringReader( inputXml );
+    UrlRewriteFilterContentDescriptor config = new UrlRewriteFilterContentDescriptorImpl();
+    config.addApply( "(https?://[^/':,]+:[\\d]+)?/cluster/app", "test-rule" );
+    HtmlFilterReaderBase filterReader = new MatchRuleXmlFilterReader( inputReader, rules, config );
+    String outputXml = new String( IOUtils.toCharArray( filterReader ) );
+    String expectedOutput =
+        "<root>\n" +
+        "  <script type=\"text/javascript\">\n" +
+        "    var appsTableData=[\n" +
+        "      [\"<a href='https://knoxhost:8443/cluster/app/application_1436831599487_0008'>application_1436831599487_0008</a>\",\"hdfs\",\"Spark Pi\",\"SPARK\",\"<a href='https://knoxhost:8443/cluster/app/application_1436831599487_0008'>History</a>\"],\n" +
+        "      [\"<a href='https://knoxhost:8443/cluster/app/application_1436831599487_0006'>application_1436831599487_0006</a>\",\"hdfs\",\"Spark Pi\",\"SPARK\",\"<a href='https://knoxhost:8443/cluster/app/application_1436831599487_0006'>History</a>\"],\n" +
+        "      [\"<a href='https://knoxhost:8443/cluster/app/application_1436831599487_0007'>application_1436831599487_0007</a>\",\"hdfs\",\"Spark Pi\",\"SPARK\",\"<a href='https://knoxhost:8443/cluster/app/application_1436831599487_0007'>History</a>\"]\n" +
+        "    ]\n" +
+        "  </script>\n" +
+        "</root>\n";
+    assertThat( outputXml, is( expectedOutput ) );
+  }
+
+  @Test
+  public void testMRJobHistoryUIJavaScriptText() throws IOException, ParserConfigurationException {
+    Map<String, Map<String, String>> rules = new HashMap<>();
+    Map<String, String> map = new HashMap<>();
+    map.put( "https?://[^/':,]+:[\\d]+", "https://knoxhost:8443/gateway/nodemanagerui/node?host=knoxhost" );
+    rules.put( "test-rule", map );
+    String inputXml =
+        "<root>\n" +
+        "  <script type=\"text/javascript\">\n" +
+        "    var appsTableData=[\n" +
+        "      [\"<a href='http://testhost:8042'>/default-rack/node</a>\",\"<a href='http://testhost:8042'>testhost:8042</a>\"],\n" +
+        "    ]\n" +
+        "  </script>\n" +
+        "</root>\n";
+    StringReader inputReader = new StringReader( inputXml );
+    UrlRewriteFilterContentDescriptor config = new UrlRewriteFilterContentDescriptorImpl();
+    config.addApply("https?://[^/':,]+:[\\d]+", "test-rule");
+    HtmlFilterReaderBase filterReader = new MatchRuleXmlFilterReader( inputReader, rules, config );
+    String outputXml = new String( IOUtils.toCharArray( filterReader ) );
+    String expectedOutput =
+        "<root>\n" +
+        "  <script type=\"text/javascript\">\n" +
+        "    var appsTableData=[\n" +
+        "      [\"<a href='https://knoxhost:8443/gateway/nodemanagerui/node?host=knoxhost'>/default-rack/node</a>\",\"<a href='https://knoxhost:8443/gateway/nodemanagerui/node?host=knoxhost'>testhost:8042</a>\"],\n" +
+        "    ]\n" +
+        "  </script>\n" +
+        "</root>\n";
+    assertThat( outputXml, is( expectedOutput ) );
+  }
+
+  public static class XmlRewriteRulesDescriptorDigesterTest {
+
+    private static DigesterLoader loader = DigesterLoader.newLoader( new XmlRewriteRulesDigester() );
+    private static Digester digester = loader.newDigester( new ExtendedBaseRules() );
+
+    @Before
+    public void setupTest() {
+      digester.setValidating( false );
+    }
+
+    @Test
+    public void testRuleParsing() throws IOException, SAXException {
+      Reader reader = new StringReader( "<rules/>" );
+      UrlRewriteRulesDescriptor config = digester.parse( reader );
+      assertThat( config.getRules().isEmpty(), is( true ) );
+
+      reader = new StringReader( "<rules><rule></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().size(), is( 1 ) );
+      UrlRewriteRuleDescriptor rule = config.getRules().get( 0 );
+      assertThat( rule, notNullValue() );
+      assertThat( rule.name(), nullValue() );
+      assertThat( rule.pattern(), nullValue() );
+      assertThat( rule.directions(), nullValue() );
+      assertThat( rule.flow(), nullValue() );
+
+      reader = new StringReader( "<rules><rule name=\"test-name\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().size(), is( 1 ) );
+      rule = config.getRules().get( 0 );
+      assertThat( rule, notNullValue() );
+      assertThat( rule.name(), is( "test-name" ) );
+      assertThat( rule.pattern(), nullValue() );
+      assertThat( rule.directions(), nullValue() );
+      assertThat( rule.flow(), nullValue() );
+
+      reader = new StringReader( "<rules><rule pattern=\"test-pattern\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().size(), is( 1 ) );
+      rule = config.getRules().get( 0 );
+      assertThat( rule, notNullValue() );
+      assertThat( rule.name(), nullValue() );
+      assertThat( rule.pattern(), is( "test-pattern" ) );
+      assertThat( rule.directions(), nullValue() );
+      assertThat( rule.flow(), nullValue() );
+
+      reader = new StringReader( "<rules><rule dir=\"request\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().size(), is( 1 ) );
+      rule = config.getRules().get( 0 );
+      assertThat( rule, notNullValue() );
+      assertThat( rule.name(), nullValue() );
+      assertThat( rule.pattern(), nullValue() );
+      assertThat( rule.directions().size(), is( 1 ) );
+      assertThat( rule.directions(), Matchers.contains( UrlRewriter.Direction.IN ) );
+      assertThat( rule.flow(), nullValue() );
+
+      reader = new StringReader( "<rules><rule flow=\"all\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().size(), is( 1 ) );
+      rule = config.getRules().get( 0 );
+      assertThat( rule, notNullValue() );
+      assertThat( rule.name(), nullValue() );
+      assertThat( rule.pattern(), nullValue() );
+      assertThat( rule.directions(), nullValue() );
+      assertThat( rule.flow(), Matchers.is( UrlRewriteStepFlow.ALL ) );
+    }
+
+    @Test
+    public void testDirectionParsing() throws IOException, SAXException {
+      Reader reader;
+      UrlRewriteRulesDescriptor config;
+
+      reader = new StringReader( "<rules><rule dir=\"request\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"Request\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"in\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"req\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"Req\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"REQ\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"inbound\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"Inbound\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"INBOUND\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"in\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"In\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"IN\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"i\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+      reader = new StringReader( "<rules><rule dir=\"I\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+
+
+      reader = new StringReader( "<rules><rule dir=\"response\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"Response\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"out\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"res\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"Res\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"RES\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"outbound\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"Outbound\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"OUTBOUND\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"out\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"Out\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"OUT\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"o\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+      reader = new StringReader( "<rules><rule dir=\"O\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.OUT ) );
+
+
+      reader = new StringReader( "<rules><rule dir=\"request,response\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), hasItem( UrlRewriter.Direction.IN ) );
+      assertThat( config.getRules().get( 0 ).directions(), hasItem( UrlRewriter.Direction.OUT ) );
+    }
+
+    @Test
+    public void testFlowParsing() throws IOException, SAXException {
+      Reader reader;
+      UrlRewriteRulesDescriptor config;
+
+      reader = new StringReader( "<rules><rule dir=\"request\"></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config.getRules().get( 0 ).directions(), contains( UrlRewriter.Direction.IN ) );
+    }
+
+    @Test
+    public void testMatchParsing() throws IOException, SAXException {
+      Reader reader;
+      UrlRewriteRulesDescriptor config;
+      UrlRewriteRuleDescriptor rule;
+      UrlRewriteMatchDescriptorExt match;
+      List<? extends UrlRewriteStepDescriptor> steps;
+
+      reader = new StringReader( "<rules><rule><match></match></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config, notNullValue() );
+      assertThat( config.getRules(), notNullValue() );
+      assertThat( config.getRules().size(), is( 1 ) );
+      rule = config.getRules().get( 0 );
+      assertThat( rule.steps(), notNullValue() );
+      assertThat( rule.steps().size(), is( 1 ) );
+      match = (UrlRewriteMatchDescriptorExt)rule.steps().get( 0 );
+      assertThat( match, notNullValue() );
+      //assertThat( match.type(), nullValue() );
+      assertThat( match.operation(), nullValue() );
+      assertThat( match.pattern(), nullValue() );
+
+      reader = new StringReader( "<rules><rule><match type=\"test-type\" op=\"test-op\" pattern=\"test-pattern\"></match></rule></rules>" );
+      config = digester.parse( reader );
+      match = (UrlRewriteMatchDescriptorExt)config.getRules().get( 0 ).steps().get( 0 );
+      //assertThat( match.type(), is("test-type") );
+      assertThat( match.operation(), is( "test-op" ) );
+      assertThat( match.pattern(), is( "test-pattern" ) );
+
+      reader = new StringReader( "<rules><rule name=\"test\"><match><match pattern=\"test-pattern\"></match></match></rule></rules>" );
+      config = digester.parse( reader );
+      steps = ((UrlRewriteMatchDescriptor)config.getRule( "test" ).steps().get( 0 )).steps();
+      assertThat( steps, notNullValue() );
+      assertThat( steps.size(), is( 1 ) );
+      assertThat( steps.get( 0 ), notNullValue() );
+      match = (UrlRewriteMatchDescriptorExt)steps.get( 0 );
+      assertThat( match.pattern(), is( "test-pattern" ) );
+    }
+
+    @Test
+    public void testCheckParsing() throws IOException, SAXException {
+      Reader reader;
+      UrlRewriteRulesDescriptor config;
+      UrlRewriteRuleDescriptor rule;
+      List<UrlRewriteStepDescriptor> steps;
+      UrlRewriteCheckDescriptorExt step;
+
+      reader = new StringReader( "<rules><rule><check></check></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config, notNullValue() );
+      assertThat( config.getRules(), notNullValue() );
+      assertThat( config.getRules().size(), is( 1 ) );
+      rule = config.getRules().get( 0 );
+      assertThat( rule.steps(), notNullValue() );
+      assertThat( rule.steps().size(), is( 1 ) );
+      step = (UrlRewriteCheckDescriptorExt)rule.steps().get( 0 );
+      assertThat( step, notNullValue() );
+      //assertThat( step.type(), nullValue() );
+      assertThat( step.operation(), nullValue() );
+      assertThat( step.input(), nullValue() );
+      assertThat( step.value(), nullValue() );
+
+      reader = new StringReader( "<rules><rule><check type=\"test-type\" op=\"test-op\" input=\"test-input\" value=\"test-value\"></check></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config, notNullValue() );
+      assertThat( config.getRules(), notNullValue() );
+      assertThat( config.getRules().size(), is( 1 ) );
+      rule = config.getRules().get( 0 );
+      assertThat( rule.steps(), notNullValue() );
+      assertThat( rule.steps().size(), is( 1 ) );
+      step = (UrlRewriteCheckDescriptorExt)rule.steps().get( 0 );
+      assertThat( step, notNullValue() );
+      //assertThat( step.type(), is( "test-type" ) );
+      assertThat( step.operation(), is( "test-op" ) );
+      assertThat( step.input(), is( "test-input" ) );
+      assertThat( step.value(), is( "test-value" ) );
+    }
+
+    @Test
+    public void testActionParsing() throws IOException, SAXException {
+      Reader reader;
+      UrlRewriteRulesDescriptor config;
+      UrlRewriteRuleDescriptor rule;
+      UrlRewriteActionDescriptorBase step;
+
+      reader = new StringReader( "<rules><rule><action></action></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config, notNullValue() );
+      assertThat( config.getRules(), notNullValue() );
+      assertThat( config.getRules().size(), is( 1 ) );
+      rule = config.getRules().get( 0 );
+      assertThat( rule.steps(), notNullValue() );
+      assertThat( rule.steps().size(), is( 1 ) );
+      step = (UrlRewriteActionDescriptorBase)rule.steps().get( 0 );
+      assertThat( step, notNullValue() );
+      //assertThat( step.type(), nullValue() );
+      assertThat( step.parameter(), nullValue() );
+
+      reader = new StringReader( "<rules><rule><action type=\"test-type\" param=\"test-param\"></action></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config, notNullValue() );
+      assertThat( config.getRules(), notNullValue() );
+      assertThat( config.getRules().size(), is( 1 ) );
+      rule = config.getRules().get( 0 );
+      assertThat( rule.steps(), notNullValue() );
+      assertThat( rule.steps().size(), is( 1 ) );
+      step = (UrlRewriteActionDescriptorBase)rule.steps().get( 0 );
+      assertThat( step, notNullValue() );
+      //assertThat( step.type(), is( "test-type" ) );
+      assertThat( step.parameter(), is( "test-param" ) );
+    }
+
+    @Test
+    public void testControlParsing() throws IOException, SAXException {
+      Reader reader;
+      UrlRewriteRulesDescriptor config;
+      UrlRewriteRuleDescriptor rule;
+      List<UrlRewriteStepDescriptor> steps;
+
+      reader = new StringReader( "<rules><rule><control></control></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config, notNullValue() );
+      assertThat( config.getRules(), notNullValue() );
+      assertThat( config.getRules().size(), is( 1 ) );
+      rule = config.getRules().get( 0 );
+      assertThat( rule.steps(), notNullValue() );
+      assertThat( rule.steps().size(), is( 1 ) );
+      UrlRewriteControlDescriptor step = (UrlRewriteControlDescriptor)rule.steps().get( 0 );
+      assertThat( step, notNullValue() );
+      assertThat(  step.flow(), nullValue() );
+
+      reader = new StringReader( "<rules><rule><control flow=\"or\"></control></rule></rules>" );
+      config = digester.parse( reader );
+      assertThat( config, notNullValue() );
+      assertThat( config.getRules(), notNullValue() );
+      assertThat( config.getRules().size(), is( 1 ) );
+      rule = config.getRules().get( 0 );
+      assertThat( rule.steps(), notNullValue() );
+      assertThat( rule.steps().size(), is( 1 ) );
+      step = (UrlRewriteControlDescriptor)rule.steps().get( 0 );
+      assertThat( step, notNullValue() );
+      assertThat( step.flow(), is( UrlRewriteStepFlow.OR ) );
+    }
+  }
+  
+  @Test
+  public void testTagNameLetterCase() throws Exception {
+    String inputXml = "<Root/>";
+    StringReader inputReader = new StringReader( inputXml );
+    
+    HtmlFilterReaderBase filterReader = new NoopXmlFilterReader( inputReader );
+    String outputXml = new String( IOUtils.toCharArray( filterReader ) );
+    assertThat( the( outputXml ), hasXPath( "/Root" ) );
+  }
+  
+  @Test
+  public void testXmlWithHtmlTagNames() throws Exception {
+    String inputXml = "<root><br><table name=\"table1\"></table><table name=\"table2\"></table></br></root>";
+    StringReader inputReader = new StringReader( inputXml );
+    
+    HtmlFilterReaderBase filterReader = new NoopXmlFilterReader( inputReader );
+    String outputXml = new String( IOUtils.toCharArray( filterReader ) );
+    assertThat( the( outputXml ), hasXPath( "/root/br/table[1]/@name", equalTo( "table1" ) ) );
+    assertThat( the( outputXml ), hasXPath( "/root/br/table[2]/@name", equalTo( "table2" ) ) );
+  }
+
+  private class TestXmlFilterReader extends HtmlFilterReaderBase {
+
+    protected TestXmlFilterReader( Reader reader, UrlRewriteFilterContentDescriptor contentConfig ) throws IOException, ParserConfigurationException {
+      super( reader );
+    }
+
+    @Override
+    protected String filterAttribute( QName elementName, QName attributeName, String attributeValue, String ruleName ) {
+      return "attr:" + ruleName + "{" + attributeValue + "}";
+    }
+
+    @Override
+    protected String filterText( QName elementName, String text, String ruleName ) {
+      return "text:" + ruleName + "{" + text + "}";
+    }
+
+    @Override
+    public String filterValueString( String name, String value, String ruleName ) {
+      return value;
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionProcessorTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionProcessorTest.java b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionProcessorTest.java
new file mode 100644
index 0000000..e49581b
--- /dev/null
+++ b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionProcessorTest.java
@@ -0,0 +1,52 @@
+/**
+ * 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.knox.gateway.filter.rewrite.impl.html;
+
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
+import org.junit.Test;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.fail;
+
+public class HtmlImportFunctionProcessorTest {
+
+  @SuppressWarnings("rawtypes")
+  @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 HtmlImportFunctionProcessor ) {
+        return;
+      }
+    }
+    fail( "Failed to find " + HtmlImportFunctionProcessor.class.getName() + " via service loader." );
+  }
+
+  @Test
+  public void testName() throws Exception {
+    HtmlImportFunctionProcessor processor = new HtmlImportFunctionProcessor();
+    assertThat( processor.name(), is( "import" ) );
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixProcessorTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixProcessorTest.java b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixProcessorTest.java
new file mode 100644
index 0000000..8098719
--- /dev/null
+++ b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixProcessorTest.java
@@ -0,0 +1,57 @@
+/**
+ * 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.knox.gateway.filter.rewrite.impl.html;
+
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
+import org.junit.Test;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.fail;
+
+public class HtmlPrefixProcessorTest {
+
+  public HtmlPrefixProcessorTest() {
+    super();
+  }
+
+  @SuppressWarnings("rawtypes")
+  @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 HtmlPrefixProcessor) {
+        return;
+      }
+    }
+    fail( "Failed to find " + HtmlPrefixProcessor.class.getName() + " via service loader." );
+  }
+
+  @Test
+  public void testName() throws Exception {
+    HtmlPrefixProcessor processor = new HtmlPrefixProcessor();
+    assertThat( processor.name(), is( "prefix" ) );
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptFilterReaderTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptFilterReaderTest.java b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptFilterReaderTest.java
new file mode 100644
index 0000000..1cca640
--- /dev/null
+++ b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptFilterReaderTest.java
@@ -0,0 +1,118 @@
+/**
+ * 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.knox.gateway.filter.rewrite.impl.javascript;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteFilterContentDescriptorImpl;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class JavaScriptFilterReaderTest {
+  public static class NoopJsFilterReader extends JavaScriptFilterReader {
+    public NoopJsFilterReader( Reader reader, UrlRewriteFilterContentDescriptor config ) throws IOException {
+      super( reader, config );
+    }
+
+    @Override
+    public String filterValueString( String name, String value, String ruleName ) {
+      return value;
+    }
+  }
+
+  public static class MatchRuleJsFilterReader extends JavaScriptFilterReader {
+    private Map<String, Map<String,String>> rules;
+    public MatchRuleJsFilterReader( Reader reader, Map<String, Map<String,String>> rules, UrlRewriteFilterContentDescriptor config ) throws IOException {
+      super( reader, config );
+      this.rules = rules;
+    }
+
+    @Override
+    public String filterValueString( String name, String value, String ruleName ) {
+      Map<String, String> rule = rules.get( ruleName );
+      if ( rule == null ) {
+        return value;
+      }
+      for ( Map.Entry<String, String> entry : rule.entrySet() ) {
+        if ( Pattern.compile( entry.getKey() ).matcher( value ).matches() ) {
+          return entry.getValue();
+        }
+      }
+      return value;
+    }
+  }
+
+  @Test
+  public void testSimple() throws IOException {
+    String inputJs = "function load_page() {}\n";
+    StringReader inputReader = new StringReader( inputJs );
+    UrlRewriteFilterContentDescriptor config = new UrlRewriteFilterContentDescriptorImpl();
+    JavaScriptFilterReader filterReader = new NoopJsFilterReader( inputReader, config );
+    String outputJs = new String( IOUtils.toCharArray( filterReader ) );
+    assertThat( outputJs, is ( inputJs ) );
+  }
+
+  @Test
+  public void testSimpleMultipleLines() throws IOException {
+    String inputJs =
+        "var url = '/webhdfs/v1' + abs_path + '?op=GET_BLOCK_LOCATIONS';\n" +
+        "$.ajax({\"url\": url, \"crossDomain\": true}).done(function(data) {}).error(network_error_handler(url));\n";
+    StringReader inputReader = new StringReader( inputJs );
+    UrlRewriteFilterContentDescriptor config = new UrlRewriteFilterContentDescriptorImpl();
+    config.addApply( "/webhdfs/v1", "test-rule" );
+    JavaScriptFilterReader filterReader = new NoopJsFilterReader( inputReader, config );
+    String outputJs = new String( IOUtils.toCharArray( filterReader ) );
+    assertThat( outputJs, is ( inputJs ) );
+  }
+
+  @Test
+  public void testMatchedJsContent() throws IOException {
+    Map<String, Map<String, String>> rules = new HashMap<>();
+    Map<String, String> map = new HashMap<>();
+    map.put( "(https?://[^/':,]+:[\\d]+)?/cluster/app", "https://knoxhost:8443/cluster/app" );
+    map.put( "/webhdfs/v1", "https://knoxhost:8443/webhdfs/v1" );
+    rules.put( "test-rule", map );
+    String inputJs =
+        "var url = '/webhdfs/v1' + abs_path + '?op=GET_BLOCK_LOCATIONS';\n" +
+        "$.ajax({\"url\": url, \"crossDomain\": true}).done(function(data) {\n" +
+        "  var url = http://testhost:8088/cluster/app/application_1436831599487_0001;\n" +
+        "}).error(network_error_handler(url));\n";
+    StringReader inputReader = new StringReader( inputJs );
+    UrlRewriteFilterContentDescriptor config = new UrlRewriteFilterContentDescriptorImpl();
+    config.addApply( "(https?://[^/':,]+:[\\d]+)?/cluster/app", "test-rule" );
+    config.addApply( "/webhdfs/v1", "test-rule" );
+    JavaScriptFilterReader filterReader = new MatchRuleJsFilterReader( inputReader, rules, config );
+    String outputJs = new String( IOUtils.toCharArray( filterReader ) );
+    String expectedOutputJs =
+        "var url = 'https://knoxhost:8443/webhdfs/v1' + abs_path + '?op=GET_BLOCK_LOCATIONS';\n" +
+        "$.ajax({\"url\": url, \"crossDomain\": true}).done(function(data) {\n" +
+        "  var url = https://knoxhost:8443/cluster/app/application_1436831599487_0001;\n" +
+        "}).error(network_error_handler(url));\n";
+    assertThat( outputJs, is ( expectedOutputJs ) );
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonFilterReaderTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonFilterReaderTest.java b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonFilterReaderTest.java
new file mode 100644
index 0000000..1378fef
--- /dev/null
+++ b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonFilterReaderTest.java
@@ -0,0 +1,363 @@
+/**
+ * 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.knox.gateway.filter.rewrite.impl.json;
+
+import com.jayway.jsonassert.JsonAssert;
+import org.apache.commons.io.IOUtils;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterApplyDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterBufferDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterDetectDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRulesDescriptorFactory;
+import org.apache.hadoop.test.TestUtils;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.nio.charset.Charset;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.fail;
+
+public class JsonFilterReaderTest {
+
+  @Test
+  public void testValueNumberWithBuffering() throws Exception {
+    String input = "{ \"apps\" : {\"app\":[{\"id\":\"one\", \"progress\":100.0, \"startedTime\":1399975176760}]} }";
+
+    UrlRewriteRulesDescriptor rulesConfig = UrlRewriteRulesDescriptorFactory.create();
+    UrlRewriteFilterDescriptor filterConfig = rulesConfig.addFilter( "filter-1" );
+    UrlRewriteFilterContentDescriptor contentConfig = filterConfig.addContent( "text/json" );
+    UrlRewriteFilterBufferDescriptor bufferConfig = contentConfig.addBuffer( "$.apps.app[*]" );
+    UrlRewriteFilterApplyDescriptor applyConfig = bufferConfig.addApply( "$.id", "test-rule" );
+
+    JsonFilterReader filter = new JsonFilterReader( new StringReader( input ), contentConfig );
+    String output = IOUtils.toString( filter );
+    assertThat( output, containsString( "\"startedTime\":1399975176760}" ) );
+  }
+
+
+  @Test
+  public void testSimple() throws IOException {
+    String inputJson = "{ \"test-name\" : \"test-value\" }";
+    StringReader inputReader = new StringReader( inputJson );
+    JsonFilterReader filterReader = new TestJsonFilterReader( inputReader, null );
+    String outputJson = new String( IOUtils.toCharArray( filterReader ) );
+    //System.out.println( "JSON=" + outputJson );
+
+    JsonAssert.with( outputJson ).assertThat( "name<test-name>", is( "value:null<test-value>" ) );
+  }
+
+  @Test
+  public void testRootArray() throws Exception {
+    String inputJson = "[\"test-value-1\",\"test-value-2\",\"test-value-3\"]";
+    StringReader inputReader = new StringReader( inputJson );
+    JsonFilterReader filterReader = new TestJsonFilterReader( inputReader, null );
+    String outputJson = new String( IOUtils.toCharArray( filterReader ) );
+    //System.out.println( "JSON=" + outputJson );
+    JsonAssert.with( outputJson ).assertThat( "$.[0]", is( "value:null<test-value-1>" ) );
+    JsonAssert.with( outputJson ).assertThat( "$.[1]", is( "value:null<test-value-2>" ) );
+    JsonAssert.with( outputJson ).assertThat( "$.[2]", is( "value:null<test-value-3>" ) );
+
+    inputJson = "[777,42]";
+    inputReader = new StringReader( inputJson );
+    filterReader = new TestJsonFilterReader( inputReader, null );
+    outputJson = new String( IOUtils.toCharArray( filterReader ) );
+    //System.out.println( "JSON=" + outputJson );
+    JsonAssert.with( outputJson ).assertThat( "$.[0]", is( 777 ) );
+    JsonAssert.with( outputJson ).assertThat( "$.[1]", is( 42 ) );
+  }
+
+  @Test
+  public void testEmptyObject() throws IOException {
+    String inputJson = "{}";
+    StringReader inputReader = new StringReader( inputJson );
+    JsonFilterReader filterReader = new TestJsonFilterReader( inputReader, null );
+    String outputJson = new String( IOUtils.toCharArray( filterReader ) );
+
+    assertThat( outputJson, is( "{}" ) );
+  }
+
+  @Test
+  public void testEmptyArray() throws IOException {
+    String inputJson = "[]";
+    StringReader inputReader = new StringReader( inputJson );
+    JsonFilterReader filterReader = new TestJsonFilterReader( inputReader, null );
+    String outputJson = new String( IOUtils.toCharArray( filterReader ) );
+
+    assertThat( outputJson, is( "[]" ) );
+  }
+
+  @Test
+  public void testUnscopedStreaming() throws IOException {
+    InputStream stream = TestUtils.getResourceStream( this.getClass(), "simple-values.json" );
+    String input = IOUtils.toString( stream, Charset.forName( "UTF-8" ) );
+
+    //System.out.println( "INPUT=" + input );
+
+    UrlRewriteRulesDescriptor rulesConfig = UrlRewriteRulesDescriptorFactory.create();
+    UrlRewriteFilterDescriptor filterConfig = rulesConfig.addFilter( "filter=1" );
+    UrlRewriteFilterContentDescriptor contentConfig = filterConfig.addContent( "text/json" );
+    UrlRewriteFilterApplyDescriptor applyConfig = contentConfig.addApply( "$['test-str']", "test-rule" );
+
+    JsonFilterReader filter = new TestJsonFilterReader( new StringReader( input ), contentConfig );
+    String output = IOUtils.toString( filter );
+
+    //System.out.println( "OUTPUT=" + output );
+
+    JsonAssert.with( output ).assertThat( "name<test-str>", is( "value:null<text>" ) );
+  }
+
+  @Test
+  public void testNamesWithDots() throws IOException {
+    InputStream stream = TestUtils.getResourceStream( this.getClass(), "dotted-field-name.json" );
+    String input = IOUtils.toString( stream, Charset.forName( "UTF-8" ) );
+
+    //System.out.println( "INPUT=" + input );
+
+    UrlRewriteRulesDescriptor rulesConfig = UrlRewriteRulesDescriptorFactory.create();
+    UrlRewriteFilterDescriptor filterConfig = rulesConfig.addFilter( "test-filter" );
+    UrlRewriteFilterContentDescriptor contentConfig = filterConfig.addContent( "application/json" );
+    //NOTE: The field names are rewritten first so the values rules need to match the rewritten name.
+    contentConfig.addApply( "$.name<testField>", "test-rule" );
+    contentConfig.addApply( "$.name<test_field>", "test-rule" );
+    contentConfig.addApply( "$.name<test-field>", "test-rule" );
+    contentConfig.addApply( "$['name<test.field>']", "test-rule" );
+
+    JsonFilterReader filter = new TestJsonFilterReader( new StringReader( input ), contentConfig );
+    String output = IOUtils.toString( filter );
+
+    //System.out.println( "OUTPUT=" + output );
+
+    JsonAssert.with( output ).assertThat( "$['name<testField>']", is( "value:test-rule<testField value>" ) );
+    JsonAssert.with( output ).assertThat( "$['name<test_field>']", is( "value:test-rule<test_field value>" ) );
+    JsonAssert.with( output ).assertThat( "$['name<test-field>']", is( "value:test-rule<test-field value>" ) );
+    JsonAssert.with( output ).assertThat( "$['name<test.field>']", is( "value:test-rule<test.field value>" ) );
+  }
+
+//  @Test
+//  public void testJsonPathObject() throws IOException {
+//    InputStream stream = TestUtils.getResourceStream( this.getClass(), "complex.json" );
+//    String input = IOUtils.toString( stream, Charset.forName( "UTF-8" ) );
+//
+//    Object o;
+//
+//    o = JsonPath.read( "$", input, JsonNode.class );
+//    assertThat( o, instanceOf( ObjectNode.class ) );
+//    assertThat( o.toString(), startsWith( "{" ) );
+//    System.out.println( o.getClass() + "=" + o );
+//
+//    o = JsonPath.read( "$['test-str']", input, JsonNode.class );
+//    System.out.println( o.getClass() + "=" + o );
+//
+//    o = JsonPath.read( "$['test-obj-multi']", input, JsonNode.class );
+//    System.out.println( o.getClass() + "=" + o );
+//
+//    o = JsonPath.read( "$['val']", (JsonNode)o, JsonNode.class );
+//    System.out.println( o.getClass() + "=" + o );
+//
+//    JsonPath p = JsonPath.compile( "$['test-obj-multi']['val']" );
+//    o = JsonPath.read( "$['test-obj-multi']['val']", input, JsonNode.class );
+//    JsonNode pp = ((JsonNode)o).findParent("val");
+//    System.out.println( "$['test-obj-multi']['val']=" + o.getClass() + "=" + o );
+//
+//  }
+//
+//  @Test
+//  public void testJsonPathArray() throws IOException {
+//    InputStream stream = TestUtils.getResourceStream( this.getClass(), "array.json" );
+//    String input = IOUtils.toString( stream, Charset.forName( "UTF-8" ) );
+//
+//    Object o;
+//
+//    o = JsonPath.read( "$", input, JsonNode.class );
+//    System.out.println( o.getClass() + "=" + o );
+//
+//    o = JsonPath.read( "$[0]", input, JsonNode.class );
+//    System.out.println( o.getClass() + "=" + o );
+//
+//    o = JsonPath.read( "$[*]", input, JsonNode.class );
+//    System.out.println( "$[*]=" + o.getClass() + "=" + o );
+//
+//    o = JsonPath.read( "$['obj1-fld1']", (JsonNode)o, JsonNode.class );
+//    System.out.println( o.getClass() + "=" + o );
+//
+//    o = JsonPath.read( "$[0]['obj1-fld1']", input, JsonNode.class );
+//    System.out.println( o.getClass() + "=" + o );
+//
+//  }
+
+  @Test
+  public void testBuffered() throws IOException {
+    InputStream stream = TestUtils.getResourceStream( this.getClass(), "simple-values.json" );
+    String input = IOUtils.toString( stream, Charset.forName( "UTF-8" ) );
+
+    //System.out.println( "INPUT=" + input );
+
+    UrlRewriteRulesDescriptor rulesConfig = UrlRewriteRulesDescriptorFactory.create();
+    UrlRewriteFilterDescriptor filterConfig = rulesConfig.addFilter( "filter-1" );
+    UrlRewriteFilterContentDescriptor contentConfig = filterConfig.addContent( "text/json" );
+    UrlRewriteFilterBufferDescriptor bufferConfig = contentConfig.addBuffer( "$" );
+    UrlRewriteFilterApplyDescriptor applyConfig = bufferConfig.addApply( "$['name<test-str>']", "test-rule" );
+    //UrlRewriteRulesDescriptorFactory.store( rulesConfig, "xml", new PrintWriter( System.out ) );
+
+    JsonFilterReader filter = new TestJsonFilterReader( new StringReader( input ), contentConfig );
+    String output = IOUtils.toString( filter );
+
+    //System.out.println( "OUTPUT=" + output );
+
+    JsonAssert.with( output ).assertThat( "name<test-str>", is( "value:test-rule<text>" ) );
+  }
+
+  @Test
+  public void testBufferedDetectApply() throws IOException {
+    InputStream stream = TestUtils.getResourceStream( this.getClass(), "properties.json" );
+    String input = IOUtils.toString( stream, Charset.forName( "UTF-8" ) );
+
+    //System.out.println( "INPUT=" + input );
+
+    UrlRewriteRulesDescriptor rulesConfig = UrlRewriteRulesDescriptorFactory.create();
+    UrlRewriteFilterDescriptor filterConfig = rulesConfig.addFilter( "filter-1" );
+    UrlRewriteFilterContentDescriptor contentConfig = filterConfig.addContent( "text/json" );
+    UrlRewriteFilterBufferDescriptor bufferConfig = contentConfig.addBuffer( "$.name<properties>.*.name<property>" );
+    UrlRewriteFilterDetectDescriptor detectConfig = bufferConfig.addDetect( "$.name<property-name>", "test-name-2" );
+    UrlRewriteFilterApplyDescriptor applyConfig = detectConfig.addApply( "$.name<property-value>", "test-rule-2" );
+
+    //UrlRewriteRulesDescriptorFactory.store( rulesConfig, "xml", new PrintWriter( System.out ) );
+
+    JsonFilterReader filter = new TestJsonFilterReader( new StringReader( input ), contentConfig );
+    String output = IOUtils.toString( filter );
+
+    //System.out.println( "OUTPUT=" + output );
+
+    JsonAssert.with( output ).assertThat( "name<properties>[0].name<property>.name<property-name>", is( "test-name-1" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[0].name<property>.name<property-value>", is( "test-value-1" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[1].name<property>.name<property-name>", is( "test-name-2" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[1].name<property>.name<property-value>", is( "value:test-rule-2<test-value-2>" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[2].name<property>.name<property-name>", is( "test-name-3" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[2].name<property>.name<property-value>", is( "test-value-3" ) );
+  }
+
+  @Test
+  public void testBufferedApply() throws IOException {
+    InputStream stream = TestUtils.getResourceStream( this.getClass(), "properties.json" );
+    String input = IOUtils.toString( stream, Charset.forName( "UTF-8" ) );
+
+    //System.out.println( "INPUT=" + input );
+
+    UrlRewriteRulesDescriptor rulesConfig = UrlRewriteRulesDescriptorFactory.create();
+    UrlRewriteFilterDescriptor filterConfig = rulesConfig.addFilter( "filter-1" );
+    UrlRewriteFilterContentDescriptor contentConfig = filterConfig.addContent( "text/json" );
+    UrlRewriteFilterBufferDescriptor bufferConfig = contentConfig.addBuffer( "$.name<properties>.*.name<property>" );
+    UrlRewriteFilterApplyDescriptor applyConfig = bufferConfig.addApply( "$.name<property-value>", "test-rule" );
+
+    //UrlRewriteRulesDescriptorFactory.store( rulesConfig, "xml", new PrintWriter( System.out ) );
+
+    JsonFilterReader filter = new TestJsonFilterReader( new StringReader( input ), contentConfig );
+    String output = IOUtils.toString( filter );
+
+    //System.out.println( "OUTPUT=" + output );
+
+    JsonAssert.with( output ).assertThat( "name<properties>[0].name<property>.name<property-name>", is( "test-name-1" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[0].name<property>.name<property-value>", is( "value:test-rule<test-value-1>" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[1].name<property>.name<property-name>", is( "test-name-2" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[1].name<property>.name<property-value>", is( "value:test-rule<test-value-2>" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[2].name<property>.name<property-name>", is( "test-name-3" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[2].name<property>.name<property-value>", is( "value:test-rule<test-value-3>" ) );
+  }
+
+  @Test
+  public void testBufferedMultiApply() throws IOException {
+    InputStream stream = TestUtils.getResourceStream( this.getClass(), "properties.json" );
+    String input = IOUtils.toString( stream, Charset.forName( "UTF-8" ) );
+
+    //System.out.println( "INPUT=" + input );
+
+    UrlRewriteRulesDescriptor rulesConfig = UrlRewriteRulesDescriptorFactory.create();
+    UrlRewriteFilterDescriptor filterConfig = rulesConfig.addFilter( "filter-1" );
+    UrlRewriteFilterContentDescriptor contentConfig = filterConfig.addContent( "text/json" );
+    UrlRewriteFilterBufferDescriptor bufferConfig = contentConfig.addBuffer( "$.name<properties>" );
+    UrlRewriteFilterApplyDescriptor applyConfig = bufferConfig.addApply( "$.*.name<property>.name<property-value>", "test-rule" );
+
+    //UrlRewriteRulesDescriptorFactory.store( rulesConfig, "xml", new PrintWriter( System.out ) );
+
+    JsonFilterReader filter = new TestJsonFilterReader( new StringReader( input ), contentConfig );
+    String output = IOUtils.toString( filter );
+
+    //System.out.println( "OUTPUT=" + output );
+
+    JsonAssert.with( output ).assertThat( "name<properties>[0].name<property>.name<property-name>", is( "test-name-1" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[0].name<property>.name<property-value>", is( "value:test-rule<test-value-1>" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[1].name<property>.name<property-name>", is( "test-name-2" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[1].name<property>.name<property-value>", is( "value:test-rule<test-value-2>" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[2].name<property>.name<property-name>", is( "test-name-3" ) );
+    JsonAssert.with( output ).assertThat( "name<properties>[2].name<property>.name<property-value>", is( "value:test-rule<test-value-3>" ) );
+  }
+
+  @Test
+  public void testInvalidConfigShouldThrowException() throws Exception {
+    String input = "{\"test-name\":\"test-value\"}";
+
+    //System.out.println( "INPUT=" + input );
+
+    UrlRewriteRulesDescriptor rulesConfig = UrlRewriteRulesDescriptorFactory.create();
+    UrlRewriteFilterDescriptor filterConfig = rulesConfig.addFilter( "filter-1" );
+    UrlRewriteFilterContentDescriptor contentConfig = filterConfig.addContent( "*/json" );
+    contentConfig.addApply( "/root/@url", "test-rule" );
+
+    //UrlRewriteRulesDescriptorFactory.store( rulesConfig, "xml", new PrintWriter( System.out ) );
+
+    try {
+      JsonFilterReader filter = new TestJsonFilterReader( new StringReader( input ), contentConfig );
+      IOUtils.toString( filter );
+      fail( "Should have thrown an IllegalArgumentException." );
+    } catch ( IOException e ) {
+      fail( "Should have thrown an IllegalArgumentException." );
+    } catch ( IllegalArgumentException e ) {
+      assertThat( e.getMessage(), containsString( "/root/@url" ) );
+    }
+  }
+
+  @Test
+  public void testEscapeCharactersBugKnox616() throws Exception {
+    String input, output;
+    JsonFilterReader filter;
+
+    input = "{ \"test-name\" : \"\\\"\" }";
+    filter = new NoopJsonFilterReader( new StringReader( input ), null );
+    output = IOUtils.toString( filter );
+    assertThat( output, is( "{\"test-name\":\"\\\"\"}" ) );
+
+    input = "{\"test-name\":\"\\b\"}";
+    filter = new NoopJsonFilterReader( new StringReader( input ), null );
+    output = IOUtils.toString( filter );
+    assertThat( output, is( "{\"test-name\":\"\\b\"}" ) );
+  }
+
+//  private void dump( ObjectMapper mapper, JsonGenerator generator, JsonNode node ) throws IOException {
+//    mapper.writeTree( generator, node );
+//    System.out.println();
+//  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/NoopJsonFilterReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/NoopJsonFilterReader.java b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/NoopJsonFilterReader.java
new file mode 100644
index 0000000..bb5c6ca
--- /dev/null
+++ b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/NoopJsonFilterReader.java
@@ -0,0 +1,39 @@
+/**
+ * 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.knox.gateway.filter.rewrite.impl.json;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+
+import java.io.IOException;
+import java.io.Reader;
+
+public class NoopJsonFilterReader extends JsonFilterReader {
+
+  public NoopJsonFilterReader( Reader reader, UrlRewriteFilterContentDescriptor config ) throws IOException {
+    super( reader, config );
+  }
+
+  protected String filterFieldName( String name ) {
+    return name;
+  }
+
+  protected String filterValueString( String name, String value, String rule ) {
+    return value;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/TestJsonFilterReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/TestJsonFilterReader.java b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/TestJsonFilterReader.java
new file mode 100644
index 0000000..197c5ec
--- /dev/null
+++ b/gateway-provider-rewrite/src/test/java/org/apache/knox/gateway/filter/rewrite/impl/json/TestJsonFilterReader.java
@@ -0,0 +1,39 @@
+/**
+ * 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.knox.gateway.filter.rewrite.impl.json;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+
+import java.io.IOException;
+import java.io.Reader;
+
+public class TestJsonFilterReader extends JsonFilterReader {
+
+  public TestJsonFilterReader( Reader reader, UrlRewriteFilterContentDescriptor config ) throws IOException {
+    super( reader, config );
+  }
+
+  protected String filterFieldName( String name ) {
+    return "name<" + name + ">";
+  }
+
+  protected String filterValueString( String name, String value, String rule ) {
+    return "value:" + rule + "<" + value + ">";
+  }
+
+}