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:22 UTC

[24/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/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterGroupDescriptorBase.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterGroupDescriptorBase.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterGroupDescriptorBase.java
new file mode 100644
index 0000000..bc1dd91
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterGroupDescriptorBase.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
+ *
+ *     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.UrlRewriteFilterApplyDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterGroupDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterPathDescriptor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UrlRewriteFilterGroupDescriptorBase
+    extends UrlRewriteFilterSelectorDescriptorBase
+    implements UrlRewriteFilterGroupDescriptor {
+
+  private List<UrlRewriteFilterPathDescriptor> selectors = new ArrayList<UrlRewriteFilterPathDescriptor>();
+
+  @Override
+  public List<UrlRewriteFilterPathDescriptor> getSelectors() {
+    return selectors;
+  }
+
+  @Override
+  public void addSelector( UrlRewriteFilterPathDescriptor selector ) {
+    this.selectors.add( selector );
+  }
+
+  @Override
+  public UrlRewriteFilterApplyDescriptor addApply( String path, String rule ) {
+    UrlRewriteFilterApplyDescriptor apply = new UrlRewriteFilterApplyDescriptorImpl();
+    apply.path( path );
+    apply.rule( rule );
+    addSelector( apply );
+    return apply;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterReader.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterReader.java
new file mode 100644
index 0000000..b512a8c
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterReader.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;
+
+import java.util.regex.Pattern;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterPathDescriptor;
+
+
+public interface UrlRewriteFilterReader {
+
+  public String filterValueString( String name, String value, String rule );
+
+  public static class RegexCompiler implements UrlRewriteFilterPathDescriptor.Compiler<Pattern> {
+    @Override
+    public Pattern compile( String expression, Pattern compiled ) {
+      if( compiled != null ) {
+        return compiled;
+      } else {
+        return Pattern.compile( expression );
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterScopeDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterScopeDescriptorImpl.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterScopeDescriptorImpl.java
new file mode 100644
index 0000000..bc64870
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterScopeDescriptorImpl.java
@@ -0,0 +1,25 @@
+/**
+ * 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.UrlRewriteFilterScopeDescriptor;
+
+public class UrlRewriteFilterScopeDescriptorImpl
+    extends UrlRewriteFilterGroupDescriptorBase
+    implements UrlRewriteFilterScopeDescriptor {
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterSelectorDescriptorBase.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterSelectorDescriptorBase.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterSelectorDescriptorBase.java
new file mode 100644
index 0000000..3994128
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterSelectorDescriptorBase.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.knox.gateway.filter.rewrite.impl;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterPathDescriptor;
+
+public class UrlRewriteFilterSelectorDescriptorBase<T> implements UrlRewriteFilterPathDescriptor<T> {
+
+  private String path;
+  private Object compiledPath;
+
+  @Override
+  public String path() {
+    return path;
+  }
+
+  @Override
+  public T path( String path ) {
+    this.path = path;
+    return (T)this;
+  }
+
+  public void setPath( String path ) {
+    this.path = path;
+  }
+
+  public String getPath()  {
+    return path;
+  }
+
+  @Override
+  public <C> C compiledPath() {
+    return (C)compiledPath;
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public T compiledPath( Object compiledPath ) {
+    this.compiledPath = compiledPath;
+    return (T)this;
+  }
+
+  @Override
+  public <C> C compiledPath( Compiler<C> compiler ) {
+    compiledPath = compiler.compile( path, (C)compiledPath );
+    return (C)compiledPath;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFunctionProcessorFactory.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFunctionProcessorFactory.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFunctionProcessorFactory.java
new file mode 100644
index 0000000..def7a24
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFunctionProcessorFactory.java
@@ -0,0 +1,113 @@
+/**
+ * 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.UrlRewriteFunctionDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFunctionDescriptorFactory;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+public abstract class UrlRewriteFunctionProcessorFactory {
+
+  private static final Map<Class<? extends UrlRewriteFunctionDescriptor>,Map<String,Class<? extends UrlRewriteFunctionProcessor>>> MAP
+      = loadProcessors();
+
+  private UrlRewriteFunctionProcessorFactory() {
+  }
+
+  public static UrlRewriteFunctionProcessor create( String name, UrlRewriteFunctionDescriptor descriptor )
+      throws IllegalAccessException, InstantiationException {
+    UrlRewriteFunctionProcessor processor;
+    if( descriptor == null ) {
+      descriptor = UrlRewriteFunctionDescriptorFactory.create( name );
+    }
+    Map<String,Class<? extends UrlRewriteFunctionProcessor>> typeMap;
+    typeMap = MAP.get( descriptor.getClass() );
+    if( typeMap == null ) {
+      Class<? extends UrlRewriteFunctionDescriptor> descriptorInterface = getDescriptorInterface( descriptor );
+      typeMap = MAP.get( descriptorInterface );
+    }
+    if( typeMap == null ) {
+      throw new IllegalArgumentException( descriptor.getClass().getName() );
+    } else {
+      Class<? extends UrlRewriteFunctionProcessor> processorClass = typeMap.get( name );
+      if( processorClass == null ) {
+        throw new IllegalArgumentException( name );
+      } else {
+        processor = processorClass.newInstance();
+      }
+    }
+    return processor;
+  }
+
+  private static Map<Class<? extends UrlRewriteFunctionDescriptor>,Map<String,Class<? extends UrlRewriteFunctionProcessor>>> loadProcessors() {
+    Map<Class<? extends UrlRewriteFunctionDescriptor>,Map<String,Class<? extends UrlRewriteFunctionProcessor>>> descriptorMap
+        = new HashMap<>();
+    ServiceLoader<UrlRewriteFunctionProcessor> processors = ServiceLoader.load( UrlRewriteFunctionProcessor.class );
+    for( UrlRewriteFunctionProcessor processor : processors ) {
+      Class<? extends UrlRewriteFunctionDescriptor> descriptorInterface = getDescriptorInterface( processor );
+      Map<String,Class<? extends UrlRewriteFunctionProcessor>> typeMap = descriptorMap.get( descriptorInterface );
+      if( typeMap == null ) {
+        typeMap = new HashMap<>();
+        descriptorMap.put( descriptorInterface, typeMap );
+      }
+      String functionName = processor.name();
+      typeMap.put( functionName, processor.getClass() );
+    }
+    return descriptorMap;
+  }
+
+  private static Class<? extends UrlRewriteFunctionDescriptor> getDescriptorInterface(
+      UrlRewriteFunctionDescriptor descriptor ) {
+    Class<? extends UrlRewriteFunctionDescriptor> descriptorClass = null;
+    for( Type interfaceType : descriptor.getClass().getGenericInterfaces() ) {
+      Class genericClass = (Class)interfaceType;
+      if( UrlRewriteFunctionDescriptor.class.isAssignableFrom( genericClass ) ) {
+        descriptorClass = uncheckedDescriptorClassCast( genericClass );
+        break;
+      }
+    }
+    return descriptorClass;
+  }
+
+  private static Class<? extends UrlRewriteFunctionDescriptor> getDescriptorInterface(
+      UrlRewriteFunctionProcessor processor ) {
+    Class<? extends UrlRewriteFunctionDescriptor> descriptorClass = null;
+    Class<? extends UrlRewriteFunctionProcessor> processorClass = processor.getClass();
+    for( Type interfaceType : processorClass.getGenericInterfaces() ) {
+      if( UrlRewriteFunctionProcessor.class.isAssignableFrom(
+          (Class)((ParameterizedType)interfaceType).getRawType() ) ) {
+        ParameterizedType interfaceClass = (ParameterizedType)interfaceType;
+        descriptorClass = uncheckedDescriptorClassCast( interfaceClass.getActualTypeArguments()[ 0 ] );
+        break;
+      }
+    }
+    return descriptorClass;
+  }
+
+  @SuppressWarnings("unchecked")
+  private static Class<? extends UrlRewriteFunctionDescriptor> uncheckedDescriptorClassCast( Type type ) {
+    return (Class<? extends UrlRewriteFunctionDescriptor>)type;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequest.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequest.java
new file mode 100644
index 0000000..f91035c
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequest.java
@@ -0,0 +1,265 @@
+/**
+ * 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.AbstractGatewayFilter;
+import org.apache.knox.gateway.filter.GatewayRequestWrapper;
+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.UrlRewriteRulesDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteServletContextListener;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteServletFilter;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStreamFilterFactory;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.knox.gateway.filter.rewrite.i18n.UrlRewriteMessages;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.util.MimeTypes;
+import org.apache.knox.gateway.util.urltemplate.Parser;
+import org.apache.knox.gateway.util.urltemplate.Resolver;
+import org.apache.knox.gateway.util.urltemplate.Template;
+
+import javax.activation.MimeType;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.net.URLDecoder;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
+
+import static org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteUtil.pickFirstRuleWithEqualsIgnoreCasePathMatch;
+
+public class UrlRewriteRequest extends GatewayRequestWrapper implements Resolver {
+
+  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
+  private static final String[] EMPTY_STRING_ARRAY = new String[]{};
+
+  private FilterConfig config;
+  private UrlRewriter rewriter;
+  private String urlRuleName;
+  private String bodyFilterName;
+  private String headersFilterName;
+  private UrlRewriteFilterContentDescriptor headersFilterConfig;
+  private String cookiesFilterName;
+
+  /**
+   * Constructs a request object wrapping the given request.
+   *
+   * @throws IllegalArgumentException if the request is null
+   */
+  public UrlRewriteRequest( FilterConfig config, HttpServletRequest request ) throws IOException {
+    super( request );
+    this.config = config;
+    this.rewriter = UrlRewriteServletContextListener.getUrlRewriter( config.getServletContext() );
+    this.urlRuleName = config.getInitParameter( UrlRewriteServletFilter.REQUEST_URL_RULE_PARAM );
+    this.bodyFilterName = config.getInitParameter( UrlRewriteServletFilter.REQUEST_BODY_FILTER_PARAM );
+    this.headersFilterName = config.getInitParameter( UrlRewriteServletFilter.REQUEST_HEADERS_FILTER_PARAM );
+    this.headersFilterConfig = getRewriteFilterConfig( headersFilterName, UrlRewriteServletFilter.HEADERS_MIME_TYPE );
+    this.cookiesFilterName = config.getInitParameter( UrlRewriteServletFilter.REQUEST_COOKIES_FILTER_PARAM );
+  }
+
+  private Template getSourceUrl() {
+    Template urlTemplate;
+    //KNOX-439[
+    //StringBuffer urlString = super.getRequestURL();
+    StringBuffer urlString = new StringBuffer( 128 );
+    urlString.append( getScheme() );
+    urlString.append( "://" );
+    urlString.append( getServerName() );
+    urlString.append( ":" );
+    urlString.append( getServerPort() );
+    urlString.append( super.getRequestURI() );
+    //]
+    String queryString = super.getQueryString();
+    if( queryString != null ) {
+      urlString.append( '?' );
+      urlString.append( queryString );
+    }
+    try {
+      urlTemplate = Parser.parseLiteral( urlString.toString() );
+    } catch( URISyntaxException e ) {
+      LOG.failedToParseValueForUrlRewrite( urlString.toString() );
+      // Shouldn't be possible given that the URL is constructed from parts of an existing URL.
+      urlTemplate = null;
+    }
+    return urlTemplate;
+  }
+
+  // Note: Source url was added to the request attributes by the GatewayFilter doFilter method.
+  private Template getTargetUrl() {
+    boolean rewriteRequestUrl = true;
+    Template targetUrl;
+    if( rewriteRequestUrl ) {
+      targetUrl = (Template)getAttribute( AbstractGatewayFilter.TARGET_REQUEST_URL_ATTRIBUTE_NAME );
+      if( targetUrl == null ) {
+        Template sourceUrl = getSourceUrl();
+        targetUrl = rewriter.rewrite( this, sourceUrl, UrlRewriter.Direction.IN, urlRuleName );
+        setAttribute( AbstractGatewayFilter.TARGET_REQUEST_URL_ATTRIBUTE_NAME, targetUrl );
+      }
+    } else {
+      targetUrl = (Template)getAttribute( AbstractGatewayFilter.SOURCE_REQUEST_URL_ATTRIBUTE_NAME );
+    }
+    return targetUrl;
+  }
+
+  private String[] splitTargetUrl( Template url ) {
+    if( url == null ) {
+      return EMPTY_STRING_ARRAY;
+    } else {
+      String s = url.toString();
+      return s.split( "\\?" );
+    }
+  }
+
+  @Override
+  public StringBuffer getRequestURL() {
+    return new StringBuffer( getRequestURI() );
+  }
+
+  //TODO: I think this method is implemented wrong based on the HttpServletRequest.getRequestURI docs.
+  // It should not include the scheme or authority parts.
+  @Override
+  public String getRequestURI() {
+    String[] split = splitTargetUrl( getTargetUrl() );
+    if( split.length > 0 ) {
+      return split[0];
+    } else {
+      return "";
+    }
+  }
+
+  @Override
+  public String getQueryString() {
+    String[] split = splitTargetUrl( getTargetUrl() );
+    if( split.length > 1 ) {
+      try {
+        return URLDecoder.decode(split[1], "UTF-8");
+      } catch ( UnsupportedEncodingException e ) {
+        LOG.failedToDecodeQueryString(split[1], e);
+        return split[1];
+      }
+    } else {
+      return null;
+    }
+  }
+
+  private String rewriteValue( UrlRewriter rewriter, String value, String rule ) {
+    try {
+      Template input = Parser.parseLiteral( value );
+      Template output = rewriter.rewrite( this, input, UrlRewriter.Direction.IN, rule );
+      value = output.getPattern();
+    } catch( URISyntaxException e ) {
+      LOG.failedToParseValueForUrlRewrite( value );
+    }
+    return value;
+  }
+
+  @Override
+  public String getHeader( String name ) {
+    String value = super.getHeader( name );
+    if( value != null ) {
+      value = rewriteValue( rewriter, super.getHeader( name ), pickFirstRuleWithEqualsIgnoreCasePathMatch( headersFilterConfig, name ) );
+    }
+    return value;
+  }
+
+  @SuppressWarnings("unchecked")
+  public Enumeration getHeaders( String name ) {
+    return new EnumerationRewriter( rewriter, super.getHeaders( name ), pickFirstRuleWithEqualsIgnoreCasePathMatch( headersFilterConfig, name ) );
+  }
+
+  @Override
+  public List<String> resolve( String name ) {
+    return Arrays.asList( config.getInitParameter( name ) );
+  }
+
+  private class EnumerationRewriter implements Enumeration<String> {
+
+    private UrlRewriter rewriter;
+    private Enumeration<String> delegate;
+    private String rule;
+
+    private EnumerationRewriter( UrlRewriter rewriter, Enumeration<String> delegate, String rule ) {
+      this.rewriter = rewriter;
+      this.delegate = delegate;
+      this.rule = rule;
+    }
+
+    @Override
+    public boolean hasMoreElements() {
+      return delegate.hasMoreElements();
+    }
+
+    @Override
+    public String nextElement() {
+      return rewriteValue( rewriter, delegate.nextElement(), rule );
+    }
+  }
+
+  @Override
+  public ServletInputStream getInputStream() throws IOException {
+    ServletInputStream input = super.getInputStream();
+    if( getContentLength() != 0 ) {
+      MimeType mimeType = getMimeType();
+      UrlRewriteFilterContentDescriptor filterContentConfig = getRewriteFilterConfig( bodyFilterName, mimeType );
+      if (filterContentConfig != null) {
+        String asType = filterContentConfig.asType();
+        if ( asType != null && asType.trim().length() > 0 ) {
+          mimeType = MimeTypes.create(asType, getCharacterEncoding());
+        }
+      }
+      InputStream stream = UrlRewriteStreamFilterFactory.create( mimeType, null, input, rewriter, this, UrlRewriter.Direction.IN, filterContentConfig );
+      input = new UrlRewriteRequestStream( stream );
+    }
+    return input;
+  }
+
+  @Override
+  public BufferedReader getReader() throws IOException {
+    return new BufferedReader( new InputStreamReader( getInputStream(), getCharacterEncoding() ) );
+  }
+
+  @Override
+  public int getContentLength() {
+    // The rewrite might change the content length so return the default of -1 to indicate the length is unknown.
+    int contentLength = super.getContentLength();
+    if( contentLength > 0 ) {
+      contentLength = -1;
+    }
+    return contentLength;
+  }
+
+  private UrlRewriteFilterContentDescriptor getRewriteFilterConfig( String filterName, MimeType mimeType ) {
+    UrlRewriteFilterContentDescriptor filterContentConfig = null;
+    UrlRewriteRulesDescriptor rewriteConfig = rewriter.getConfig();
+    if( rewriteConfig != null ) {
+      UrlRewriteFilterDescriptor filterConfig = rewriteConfig.getFilter( filterName );
+      if( filterConfig != null ) {
+        filterContentConfig = filterConfig.getContent( mimeType );
+      }
+    }
+    return filterContentConfig;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequestStream.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequestStream.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequestStream.java
new file mode 100644
index 0000000..5995317
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequestStream.java
@@ -0,0 +1,40 @@
+/**
+ * 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 java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.knox.gateway.servlet.SynchronousServletInputStreamAdapter;
+
+//TODO: This needs to be coded much more efficiently!
+public class UrlRewriteRequestStream extends
+    SynchronousServletInputStreamAdapter {
+
+  private InputStream stream;
+
+  public UrlRewriteRequestStream( InputStream stream ) {
+    this.stream = stream;
+  }
+
+  @Override
+  public int read() throws IOException {
+    return stream.read();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponse.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponse.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponse.java
new file mode 100644
index 0000000..d451c26
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponse.java
@@ -0,0 +1,331 @@
+/**
+ * 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.GatewayResponseWrapper;
+import org.apache.knox.gateway.filter.ResponseStreamer;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteServletContextListener;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteServletFilter;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStreamFilterFactory;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.knox.gateway.filter.rewrite.i18n.UrlRewriteMessages;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.util.MimeTypes;
+import org.apache.knox.gateway.util.Urls;
+import org.apache.knox.gateway.util.urltemplate.Params;
+import org.apache.knox.gateway.util.urltemplate.Parser;
+import org.apache.knox.gateway.util.urltemplate.Template;
+import org.apache.commons.io.IOUtils;
+
+import javax.activation.MimeType;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+import java.util.zip.ZipException;
+
+import static org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteUtil.getRewriteFilterConfig;
+import static org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteUtil.pickFirstRuleWithEqualsIgnoreCasePathMatch;
+
+/**
+ *
+ */
+public class UrlRewriteResponse extends GatewayResponseWrapper implements Params,
+    ResponseStreamer {
+
+  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
+
+  // An 8K buffer better matches the underlying buffer sizes.
+  // Testing with 16K made no appreciable difference.
+  private static final int STREAM_BUFFER_SIZE = 8 * 1024;
+
+  private static final Set<String> IGNORE_HEADER_NAMES = new HashSet<>();
+  static {
+    IGNORE_HEADER_NAMES.add( "Content-Length" );
+  }
+
+  private static final String REQUEST_PARAM_PREFIX = "request.";
+  private static final String CLUSTER_PARAM_PREFIX = "cluster.";
+  private static final String GATEWAY_PARAM_PREFIX = "gateway.";
+  public  static final String INBOUND_QUERY_PARAM_PREFIX   = "query.param.";
+
+  private UrlRewriter rewriter;
+  private FilterConfig config;
+  private HttpServletRequest request;
+  private HttpServletResponse response;
+  private ServletOutputStream output;
+  private String bodyFilterName;
+  private String headersFilterName;
+  private UrlRewriteFilterContentDescriptor headersFilterConfig;
+  private String cookiesFilterName;
+  private String xForwardedHostname;
+  private String xForwardedPort;
+  private String xForwardedScheme;
+
+  public UrlRewriteResponse( FilterConfig config, HttpServletRequest request, HttpServletResponse response )
+      throws IOException {
+    super( response );
+    this.rewriter = UrlRewriteServletContextListener.getUrlRewriter( config.getServletContext() );
+    this.config = config;
+    this.request = request;
+    this.response = response;
+    this.output = null;
+    getXForwardedHeaders();
+    this.bodyFilterName = config.getInitParameter( UrlRewriteServletFilter.RESPONSE_BODY_FILTER_PARAM );
+    this.headersFilterName = config.getInitParameter( UrlRewriteServletFilter.RESPONSE_HEADERS_FILTER_PARAM );
+    this.headersFilterConfig = getRewriteFilterConfig( rewriter.getConfig(), headersFilterName, UrlRewriteServletFilter.HEADERS_MIME_TYPE );
+    this.cookiesFilterName = config.getInitParameter( UrlRewriteServletFilter.RESPONSE_COOKIES_FILTER_PARAM );
+  }
+
+  protected boolean ignoreHeader( String name ) {
+    return IGNORE_HEADER_NAMES.contains( name );
+  }
+
+  private String rewriteValue( String value, String rule ) {
+    try {
+      Template input = Parser.parseLiteral( value );
+      Template output = rewriter.rewrite( this, input, UrlRewriter.Direction.OUT, rule );
+      if( output != null ) {
+        value = output.toString();
+      }
+    } catch( URISyntaxException e ) {
+      LOG.failedToParseValueForUrlRewrite( value );
+    }
+    return value;
+  }
+
+  // Ignore the Content-Length from the dispatch respond since the respond body may be rewritten.
+  @Override
+  public void setHeader( String name, String value ) {
+    if( !ignoreHeader( name) ) {
+      value = rewriteValue( value, pickFirstRuleWithEqualsIgnoreCasePathMatch( headersFilterConfig, name ) );
+      super.setHeader( name, value );
+    }
+  }
+
+  // Ignore the Content-Length from the dispatch respond since the respond body may be rewritten.
+  @Override
+  public void addHeader( String name, String value ) {
+    if( !ignoreHeader( name ) ) {
+      String rule = pickFirstRuleWithEqualsIgnoreCasePathMatch( headersFilterConfig, name );
+      value = rewriteValue( value, rule );
+      super.addHeader( name, value );
+    }
+  }
+
+  @Override
+  public OutputStream getRawOutputStream() throws IOException {
+    return response.getOutputStream();
+  }
+
+  @Override
+  public void streamResponse( InputStream input, OutputStream output ) throws IOException {
+    InputStream inStream;
+    OutputStream outStream;
+    boolean isGzip = false;
+    BufferedInputStream inBuffer = new BufferedInputStream(input);
+    try {
+      // Use this way to check whether the input stream is gzip compressed, in case
+      // the content encoding header is unknown, as it could be unset in inbound response
+      inBuffer.mark(STREAM_BUFFER_SIZE);
+      inStream = new GZIPInputStream(inBuffer);
+      isGzip = true;
+    } catch (ZipException e) {
+      inBuffer.reset();
+      inStream = inBuffer;
+    } catch (IOException e) {
+      inBuffer.reset();
+      inStream = inBuffer;
+    }
+
+    MimeType mimeType = getMimeType();
+    UrlRewriteFilterContentDescriptor filterContentConfig =
+        getRewriteFilterConfig( rewriter.getConfig(), bodyFilterName, mimeType );
+    if (filterContentConfig != null) {
+      String asType = filterContentConfig.asType();
+      if ( asType != null && asType.trim().length() > 0 ) {
+        mimeType = MimeTypes.create(asType, getCharacterEncoding());
+      }
+    }
+    InputStream filteredInput = UrlRewriteStreamFilterFactory.create(
+        mimeType, null, inStream, rewriter, this, UrlRewriter.Direction.OUT, filterContentConfig );
+    outStream = (isGzip) ? new GZIPOutputStream(output) : output;
+    IOUtils.copyLarge( filteredInput, outStream, new byte[STREAM_BUFFER_SIZE] );
+    //KNOX-685: outStream.flush();
+    outStream.close();
+  }
+
+  //TODO: Need to buffer the output here and when it is closed, rewrite it and then write the result to the stream.
+  // This should only happen if the caller isn't using the streaming model.
+  @Override
+  public ServletOutputStream getOutputStream() throws IOException {
+    if( output == null ) {
+      output = new UrlRewriteResponseStream( this );
+    }
+    return output;
+  }
+
+  @Override
+  public Set<String> getNames() {
+    return Collections.emptySet();
+  }
+
+  @Override
+  @SuppressWarnings( "unchecked" )
+  public List<String> resolve( String name ) {
+    if( name.startsWith( REQUEST_PARAM_PREFIX ) ) {
+      return Arrays.asList( getRequestParam( name.substring( REQUEST_PARAM_PREFIX.length() ) ) );
+    } else if ( name.startsWith( GATEWAY_PARAM_PREFIX ) ) {
+      return Arrays.asList( getGatewayParam( name.substring( GATEWAY_PARAM_PREFIX.length() ) ) );
+    } else if ( name.startsWith( CLUSTER_PARAM_PREFIX ) ) {
+      return Arrays.asList( getClusterParam( name.substring( GATEWAY_PARAM_PREFIX.length() ) ) );
+    } else if ( name.startsWith( INBOUND_QUERY_PARAM_PREFIX ) ) {
+      return getInboundQueryParam(name.substring(INBOUND_QUERY_PARAM_PREFIX.length()));
+    } else {
+      return Arrays.asList( config.getInitParameter( name ) );
+    }
+  }
+
+  // KNOX-464: Doing this because Jetty only returns the string version of the IP address for request.getLocalName().
+  // Hopefully the local hostname will be cached so this will not be a significant performance hit.
+  // Previously this was an inline request.getServerName() but this ended up mixing the hostname from the Host header
+  // and the local port which was making load balancer configuration difficult if not impossible.
+  private String getRequestLocalHostName() {
+    String hostName = request.getLocalName();
+    try {
+      hostName = InetAddress.getByName( hostName ).getHostName();
+    } catch( UnknownHostException e ) {
+      // Ignore it and use the original hostname.
+    }
+    return hostName;
+  }
+
+  private String getGatewayParam( String name ) {
+    if( "url".equals( name ) ) {
+      if( xForwardedPort == null ) {
+        return xForwardedScheme + "://" + xForwardedHostname + request.getContextPath();
+      } else {
+        return xForwardedScheme + "://" + xForwardedHostname + ":" + xForwardedPort + request.getContextPath();
+      }
+    } else if( "scheme".equals( name ) ) {
+      return xForwardedScheme;
+    } else if( "host".equals( name ) ) {
+      return xForwardedHostname;
+    } else if( "port".equals( name ) ) {
+        return xForwardedPort;
+    } else if( "addr".equals( name ) || "address".equals( name ) ) {
+      if( xForwardedPort == null ) {
+        return xForwardedHostname;
+      } else {
+        return xForwardedHostname + ":" + xForwardedPort;
+      }
+    } else if( "path".equals( name ) ) {
+      return request.getContextPath();
+    } else {
+      return null;
+    }
+  }
+
+  private String getClusterParam( String name ) {
+    if( "name".equals( name ) ) {
+      return config.getServletContext().getServletContextName();
+    } else {
+      return null;
+    }
+  }
+
+  private List <String> getInboundQueryParam(String name ){
+     List <String> inboundHosts = null;
+     if( this.request!=null )
+       inboundHosts =
+         Arrays.asList( this.request.getParameterValues(name));
+     return inboundHosts;
+  }
+
+  private String getRequestParam( String name ) {
+    if( "host".equals( name ) ) {
+      return request.getServerName();
+    } else if ( "port".equals( name ) ) {
+      return Integer.toString( request.getLocalPort() );
+    } else if ( "scheme".equals( name ) ) {
+      return request.getScheme();
+    } else if ( "context-path".equals( name ) ) {
+      return Urls.stripLeadingSlash( request.getContextPath() );
+    } else {
+      config.getServletContext().getServletContextName();
+      return null;
+    }
+  }
+
+  @SuppressWarnings("deprecation")
+  public String encodeUrl( String url ) {
+    return this.encodeURL( url );
+  }
+
+  //TODO: Route these through the rewriter.
+  public String encodeURL( String url ) {
+    throw new UnsupportedOperationException();
+  }
+
+  @SuppressWarnings("deprecation")
+  public String encodeRedirectUrl( String url ) {
+    return this.encodeRedirectURL( url );
+  }
+
+  //TODO: Route these through the rewriter.
+  public String encodeRedirectURL( String url ) {
+    throw new UnsupportedOperationException();
+  }
+
+  private void getXForwardedHeaders() {
+    xForwardedHostname = request.getHeader( "X-Forwarded-Host" );
+    xForwardedPort = request.getHeader( "X-Forwarded-Port" );
+    xForwardedScheme = request.getHeader( "X-Forwarded-Proto" );
+    if ( xForwardedScheme == null ) {
+      xForwardedScheme = request.getScheme();
+    }
+    if ( xForwardedHostname != null ) {
+      int separator = xForwardedHostname.indexOf( ":" );
+      if ( separator > 0 ) {
+        //a specific port in the forwarded host wins
+        xForwardedPort = xForwardedHostname.substring(separator + 1, xForwardedHostname.length());
+        xForwardedHostname = xForwardedHostname.substring( 0, separator );
+      }
+    } else {
+      xForwardedHostname = getRequestLocalHostName();
+      xForwardedPort = Integer.toString( request.getLocalPort() );
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponseStream.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponseStream.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponseStream.java
new file mode 100644
index 0000000..300a8b4
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponseStream.java
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.filter.rewrite.impl;
+
+import org.apache.knox.gateway.filter.GatewayResponse;
+import org.apache.knox.gateway.servlet.SynchronousServletOutputStreamAdapter;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+//TODO: This needs to be coded much more efficiently!
+public class UrlRewriteResponseStream extends
+    SynchronousServletOutputStreamAdapter {
+
+  private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+  private GatewayResponse response;
+  private ByteArrayOutputStream buffer;
+
+  public UrlRewriteResponseStream( GatewayResponse response ) {
+    this.response = response;
+    this.buffer = new ByteArrayOutputStream( DEFAULT_BUFFER_SIZE );
+  }
+
+  @Override
+  public void write( int b ) throws IOException {
+    buffer.write( b );
+  }
+
+  @Override
+  public void close() throws IOException {
+    InputStream stream = new ByteArrayInputStream( buffer.toByteArray() );
+    response.streamResponse( stream ) ;
+    stream.close();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleDescriptorImpl.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleDescriptorImpl.java
new file mode 100644
index 0000000..d86c670
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleDescriptorImpl.java
@@ -0,0 +1,195 @@
+/**
+ * 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.UrlRewriteRuleDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStepDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteFlowDescriptorBase;
+import org.apache.knox.gateway.util.urltemplate.Parser;
+import org.apache.knox.gateway.util.urltemplate.Template;
+
+import java.net.URISyntaxException;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+public class UrlRewriteRuleDescriptorImpl extends UrlRewriteFlowDescriptorBase<UrlRewriteRuleDescriptor>
+    implements UrlRewriteRuleDescriptor {
+
+  private String name;
+  private String scope;
+  private String pattern;
+  private Template template;
+  private EnumSet<UrlRewriter.Direction> directions;
+
+  public UrlRewriteRuleDescriptorImpl() {
+    super( "rule" );
+  }
+
+  @Override
+  public String name() {
+    return this.name;
+  }
+
+  @Override
+  public UrlRewriteRuleDescriptor name( String name ) {
+    this.name = name;
+    return this;
+  }
+
+  public void setName( String name ) {
+    name( name );
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getScope() {
+    return scope;
+  }
+
+  public void setScope(String scope) {
+    scope( scope );
+  }
+
+  @Override
+  public String scope() {
+    return scope;
+  }
+
+  @Override
+  public UrlRewriteStepDescriptor scope( String scope ) {
+    this.scope = scope;
+    return this;
+  }
+
+  @Override
+  public EnumSet<UrlRewriter.Direction> directions() {
+    return directions;
+  }
+
+  @Override
+  public UrlRewriteRuleDescriptor directions( String directions ) {
+    this.directions = parseDirections( directions );
+    return this;
+  }
+
+  public void setDirections( String directions ) {
+    directions( directions );
+  }
+
+  public void setDirection( String directions ) {
+    directions( directions );
+  }
+
+  public void setDir( String directions ) {
+    directions( directions );
+  }
+
+  public String getDir() {
+    String s = null;
+    if( directions != null ) {
+      StringBuilder sb = new StringBuilder();
+      for( UrlRewriter.Direction direction: directions ) {
+        if( sb.length() > 0 ) {
+          sb.append( ',' );
+        }
+        sb.append( direction.toString() );
+      }
+      s = sb.toString();
+    }
+    return s;
+  }
+
+  @Override
+  public UrlRewriteRuleDescriptor directions( UrlRewriter.Direction... directions ) {
+    return this;
+  }
+
+  @Override
+  public String pattern() {
+    return pattern;
+  }
+
+  @Override
+  public UrlRewriteRuleDescriptor pattern( String pattern ) throws URISyntaxException {
+    this.pattern = pattern;
+    this.template = Parser.parseTemplate( pattern );
+    return this;
+  }
+
+  public void setPattern( String pattern ) throws URISyntaxException {
+    pattern( pattern );
+  }
+
+  public void setUrl( String pattern ) throws URISyntaxException {
+    pattern( pattern );
+  }
+
+  public String getPattern() {
+    return pattern();
+  }
+
+  @Override
+  public Template template() {
+    return template;
+  }
+
+  @Override
+  public UrlRewriteRuleDescriptor template( Template template ) {
+    this.template = template;
+    this.pattern = template.toString();
+    return this;
+  }
+
+  private static EnumSet<UrlRewriter.Direction> parseDirections( String directions ) {
+    EnumSet<UrlRewriter.Direction> set = EnumSet.noneOf( UrlRewriter.Direction.class );
+    StringTokenizer parser = new StringTokenizer( directions, " ,;:/|+" );
+    while( parser.hasMoreTokens() ) {
+      UrlRewriter.Direction direction = parseDirection( parser.nextToken() );
+      if( direction != null ) {
+        set.add( direction );
+      }
+    }
+    return set;
+  }
+
+  private static UrlRewriter.Direction parseDirection( String direction ) {
+    direction = direction.trim().toLowerCase();
+    return directionNameMap.get( direction );
+  }
+
+  private static Map<String,UrlRewriter.Direction> directionNameMap = new HashMap<>();
+  static {
+    directionNameMap.put( "inbound", UrlRewriter.Direction.IN );
+    directionNameMap.put( "in", UrlRewriter.Direction.IN );
+    directionNameMap.put( "i", UrlRewriter.Direction.IN );
+    directionNameMap.put( "request", UrlRewriter.Direction.IN );
+    directionNameMap.put( "req", UrlRewriter.Direction.IN );
+
+    directionNameMap.put( "outbound", UrlRewriter.Direction.OUT );
+    directionNameMap.put( "out", UrlRewriter.Direction.OUT );
+    directionNameMap.put( "o", UrlRewriter.Direction.OUT );
+    directionNameMap.put( "response", UrlRewriter.Direction.OUT );
+    directionNameMap.put( "res", UrlRewriter.Direction.OUT );
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorHolder.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorHolder.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorHolder.java
new file mode 100644
index 0000000..1c3fb11
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorHolder.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.knox.gateway.filter.rewrite.impl;
+
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteEnvironment;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRuleDescriptor;
+import org.apache.knox.gateway.filter.rewrite.ext.ScopedMatcher;
+
+import java.util.List;
+
+public class UrlRewriteRuleProcessorHolder extends UrlRewriteStepProcessorHolder {
+
+  private String ruleName;
+
+  private String scope;
+
+  public void initialize( UrlRewriteEnvironment environment, UrlRewriteRuleDescriptor descriptor ) throws Exception {
+    super.initialize( environment, descriptor );
+    ruleName = descriptor.name();
+    //if a scope is set in the rewrite file, use that
+    if (descriptor.scope() != null) {
+      scope = descriptor.scope();
+    } else {
+      //by convention the name of the rules start with ROLENAME/servicename/direction
+      //use the first part of the name to determine the scope, therefore setting the scope of a rule to
+      //be local to that service
+      int slashIndex = ruleName.indexOf('/');
+      if (slashIndex > 0) {
+        scope = ruleName.substring( 0, slashIndex );
+      }
+      //check config to see if the is an override configuration for a given service to have all its rules set to global
+      GatewayConfig gatewayConfig = environment.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
+      if (gatewayConfig != null) {
+        List<String> globalRulesServices = gatewayConfig.getGlobalRulesServices();
+        if ( globalRulesServices.contains(scope) ) {
+          scope = ScopedMatcher.GLOBAL_SCOPE;
+        }
+      }
+    }
+  }
+
+  public String getRuleName() {
+    return ruleName;
+  }
+
+  public String getScope() {
+    return scope;
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorImpl.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorImpl.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorImpl.java
new file mode 100644
index 0000000..e02ad23
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorImpl.java
@@ -0,0 +1,59 @@
+/**
+ * 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.UrlRewriteRuleDescriptor;
+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.ext.UrlRewriteMatchProcessorExt;
+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;
+
+public class UrlRewriteRuleProcessorImpl implements
+    UrlRewriteStepProcessor<UrlRewriteRuleDescriptor> {
+
+  private UrlRewriteMatchProcessorExt matchProcessor;
+
+  @Override
+  public String getType() {
+    return "rule";
+  }
+
+  @Override
+  public void initialize( UrlRewriteEnvironment environment, UrlRewriteRuleDescriptor descriptor ) throws Exception {
+    UrlRewriteMatchDescriptor matchDescriptor = new UrlRewriteMatchDescriptorExt();
+    matchDescriptor.operation( "matches" );
+    matchDescriptor.flow( descriptor.flow() );
+    matchDescriptor.template( descriptor.template() );
+    matchProcessor = new UrlRewriteMatchProcessorExt();
+    matchProcessor.initialize( environment, matchDescriptor );
+  }
+
+  @Override
+  public UrlRewriteStepStatus process( UrlRewriteContext context ) throws Exception {
+    return matchProcessor.process( context );
+  }
+
+  @Override
+  public void destroy() {
+    matchProcessor.destroy();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRulesDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRulesDescriptorImpl.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRulesDescriptorImpl.java
new file mode 100644
index 0000000..86dbc2a
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRulesDescriptorImpl.java
@@ -0,0 +1,143 @@
+/**
+ * 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.UrlRewriteFilterDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFunctionDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFunctionDescriptorFactory;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRuleDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class UrlRewriteRulesDescriptorImpl implements UrlRewriteRulesDescriptor {
+
+  private Map<String,UrlRewriteFunctionDescriptor> funcMap = new HashMap<>();
+  private List<UrlRewriteFunctionDescriptor> funcList = new ArrayList<UrlRewriteFunctionDescriptor>();
+  private List<UrlRewriteRuleDescriptor> ruleList = new ArrayList<UrlRewriteRuleDescriptor>();
+  private Map<String,UrlRewriteRuleDescriptor> ruleMap = new HashMap<>();
+  private List<UrlRewriteFilterDescriptor> filterList = new ArrayList<UrlRewriteFilterDescriptor>();
+  private Map<String,UrlRewriteFilterDescriptor> filterMap = new HashMap<>();
+
+  @Override
+  public void addRules( UrlRewriteRulesDescriptor rules ) {
+    for( UrlRewriteRuleDescriptor rule : rules.getRules() ) {
+      addRule( rule );
+    }
+    for( UrlRewriteFilterDescriptor filter : rules.getFilters() ) {
+      addFilter( filter  );
+    }
+  }
+
+  @Override
+  public UrlRewriteRuleDescriptor getRule( String name ) {
+    return ruleMap.get( name );
+  }
+
+  @Override
+  public List<UrlRewriteRuleDescriptor> getRules() {
+    return ruleList;
+  }
+
+  @Override
+  public UrlRewriteRuleDescriptor addRule( String name ) {
+    UrlRewriteRuleDescriptor rule = newRule();
+    rule.name( name );
+    addRule( rule );
+    return rule;
+  }
+
+  @Override
+  public UrlRewriteRuleDescriptor newRule() {
+    return new UrlRewriteRuleDescriptorImpl();
+  }
+
+  @Override
+  public void addRule( UrlRewriteRuleDescriptor rule ) {
+    ruleList.add( rule );
+    String name = rule.name();
+    if( name != null && name.length() > 0 ) {
+      ruleMap.put( rule.name(), rule );
+    }
+  }
+
+  @Override
+  public List<UrlRewriteFunctionDescriptor> getFunctions() {
+    return funcList;
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public <T extends UrlRewriteFunctionDescriptor<?>> T getFunction( String name ) {
+    T descriptor = (T)funcMap.get( name );
+    return (T)descriptor;
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public <T extends UrlRewriteFunctionDescriptor<?>> T addFunction( String name ) {
+    T descriptor = (T)newFunction( name );
+    addFunction( descriptor );
+    return (T)descriptor;
+  }
+
+  @SuppressWarnings("unchecked")
+  protected <T extends UrlRewriteFunctionDescriptor<?>> T newFunction( String name ) {
+    T descriptor = (T)UrlRewriteFunctionDescriptorFactory.create( name );
+    return (T)descriptor;
+  }
+
+  protected void addFunction( UrlRewriteFunctionDescriptor descriptor ) {
+    funcList.add( descriptor );
+    funcMap.put( descriptor.name(), descriptor );
+  }
+
+
+  @Override
+  public List<UrlRewriteFilterDescriptor> getFilters() {
+    return filterList;
+  }
+
+  @Override
+  public UrlRewriteFilterDescriptor getFilter( String name ) {
+    return filterMap.get( name );
+  }
+
+  @Override
+  public UrlRewriteFilterDescriptor newFilter() {
+    return new UrlRewriteFilterDescriptorImpl();
+  }
+
+  @Override
+  public UrlRewriteFilterDescriptor addFilter( String name ) {
+    UrlRewriteFilterDescriptor filter = newFilter();
+    filter.name( name );
+    addFilter( filter );
+    return filter;
+  }
+
+  @Override
+  public void addFilter( UrlRewriteFilterDescriptor descriptor ) {
+    filterList.add( descriptor );
+    filterMap.put( descriptor.name(), descriptor );
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorFactory.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorFactory.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorFactory.java
new file mode 100644
index 0000000..7d88f8b
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorFactory.java
@@ -0,0 +1,106 @@
+/**
+ * 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.UrlRewriteStepDescriptor;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStepProcessor;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+public abstract class UrlRewriteStepProcessorFactory {
+
+  private static final Map<Class<? extends UrlRewriteStepDescriptor>,Map<String,Class<? extends UrlRewriteStepProcessor>>> MAP
+      = loadStepProcessors();
+
+  private UrlRewriteStepProcessorFactory() {
+  }
+
+  public static UrlRewriteStepProcessor create( UrlRewriteStepDescriptor descriptor ) throws IllegalAccessException, InstantiationException {
+    UrlRewriteStepProcessor processor;
+    Map<String,Class<? extends UrlRewriteStepProcessor>> typeMap;
+    typeMap = MAP.get( descriptor.getClass() );
+    if( typeMap == null ) {
+      Class<? extends UrlRewriteStepDescriptor> descriptorInterface = getDescriptorInterface( descriptor );
+      typeMap = MAP.get( descriptorInterface );
+    }
+    if( typeMap == null ) {
+      throw new IllegalArgumentException( descriptor.getClass().getName() );
+    } else {
+      String type = descriptor.type();
+      Class<? extends UrlRewriteStepProcessor> processorClass = typeMap.get( type );
+      if( processorClass == null ) {
+        throw new IllegalArgumentException( type );
+      } else {
+        processor = processorClass.newInstance();
+      }
+    }
+    return processor;
+  }
+
+  private static Map<Class<? extends UrlRewriteStepDescriptor>,Map<String,Class<? extends UrlRewriteStepProcessor>>> loadStepProcessors() {
+    Map<Class<? extends UrlRewriteStepDescriptor>,Map<String,Class<? extends UrlRewriteStepProcessor>>> descriptorMap
+        = new HashMap<>();
+    ServiceLoader<UrlRewriteStepProcessor> processors = ServiceLoader.load( UrlRewriteStepProcessor.class );
+    for( UrlRewriteStepProcessor processor : processors ) {
+      Class<? extends UrlRewriteStepDescriptor> descriptorInterface = getDescriptorInterface( processor );
+      Map<String,Class<? extends UrlRewriteStepProcessor>> typeMap = descriptorMap.get( descriptorInterface );
+      if( typeMap == null ) {
+        typeMap = new HashMap<>();
+        descriptorMap.put( descriptorInterface, typeMap );
+      }
+      String processorType = processor.getType();
+      typeMap.put( processorType, processor.getClass() );
+    }
+    return descriptorMap;
+  }
+
+  private static Class<? extends UrlRewriteStepDescriptor> getDescriptorInterface( UrlRewriteStepDescriptor descriptor ) {
+    Class<? extends UrlRewriteStepDescriptor> descriptorClass = null;
+    for( Type interfaceType : descriptor.getClass().getGenericInterfaces() ) {
+      Class genericClass = (Class)interfaceType;
+      if( UrlRewriteStepDescriptor.class.isAssignableFrom( genericClass ) ) {
+        descriptorClass = uncheckedStepDescriptorClassCast( genericClass );
+        break;
+      }
+    }
+    return descriptorClass;
+  }
+
+  private static Class<? extends UrlRewriteStepDescriptor> getDescriptorInterface( UrlRewriteStepProcessor processor ) {
+    Class<? extends UrlRewriteStepDescriptor> descriptorClass = null;
+    Class<? extends UrlRewriteStepProcessor> processorClass = processor.getClass();
+    for( Type interfaceType : processorClass.getGenericInterfaces() ) {
+      if( UrlRewriteStepProcessor.class.isAssignableFrom( (Class)((ParameterizedType)interfaceType).getRawType() ) ) {
+        ParameterizedType interfaceClass = (ParameterizedType)interfaceType;
+        descriptorClass = uncheckedStepDescriptorClassCast( interfaceClass.getActualTypeArguments()[ 0 ] );
+        break;
+      }
+    }
+    return descriptorClass;
+  }
+
+  @SuppressWarnings("unchecked")
+  private static Class<? extends UrlRewriteStepDescriptor> uncheckedStepDescriptorClassCast( Type type ) {
+    return (Class<? extends UrlRewriteStepDescriptor>)type;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorHolder.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorHolder.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorHolder.java
new file mode 100644
index 0000000..1d4811c
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorHolder.java
@@ -0,0 +1,233 @@
+/**
+ * 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.i18n.UrlRewriteMessages;
+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.apache.knox.gateway.i18n.messages.MessagesFactory;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class UrlRewriteStepProcessorHolder implements UrlRewriteStepProcessor {
+
+  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
+
+  private boolean isCondition;
+  private UrlRewriteStepDescriptor descriptor;
+  private UrlRewriteStepProcessor processor;
+  private List<UrlRewriteStepProcessorHolder> childProcessors;
+
+  @Override
+  public String getType() {
+    return "system";
+  }
+
+  public boolean isCondition() {
+    return isCondition;
+  }
+
+  public boolean isAction() {
+    return !isCondition;
+  }
+
+  @Override
+  @SuppressWarnings( "unchecked" )
+  public void initialize( UrlRewriteEnvironment environment, UrlRewriteStepDescriptor descriptor ) throws Exception {
+    UrlRewriteStepProcessor processor = UrlRewriteStepProcessorFactory.create( descriptor );
+    processor.initialize( environment, descriptor );
+    initialize( environment, descriptor, processor );
+  }
+
+  // For unit testing.
+  @SuppressWarnings("unchecked")
+  void initialize( UrlRewriteEnvironment environment, UrlRewriteStepDescriptor descriptor, UrlRewriteStepProcessor processor ) throws Exception {
+    this.descriptor = descriptor;
+    this.processor = processor;
+    this.isCondition = descriptor instanceof UrlRewriteFlowDescriptor;
+    this.childProcessors = new ArrayList<UrlRewriteStepProcessorHolder>();
+    if( isCondition ) {
+      UrlRewriteFlowDescriptor flowDescriptor = (UrlRewriteFlowDescriptor)descriptor;
+      List<UrlRewriteStepDescriptor> stepList = flowDescriptor.steps();
+      if( stepList != null && !stepList.isEmpty() ) {
+        Iterator<UrlRewriteStepDescriptor> stepIterator = stepList.iterator();
+        while( stepIterator.hasNext() ) {
+          UrlRewriteStepDescriptor stepDescriptor = stepIterator.next();
+          UrlRewriteStepProcessorHolder stepProcessor = new UrlRewriteStepProcessorHolder();
+          stepProcessor.initialize( environment, stepDescriptor );
+          childProcessors.add( stepProcessor );
+        }
+      }
+    }
+  }
+
+  // For unit testing.
+  UrlRewriteStepDescriptor getDescriptor() {
+    return descriptor;
+  }
+
+  // For unit testing.
+  UrlRewriteStepProcessor getProcessor() {
+    return processor;
+  }
+
+  @Override
+  public UrlRewriteStepStatus process( UrlRewriteContext context ) throws Exception {
+    UrlRewriteStepStatus status = UrlRewriteStepStatus.SUCCESS;
+    // If initialization failed then fail processing
+    if( processor != null ) {
+      status = processor.process( context );
+      if( UrlRewriteStepStatus.SUCCESS == status &&
+          descriptor instanceof UrlRewriteFlowDescriptor &&
+          !childProcessors.isEmpty() ) {
+        UrlRewriteFlowDescriptor flowDescriptor = (UrlRewriteFlowDescriptor)descriptor;
+        UrlRewriteStepFlow flow = flowDescriptor.flow();
+        if( flow == null ) {
+          flow = UrlRewriteStepFlow.AND;
+        }
+        switch( flow ) {
+          case ALL:
+            return processAllFlow( context );
+          case AND:
+            return processAndFlow( context );
+          case OR:
+            return processOrFlow( context );
+        }
+      }
+    }
+    return status;
+  }
+
+  private UrlRewriteStepStatus processAllFlow( UrlRewriteContext context ) throws Exception {
+    UrlRewriteStepProcessorState state = new UrlRewriteStepProcessorState( childProcessors.iterator() );
+    UrlRewriteStepStatus stepStatus = UrlRewriteStepStatus.SUCCESS;
+    UrlRewriteStepProcessorHolder step;
+    while( state.hasNext() ) {
+      while( state.hasNextCondition() ) {
+        step = state.nextCondition( stepStatus );
+        stepStatus = step.process( context );
+        if( stepStatus == UrlRewriteStepStatus.FINISHED ) {
+          return stepStatus;
+        }
+      }
+      stepStatus = processActions( context, state );
+      if( stepStatus == UrlRewriteStepStatus.FINISHED ) {
+        return stepStatus;
+      }
+    }
+    return UrlRewriteStepStatus.SUCCESS;
+  }
+
+  // All conditions proceeding a set of one or more actions must succeed for the actions to be executed.
+  private UrlRewriteStepStatus processAndFlow( UrlRewriteContext context ) throws Exception {
+    UrlRewriteStepProcessorState state = new UrlRewriteStepProcessorState( childProcessors.iterator() );
+    UrlRewriteStepStatus stepStatus = UrlRewriteStepStatus.SUCCESS;
+    UrlRewriteStepProcessorHolder step;
+    while( state.hasNext() ) {
+      while( state.hasNextCondition() ) {
+        step = state.nextCondition( stepStatus );
+        stepStatus = step.process( context );
+        if( !( stepStatus == UrlRewriteStepStatus.SUCCESS ) ) {
+          return stepStatus;
+        }
+      }
+      stepStatus = processActions( context, state );
+      if( !( stepStatus == UrlRewriteStepStatus.SUCCESS ) ) {
+        return stepStatus;
+      }
+    }
+    return UrlRewriteStepStatus.SUCCESS;
+  }
+
+  // At least one condition proceeding a set of one or more actions must succedd for the actions to be executed.
+  private UrlRewriteStepStatus processOrFlow( UrlRewriteContext context ) throws Exception {
+    UrlRewriteStepProcessorState state = new UrlRewriteStepProcessorState( childProcessors.iterator() );
+    UrlRewriteStepStatus status = UrlRewriteStepStatus.SUCCESS;
+    UrlRewriteStepProcessorHolder step;
+    while( state.hasNext() ) {
+      UrlRewriteStepStatus flowStatus = UrlRewriteStepStatus.FAILURE;
+      while( state.hasNextCondition() ) {
+        step = state.nextCondition( status );
+        if( flowStatus == UrlRewriteStepStatus.FAILURE ) {
+          status = step.process( context );
+          switch( status ) {
+            case SUCCESS:
+              flowStatus = UrlRewriteStepStatus.SUCCESS;
+              continue;
+            case FINISHED:
+              return status;
+          }
+        }
+      }
+      status = processActions( context, state );
+      if( status != UrlRewriteStepStatus.SUCCESS ) {
+        return status;
+      }
+    }
+    return UrlRewriteStepStatus.SUCCESS;
+  }
+
+  private UrlRewriteStepStatus processActions( UrlRewriteContext context, UrlRewriteStepProcessorState state )
+      throws Exception {
+    UrlRewriteStepStatus flowStatus = UrlRewriteStepStatus.SUCCESS;
+    while( state.hasNextAction() ) {
+      if( flowStatus == UrlRewriteStepStatus.SUCCESS ) {
+        UrlRewriteStepStatus stepStatus = UrlRewriteStepStatus.SUCCESS;
+        UrlRewriteStepProcessorHolder step = state.nextAction( stepStatus );
+        stepStatus = step.process( context );
+        switch( stepStatus ) {
+          case FAILURE:
+            flowStatus = UrlRewriteStepStatus.FAILURE;
+            continue;
+          case FINISHED:
+            return stepStatus;
+        }
+      }
+    }
+    return flowStatus;
+  }
+
+  @Override
+  public void destroy() throws Exception {
+    destroy( processor );
+    if( descriptor instanceof UrlRewriteFlowDescriptor ) {
+      for( UrlRewriteStepProcessorHolder childProcessor : childProcessors ) {
+        destroy( childProcessor );
+      }
+    }
+  }
+
+  public void destroy( UrlRewriteStepProcessor processor ) {
+    if( processor != null ) {
+      try {
+        processor.destroy();
+      } catch( Exception e ) {
+        // Maybe it makes sense to throw exception
+        LOG.failedToDestroyRewriteStepProcessor( e );
+      }
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorState.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorState.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorState.java
new file mode 100644
index 0000000..dead19d
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorState.java
@@ -0,0 +1,88 @@
+/**
+ * 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.spi.UrlRewriteStepStatus;
+
+import java.util.Iterator;
+
+class UrlRewriteStepProcessorState {
+
+  private UrlRewriteStepStatus status;
+  private UrlRewriteStepProcessorHolder next;
+  private Iterator<UrlRewriteStepProcessorHolder> steps;
+
+  UrlRewriteStepProcessorState( Iterator<UrlRewriteStepProcessorHolder> steps ) {
+    this.status = UrlRewriteStepStatus.SUCCESS;
+    this.next = null;
+    this.steps = steps;
+  }
+
+  private UrlRewriteStepProcessorHolder peek() {
+    if( next == null && steps.hasNext() ) {
+      next = steps.next();
+      return next;
+    } else if ( next != null ) {
+      return next;
+    } else {
+      return null;
+    }
+  }
+
+  public boolean hasNextCondition() {
+    UrlRewriteStepProcessorHolder curr = peek();
+    return curr != null && curr.isCondition();
+  }
+
+  public boolean hasNextAction() {
+    UrlRewriteStepProcessorHolder curr = peek();
+    return curr != null && curr.isAction();
+  }
+
+  private UrlRewriteStepProcessorHolder take( UrlRewriteStepStatus lastStatus ) {
+    UrlRewriteStepProcessorHolder step = peek();
+    status = lastStatus;
+    next = null;
+    return step;
+  }
+
+  public UrlRewriteStepProcessorHolder nextCondition( UrlRewriteStepStatus lastStatus ){
+    if( hasNextCondition() ) {
+      return take( lastStatus );
+    } else {
+      return null;
+    }
+  }
+
+  public UrlRewriteStepProcessorHolder nextAction( UrlRewriteStepStatus lastStatus ){
+    if( hasNextAction() ) {
+      return take( lastStatus );
+    } else {
+      return null;
+    }
+  }
+
+  public UrlRewriteStepStatus status(){
+    return status;
+  }
+
+  public boolean hasNext() {
+      return next != null || steps.hasNext();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteUtil.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteUtil.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteUtil.java
new file mode 100644
index 0000000..4e68d27
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteUtil.java
@@ -0,0 +1,92 @@
+/**
+ * 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 java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterApplyDescriptor;
+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.UrlRewriteFilterPathDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor;
+
+import javax.activation.MimeType;
+
+public class UrlRewriteUtil {
+
+  public static String pickFirstRuleWithEqualsIgnoreCasePathMatch( UrlRewriteFilterContentDescriptor config, String name ) {
+    String rule = "*";
+    if( config != null && !config.getSelectors().isEmpty() && name != null ) {
+      rule = "";
+      for( UrlRewriteFilterPathDescriptor selector : config.getSelectors() ) {
+        if( name.equalsIgnoreCase( selector.path() ) ) {
+          if( selector instanceof UrlRewriteFilterApplyDescriptor) {
+            rule = ((UrlRewriteFilterApplyDescriptor)selector).rule();
+          }
+          break;
+        }
+      }
+    }
+    return rule;
+  }
+
+  public static UrlRewriteFilterContentDescriptor getRewriteFilterConfig(
+      UrlRewriteRulesDescriptor config, String filterName, MimeType mimeType ) {
+    UrlRewriteFilterContentDescriptor filterContentConfig = null;
+    if( config != null ) {
+      UrlRewriteFilterDescriptor filterConfig = config.getFilter( filterName );
+      if( filterConfig != null ) {
+        filterContentConfig = filterConfig.getContent( mimeType );
+      }
+    }
+    return filterContentConfig;
+  }
+
+  public static String filterJavaScript( String inputValue, UrlRewriteFilterContentDescriptor config,
+      UrlRewriteFilterReader filterReader, UrlRewriteFilterPathDescriptor.Compiler<Pattern> regexCompiler ) {
+    StringBuffer tbuff = new StringBuffer();
+    StringBuffer sbuff = new StringBuffer();
+    sbuff.append( inputValue );
+    if( config != null && !config.getSelectors().isEmpty() ) {
+      for( UrlRewriteFilterPathDescriptor selector : config.getSelectors() ) {
+        if ( selector instanceof UrlRewriteFilterApplyDescriptor ) {
+          UrlRewriteFilterApplyDescriptor apply = (UrlRewriteFilterApplyDescriptor)selector;
+          Matcher matcher = apply.compiledPath( regexCompiler ).matcher( sbuff );
+          int index = 0;
+          while ( matcher.find() ) {
+            int start = matcher.start();
+            int end = matcher.end();
+            if ( start != -1 && end != -1 ) {
+              tbuff.append( sbuff, index, start );
+              String value = matcher.group();
+              value = filterReader.filterValueString( null, value, apply.rule() );
+              tbuff.append(value);
+              index = end;
+            }
+          }
+          tbuff.append( sbuff, index, sbuff.length() );
+          sbuff.setLength( 0 );
+          sbuff.append( tbuff );
+          tbuff.setLength( 0 );
+        }
+      }
+    }
+    return sbuff.toString();
+  }
+}