You are viewing a plain text version of this content. The canonical link for it is here.
Posted to portalapps-dev@portals.apache.org by wo...@apache.org on 2009/09/17 12:22:07 UTC

svn commit: r816126 [1/2] - in /portals/applications/webcontent/trunk: webcontent-jar/ webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/portlet/ webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ webco...

Author: woonsan
Date: Thu Sep 17 10:22:06 2009
New Revision: 816126

URL: http://svn.apache.org/viewvc?rev=816126&view=rev
Log:
APA-16: Adding out-of-box reverse proxy service component.
Also, some improvements as follows:
  - Makes it fully configurable on http client, connection manager, proxy path url mappings and rewriter configurations.
  - Enhances performance by following the performance guidelines of httpclient.
  - Uses Portlet 2.0 head contribution feature to add auto-resizing javascript just once.
  - Adds out-of-box reverse proxy servlet with example configurations for www.apache.org and portals.apache.org
  - Improves redirection url location handling.

Added:
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapper.java   (with props)
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java   (with props)
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyService.java   (with props)
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java   (with props)
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java   (with props)
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java   (with props)
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java   (with props)
    portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy-urlmappings.properties   (with props)
Modified:
    portals/applications/webcontent/trunk/webcontent-jar/pom.xml
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/portlet/IFrameGenericPortlet.java
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/rewriter/MappingRewriterController.java
    portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/rewriter/RewriterController.java
    portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/portlet.xml
    portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/web.xml
    portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/javascript/iframe_autoresize.js

Modified: portals/applications/webcontent/trunk/webcontent-jar/pom.xml
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/pom.xml?rev=816126&r1=816125&r2=816126&view=diff
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-jar/pom.xml (original)
+++ portals/applications/webcontent/trunk/webcontent-jar/pom.xml Thu Sep 17 10:22:06 2009
@@ -38,7 +38,10 @@
     <nekohtml.version>0.9.5</nekohtml.version>
     <castor.version>1.1.1-xml</castor.version>
     <commons-lang.version>2.4</commons-lang.version>
+    <commons-io.version>1.4</commons-io.version>
     <commons-httpclient.version>3.0.1</commons-httpclient.version>
+    <commons-fileupload.version>1.2</commons-fileupload.version>
+    <commons-beanutils.version>1.7.0</commons-beanutils.version>
   </properties>
 
   <!-- Dependencies -->
@@ -103,6 +106,11 @@
       <version>${commons-lang.version}</version>
     </dependency>
     <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>${commons-io.version}</version>
+    </dependency>
+    <dependency>
       <groupId>commons-httpclient</groupId>
       <artifactId>commons-httpclient</artifactId>
       <version>${commons-httpclient.version}</version>
@@ -118,6 +126,16 @@
       </exclusions>
     </dependency>
     <dependency>
+      <groupId>commons-fileupload</groupId>
+      <artifactId>commons-fileupload</artifactId>
+      <version>${commons-fileupload.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-beanutils</groupId>
+      <artifactId>commons-beanutils</artifactId>
+      <version>${commons-beanutils.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-log4j12</artifactId>
       <version>${slf4j.version}</version>

Modified: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/portlet/IFrameGenericPortlet.java
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/portlet/IFrameGenericPortlet.java?rev=816126&r1=816125&r2=816126&view=diff
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/portlet/IFrameGenericPortlet.java (original)
+++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/portlet/IFrameGenericPortlet.java Thu Sep 17 10:22:06 2009
@@ -22,6 +22,7 @@
 
 import javax.portlet.ActionRequest;
 import javax.portlet.ActionResponse;
+import javax.portlet.MimeResponse;
 import javax.portlet.PortletConfig;
 import javax.portlet.PortletException;
 import javax.portlet.PortletPreferences;
@@ -32,6 +33,7 @@
 import org.apache.commons.lang.BooleanUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.portals.bridges.velocity.GenericVelocityPortlet;
+import org.w3c.dom.Element;
 
 /**
  * IFrameGenericPortlet
@@ -42,10 +44,13 @@
 public class IFrameGenericPortlet extends GenericVelocityPortlet
 {
 
-    private Map attributes = new HashMap();
+    public static final String IFRAME_AUTORESIZE_SCRIPT_ID = "org.apache.portals.applications.webcontent.portlet.iframe.autoresize";
 
-    private Map maxAttributes = new HashMap();
+    private Map<String, String> attributes = new HashMap<String, String>();
 
+    private Map<String, String> maxAttributes = new HashMap<String, String>();
+
+    @Override
     public void init(PortletConfig config) throws PortletException
     {
         super.init(config);
@@ -83,12 +88,12 @@
         return this.getMappedAttributePreference(prefs, "MAX-" + attribute, maxAttributes);
     }
 
-    private String getMappedAttributePreference(PortletPreferences prefs, String attribute, Map map)
+    private String getMappedAttributePreference(PortletPreferences prefs, String attribute, Map<String, String> map)
     {
-        return prefs.getValue(attribute, (String) map.get(attribute));
+        return prefs.getValue(attribute, map.get(attribute));
     }
 
-    private void appendAttribute(PortletPreferences prefs, StringBuffer content, String attribute, Map map)
+    private void appendAttribute(PortletPreferences prefs, StringBuffer content, String attribute, Map<String, String> map)
     {
         String value;
 
@@ -110,7 +115,31 @@
     {
         appendAttribute(prefs, content, attribute, maxAttributes);
     }
-
+    
+    @Override
+    protected void doHeaders(RenderRequest request, RenderResponse response) 
+    {
+        PortletPreferences prefs = request.getPreferences();
+        String autoResize = getAttributePreference(prefs, "AUTORESIZE");
+        String autoResizeScript = getAttributePreference(prefs, "AUTORESIZESCRIPT");
+        
+        if (BooleanUtils.toBoolean(autoResize))
+        {
+            if (StringUtils.isBlank(autoResizeScript))
+            {
+                autoResizeScript = request.getContextPath() + "/javascript/iframe_autoresize.js";
+            }
+            
+            Element headElem = response.createElement("script");
+            headElem.setAttribute("id", IFRAME_AUTORESIZE_SCRIPT_ID);
+            headElem.setAttribute("language", "JavaScript");
+            headElem.setAttribute("type", "text/javascript");
+            headElem.setAttribute("src", autoResizeScript);
+            response.addProperty(MimeResponse.MARKUP_HEAD_ELEMENT, headElem);
+        }
+    }
+    
+    @Override
     public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException
     {
         String viewPage = (String)request.getAttribute(PARAM_VIEW_PAGE);
@@ -124,6 +153,7 @@
         }
     }
 
+    @Override
     public void doEdit(RenderRequest request, RenderResponse response) throws PortletException, IOException
     {
         response.setContentType("text/html");
@@ -179,19 +209,6 @@
         // end fix JS2-349
         content.append("</TD></TR></TBODY></TABLE>");
 
-        String autoResize = getAttributePreference(prefs, "AUTORESIZE");
-        String autoResizeScript = getAttributePreference(prefs, "AUTORESIZESCRIPT");
-        
-        if (StringUtils.isBlank(autoResizeScript))
-        {
-            autoResizeScript = request.getContextPath() + "/javascript/iframe_autoresize.js";
-        }
-        
-        if (BooleanUtils.toBoolean(autoResize))
-        {
-            content.append("<SCRIPT LANGUAGE=\"JavaScript\" SRC=\"").append(autoResizeScript).append("\"></SCRIPT>\n");
-        }
-        
         // set required content type and write HTML IFRAME content
         response.setContentType("text/html");
         response.getWriter().print(content.toString());
@@ -208,13 +225,20 @@
         String proxyRemoteURL = getAttributePreference(prefs, "PROXYREMOTEURL");
         String proxyLocalPath = getAttributePreference(prefs, "PROXYLOCALPATH");
 
-        if (source == null) 
+        if (source == null)
         {
             source = "";
         }
         else if (StringUtils.isNotEmpty(proxyRemoteURL) && StringUtils.isNotEmpty(proxyLocalPath) && source.startsWith(proxyRemoteURL))
         {
-            source = proxyLocalPath + source.substring(proxyRemoteURL.length());
+            if (proxyLocalPath.startsWith("/"))
+            {
+                source = proxyLocalPath + source.substring(proxyRemoteURL.length());
+            }
+            else
+            {
+                source = request.getContextPath() + "/" + proxyLocalPath + source.substring(proxyRemoteURL.length());
+            }
         }
         
         return source;
@@ -223,6 +247,7 @@
     /**
      * Save the prefs
      */
+    @Override
     public void processAction(ActionRequest request, ActionResponse actionResponse) throws PortletException,
             IOException
     {

Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapper.java
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapper.java?rev=816126&view=auto
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapper.java (added)
+++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapper.java Thu Sep 17 10:22:06 2009
@@ -0,0 +1,55 @@
+/*
+ * 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.portals.applications.webcontent.proxy;
+
+/**
+ * proxy path mapper interface for http reverse proxy service.
+ * 
+ * @version $Id$
+ */
+public interface HttpReverseProxyPathMapper
+{
+    
+    /**
+     * Returns the local base path to be mapped to remote base url.
+     * @return
+     */
+    public String getLocalBasePath();
+    
+    /**
+     * Returns the remote base url mapped by the local path.
+     * @return
+     */
+    public String getRemoteBaseURL();
+    
+    /**
+     * Generates a remote url mapped by the path.
+     * <P>It returns a translated remote URL or null if the translation fails.</P>
+     * @param localPath
+     * @return
+     */
+    public String getRemoteURL(String localPath);
+    
+    /**
+     * Generates a local url mapped by the remote url.
+     * <P>It returns a translated local path or null if the translation fails.</P>
+     * @param remoteURL
+     * @return
+     */
+    public String getLocalPath(String remoteURL);
+    
+}
\ No newline at end of file

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapper.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapper.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java?rev=816126&view=auto
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java (added)
+++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java Thu Sep 17 10:22:06 2009
@@ -0,0 +1,56 @@
+/*
+ * 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.portals.applications.webcontent.proxy;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.portals.applications.webcontent.rewriter.Rewriter;
+import org.apache.portals.applications.webcontent.rewriter.RewriterController;
+
+/**
+ * proxy path mapper provider interface for http reverse proxy service.
+ * 
+ * @version $Id$
+ */
+public interface HttpReverseProxyPathMapperProvider
+{
+    
+    /**
+     * Finds a reverse proxy path mapper based on the request
+     * @return
+     */
+    public HttpReverseProxyPathMapper findMapper(HttpServletRequest request);
+    
+    /**
+     * Finds a reverse proxy path mapper based on the remoteURL
+     * @return
+     */
+    public HttpReverseProxyPathMapper findMapperByRemoteURL(String remoteURL);
+    
+    /**
+     * Finds a reverse proxy rewriter controller based on the request
+     * @return
+     */
+    public RewriterController findRewriterController(HttpServletRequest request);
+    
+    /**
+     * Finds a reverse proxy rewriter based on the request
+     * @return
+     */
+    public Rewriter findRewriter(HttpServletRequest request);
+    
+}
\ No newline at end of file

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyService.java
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyService.java?rev=816126&view=auto
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyService.java (added)
+++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyService.java Thu Sep 17 10:22:06 2009
@@ -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.portals.applications.webcontent.proxy;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * HTTP Reverse Proxy Service interface
+ * 
+ * @version $Id$
+ */
+public interface HttpReverseProxyService
+{
+    
+    public void initialize();
+    
+    public void destroy();
+    
+    public void invoke(HttpServletRequest request, HttpServletResponse response) throws IOException;
+    
+}

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyService.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyService.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyService.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java?rev=816126&view=auto
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java (added)
+++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java Thu Sep 17 10:22:06 2009
@@ -0,0 +1,67 @@
+/*
+ * 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.portals.applications.webcontent.proxy.impl;
+
+import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapper;
+
+/**
+ * Default implementation of <CODE>HttpProxyPathMapper</CODE> interface.
+ * 
+ * @version $Id$
+ */
+public class DefaultHttpProxyPathMapperImpl implements HttpReverseProxyPathMapper
+{
+    private String localBasePath;
+    private String remoteBaseURL;
+    
+    public DefaultHttpProxyPathMapperImpl(String localBasePath, String remoteBaseURL)
+    {
+        this.localBasePath = localBasePath;
+        this.remoteBaseURL = remoteBaseURL;
+    }
+    
+    public String getLocalBasePath()
+    {
+        return localBasePath;
+    }
+
+    public String getRemoteBaseURL()
+    {
+        return remoteBaseURL;
+    }
+
+    public String getRemoteURL(String localPath)
+    {
+        if (localPath.startsWith(localBasePath))
+        {
+            return remoteBaseURL + localPath.substring(localBasePath.length());
+        }
+        
+        return null;
+    }
+    
+    public String getLocalPath(String remoteURL)
+    {
+        if (remoteURL.startsWith(remoteBaseURL))
+        {
+            return localBasePath + remoteURL.substring(remoteBaseURL.length());
+        }
+        
+        return null;
+    }
+    
+}

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java?rev=816126&view=auto
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java (added)
+++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java Thu Sep 17 10:22:06 2009
@@ -0,0 +1,122 @@
+/*
+ * 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.portals.applications.webcontent.proxy.impl;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.collections.KeyValue;
+import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapper;
+import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapperProvider;
+import org.apache.portals.applications.webcontent.rewriter.Rewriter;
+import org.apache.portals.applications.webcontent.rewriter.RewriterController;
+
+/**
+ * Default <CODE>HttpReverseProxyPathMapperProvider</CODE> implementation
+ * 
+ * @version $Id$
+ */
+public class DefaultHttpReverseProxyPathMapperProviderImpl implements HttpReverseProxyPathMapperProvider
+{
+    
+    /**
+     * Proxy path mappers
+     */
+    private List<HttpReverseProxyPathMapper> proxyPathMappers;
+    
+    /**
+     * rewriter controller mappers
+     */
+    private List<KeyValue> rewriterControllerPairs;
+    
+    /**
+     * rewriters
+     */
+    private List<KeyValue> rewriterPairs;
+    
+    public DefaultHttpReverseProxyPathMapperProviderImpl(List<HttpReverseProxyPathMapper> proxyPathMappers, 
+                                                         List<KeyValue> rewriterControllerPairs,
+                                                         List<KeyValue> rewriterPairs)
+    {
+        this.proxyPathMappers = proxyPathMappers;
+        this.rewriterControllerPairs = rewriterControllerPairs;
+        this.rewriterPairs = rewriterPairs;
+    }
+    
+    public HttpReverseProxyPathMapper findMapper(HttpServletRequest request)
+    {
+        String pathInfo = request.getPathInfo();
+        
+        for (HttpReverseProxyPathMapper proxyPathMapper : proxyPathMappers)
+        {
+            if (pathInfo.startsWith(proxyPathMapper.getLocalBasePath()))
+            {
+                return proxyPathMapper;
+            }
+        }
+        
+        return null;
+    }
+    
+    public HttpReverseProxyPathMapper findMapperByRemoteURL(String remoteURL)
+    {
+        if (remoteURL != null)
+        {
+            for (HttpReverseProxyPathMapper proxyPathMapper : proxyPathMappers)
+            {
+                if (remoteURL.startsWith(proxyPathMapper.getRemoteBaseURL()))
+                {
+                    return proxyPathMapper;
+                }
+            }
+        }
+        
+        return null;
+    }
+
+    public RewriterController findRewriterController(HttpServletRequest request)
+    {
+        String pathInfo = request.getPathInfo();
+        
+        for (KeyValue pair : rewriterControllerPairs)
+        {
+            if (pathInfo.startsWith((String) pair.getKey()))
+            {
+                return (RewriterController) pair.getValue();
+            }
+        }
+        
+        return null;
+    }
+    
+    public Rewriter findRewriter(HttpServletRequest request)
+    {
+        String pathInfo = request.getPathInfo();
+        
+        for (KeyValue pair : rewriterPairs)
+        {
+            if (pathInfo.startsWith((String) pair.getKey()))
+            {
+                return (Rewriter) pair.getValue();
+            }
+        }
+        
+        return null;
+    }
+    
+}

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java?rev=816126&view=auto
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java (added)
+++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java Thu Sep 17 10:22:06 2009
@@ -0,0 +1,297 @@
+/*
+ * 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.portals.applications.webcontent.proxy.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.collections.KeyValue;
+import org.apache.commons.collections.keyvalue.DefaultKeyValue;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.BooleanUtils;
+import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapper;
+import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapperProvider;
+import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyService;
+import org.apache.portals.applications.webcontent.rewriter.MappingRewriterController;
+import org.apache.portals.applications.webcontent.rewriter.Rewriter;
+import org.apache.portals.applications.webcontent.rewriter.RewriterController;
+import org.apache.portals.applications.webcontent.rewriter.WebContentRewriter;
+import org.apache.portals.applications.webcontent.rewriter.html.neko.NekoParserAdaptor;
+import org.apache.portals.applications.webcontent.rewriter.xml.SaxParserAdaptor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Default reverse proxy servlet implementation as an example.
+ * 
+ * @version $Id$
+ */
+public class DefaultHttpReverseProxyServlet extends HttpServlet
+{
+    private static final long serialVersionUID = 1L;
+    
+    private static Logger log = LoggerFactory.getLogger(DefaultHttpReverseProxyServlet.class);
+    
+    protected HttpReverseProxyPathMapperProvider proxyPathMapperProvider;
+    protected HttpReverseProxyService proxyService;
+
+    @Override
+    public void init(ServletConfig config) throws ServletException
+    {
+        List<HttpReverseProxyPathMapper> proxyPathMappers = new ArrayList<HttpReverseProxyPathMapper>();
+        List<KeyValue> rewriterControllerPairs = new ArrayList<KeyValue>();
+        List<KeyValue> rewriterPairs = new ArrayList<KeyValue>();
+
+        String urlMappings = config.getInitParameter("reverseproxy.urlmappings");
+        
+        if (urlMappings != null)
+        {
+            Pattern reverseProxyPathURLPattern = Pattern.compile("^ReverseProxyPathURL\\s+(\\S+)\\s+(\\S+)$", Pattern.CASE_INSENSITIVE);
+            Pattern reverseProxyRewriterPattern = Pattern.compile("^ReverseProxyRewriter\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)$", Pattern.CASE_INSENSITIVE);
+            
+            List lines = readLines(config, urlMappings, null);
+            
+            for (Iterator it = lines.iterator(); it.hasNext(); )
+            {
+                String line = ((String) it.next()).trim();
+                
+                if ("".equals(line) || line.startsWith("#"))
+                {
+                    continue;
+                }
+                
+                Matcher reverseProxyPathURLMatcher = reverseProxyPathURLPattern.matcher(line);
+                Matcher reverseProxyRewriterMatcher = reverseProxyRewriterPattern.matcher(line);
+                
+                if (reverseProxyPathURLMatcher.matches())
+                {
+                    String localBasePath = reverseProxyPathURLMatcher.group(1);
+                    String remoteBaseURL = reverseProxyPathURLMatcher.group(2);
+                    HttpReverseProxyPathMapper proxyPathMapper = new DefaultHttpProxyPathMapperImpl(localBasePath, remoteBaseURL);
+                    proxyPathMappers.add(proxyPathMapper);
+                }
+                else if (reverseProxyRewriterMatcher.matches())
+                {
+                    String localBasePath = reverseProxyRewriterMatcher.group(1);
+                    String rulesMapping = reverseProxyRewriterMatcher.group(2);
+                    String rewriterRules = reverseProxyRewriterMatcher.group(3);
+                    
+                    try
+                    {
+                        RewriterController rewriterController = createRewriterController(config, rulesMapping);
+                        rewriterControllerPairs.add(new DefaultKeyValue(localBasePath, rewriterController));
+                        Rewriter rewriter = createRewriter(config, rewriterController, rewriterRules);
+                        rewriterPairs.add(new DefaultKeyValue(localBasePath, rewriter));
+                    }
+                    catch (Exception e)
+                    {
+                        throw new ServletException(e);
+                    }
+                }
+            }
+        }
+        
+        proxyPathMapperProvider = new DefaultHttpReverseProxyPathMapperProviderImpl(proxyPathMappers, rewriterControllerPairs, rewriterPairs);
+        proxyService = new RewritableHttpReverseProxyServiceImpl(proxyPathMapperProvider);
+        
+        String param = config.getInitParameter("reverseproxy.fileuploaddir");
+        
+        if (param != null)
+        {
+            param = param.trim();
+            
+            if (param.startsWith("file:"))
+            {
+                ((RewritableHttpReverseProxyServiceImpl) proxyService).setFileUploadDir(new File(URI.create(param)));
+            }
+            else if (param.startsWith("/"))
+            {
+                ((RewritableHttpReverseProxyServiceImpl) proxyService).setFileUploadDir(new File(config.getServletContext().getRealPath(param)));
+            }
+            else
+            {
+                ((RewritableHttpReverseProxyServiceImpl) proxyService).setFileUploadDir(new File(param));
+            }
+        }
+        
+        param = config.getInitParameter("reverseproxy.hostheadervalue");
+        
+        if (param != null)
+        {
+            ((RewritableHttpReverseProxyServiceImpl) proxyService).setHostHeaderValue(param.trim());
+        }
+        
+        param = config.getInitParameter("reverseproxy.localbaseurl");
+        
+        if (param != null)
+        {
+            ((RewritableHttpReverseProxyServiceImpl) proxyService).setLocalBaseURL(param.trim());
+        }
+        
+        param = config.getInitParameter("reverseproxy.maxfileuploadsize");
+        
+        if (param != null)
+        {
+            ((RewritableHttpReverseProxyServiceImpl) proxyService).setMaxFileUploadSize(Integer.parseInt(param.trim()));
+        }
+        
+        param = config.getInitParameter("reverseproxy.prependlocalbaseurl");
+        
+        if (param != null)
+        {
+            ((RewritableHttpReverseProxyServiceImpl) proxyService).setPrependLocalBaseURL(BooleanUtils.toBoolean(param.trim()));
+        }
+        
+        param = config.getInitParameter("reverseproxy.cookiepolicy");
+        
+        if (param != null)
+        {
+            ((RewritableHttpReverseProxyServiceImpl) proxyService).setCookiePolicy(param.trim());
+        }
+        
+        Properties connectionProperties = new Properties();
+        
+        for (Enumeration enumParamNames = config.getInitParameterNames(); enumParamNames.hasMoreElements(); )
+        {
+            String paramName = (String) enumParamNames.nextElement();
+            
+            if (paramName.startsWith("connection.property."))
+            {
+                String propName = paramName.substring("connection.property.".length());
+                String paramValue = config.getInitParameter(paramName);
+                connectionProperties.setProperty(propName, paramValue);
+            }
+        }
+        
+        if (!connectionProperties.isEmpty())
+        {
+            ((RewritableHttpReverseProxyServiceImpl) proxyService).setConnectionProperties(connectionProperties);
+        }
+        
+        proxyService.initialize();
+    }
+    
+    public void destroy()
+    {
+        if (proxyService != null)
+        {
+            proxyService.destroy();
+        }
+        
+        proxyService = null;
+    }
+    
+    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        proxyService.invoke(request, response);
+    }
+    
+    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        doGet(request, response);
+    }
+    
+    private List readLines(ServletConfig config, String resourceContextRelativePath, String encoding)
+    {
+        List lines = null;
+        InputStream input = null;
+        
+        try 
+        {
+            input = config.getServletContext().getResourceAsStream(resourceContextRelativePath);
+            lines = IOUtils.readLines(input, encoding);
+        }
+        catch (IOException e)
+        {
+            log.error("Failed to read resource: " + resourceContextRelativePath + ". " + e);
+        }
+        finally
+        {
+            if (input != null)
+            {
+                try 
+                {
+                    input.close();
+                }
+                catch (Exception ce)
+                {
+                }
+            }
+        }
+        
+        return Collections.unmodifiableList(lines != null ? lines : Collections.emptyList());
+    }
+    
+    private RewriterController createRewriterController(ServletConfig config, String rulesMappingResourcePath) throws Exception
+    {
+        RewriterController rwc = null;
+        
+        Class [] rewriterClasses = new Class[] { WebContentRewriter.class, WebContentRewriter.class};
+        Class [] adaptorClasses = new Class[] { NekoParserAdaptor.class, SaxParserAdaptor.class};
+        
+        rwc = new MappingRewriterController(config.getServletContext().getRealPath(rulesMappingResourcePath), 
+                                            Arrays.asList(rewriterClasses), 
+                                            Arrays.asList(adaptorClasses));
+        
+        return rwc;
+    }
+    
+    private Rewriter createRewriter(ServletConfig config, RewriterController rwc, String rewriterRulesResourcePath) throws Exception
+    {
+        Rewriter rewriter = null;
+        InputStream rewriterRulesInput = null;
+        
+        try
+        {
+            rewriterRulesInput = config.getServletContext().getResourceAsStream(rewriterRulesResourcePath);
+            rewriter = rwc.createRewriter(rwc.loadRuleset(rewriterRulesInput));
+        }
+        finally
+        {
+            if (rewriterRulesInput != null)
+            {
+                try 
+                {
+                    rewriterRulesInput.close();
+                }
+                catch (Exception ce) 
+                {
+                }
+            }
+        }
+        
+        return rewriter;
+    }
+    
+}

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java?rev=816126&view=auto
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java (added)
+++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java Thu Sep 17 10:22:06 2009
@@ -0,0 +1,620 @@
+/*
+ * 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.portals.applications.webcontent.proxy.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.disk.DiskFileItem;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.HttpMethodBase;
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.apache.commons.httpclient.NameValuePair;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
+import org.apache.commons.httpclient.methods.multipart.FilePart;
+import org.apache.commons.httpclient.methods.multipart.FilePartSource;
+import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
+import org.apache.commons.httpclient.methods.multipart.Part;
+import org.apache.commons.httpclient.methods.multipart.PartSource;
+import org.apache.commons.httpclient.methods.multipart.StringPart;
+import org.apache.commons.io.IOUtils;
+import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapper;
+import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapperProvider;
+import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyService;
+import org.apache.portals.applications.webcontent.rewriter.ParserAdaptor;
+import org.apache.portals.applications.webcontent.rewriter.Rewriter;
+import org.apache.portals.applications.webcontent.rewriter.RewriterController;
+import org.apache.portals.applications.webcontent.rewriter.RewriterException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * HTTP Reverse Proxy Service Implementation
+ * 
+ * @version $Id$
+ */
+public class RewritableHttpReverseProxyServiceImpl implements HttpReverseProxyService
+{
+    
+    private static Logger log = LoggerFactory.getLogger(RewritableHttpReverseProxyServiceImpl.class);
+    
+    public static final String REVERSE_PROXY_PATH_MAPPER_ATTRIBUTE = "org.apache.jetspeed.proxy.reverseProxyPathMapper";
+    
+    public static final String REWRITER_CONTROLLER_ATTRIBUTE = "rewriterController";
+    
+    private static final String LOCATION_HEADER = "Location";
+    
+    private static final String CONTENT_TYPE_HEADER = "Content-Type";
+    
+    private static final String CONTENT_LENGTH_HEADER = "Content-Length";
+    
+    private static final String HOST_HEADER = "Host";
+
+    /**
+     * Proxy path mapper provider
+     */
+    private HttpReverseProxyPathMapperProvider proxyPathMapperProvider;
+    
+    /**
+     * Forced host header value
+     */
+    private String hostHeaderValue;
+    
+    /**
+     * forced local base url. e.g., "http://localhost:8080/jetspeed/webcontent".
+     */
+    private String localBaseURL;
+    
+    /**
+     * flag to force to prefix localBaseURL when it redirects
+     */
+    private boolean prependLocalBaseURL;
+    
+    /**
+     * Maximum file upload size
+     */
+    private int maxFileUploadSize;
+    
+    /**
+     * File upload directory
+     */
+    private File fileUploadDir;
+    
+    /**
+     * Cookie policy
+     */
+    private String cookiePolicy = CookiePolicy.IGNORE_COOKIES;
+    
+    /**
+     * The shared http client for performance.
+     */
+    private HttpClient httpClient;
+    
+    /**
+     * HTTP Connection Parameters
+     */
+    private Properties connectionProperties;
+
+    public RewritableHttpReverseProxyServiceImpl(HttpReverseProxyPathMapperProvider proxyPathMapperProvider)
+    {
+        this.proxyPathMapperProvider = proxyPathMapperProvider;
+    }
+    
+    public void setHostHeaderValue(String hostHeaderValue)
+    {
+        this.hostHeaderValue = hostHeaderValue;
+    }
+    
+    public void setLocalBaseURL(String localBaseURL)
+    {
+        this.localBaseURL = localBaseURL;
+    }
+    
+    public void setPrependLocalBaseURL(boolean prependLocalBaseURL)
+    {
+        this.prependLocalBaseURL = prependLocalBaseURL;
+    }
+    
+    public void setMaxFileUploadSize(int maxFileUploadSize)
+    {
+        this.maxFileUploadSize = maxFileUploadSize;
+    }
+    
+    public void setFileUploadDir(File fileUploadDir)
+    {
+        this.fileUploadDir = fileUploadDir;
+    }
+    
+    public void setCookiePolicy(String cookiePolicy)
+    {
+        this.cookiePolicy = cookiePolicy;
+    }
+    
+    public void setConnectionProperties(Properties connectionProperties)
+    {
+        this.connectionProperties = connectionProperties;
+    }
+    
+    public void initialize()
+    {
+        MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
+        
+        if (connectionProperties != null)
+        {
+            for (Enumeration enumPropNames = connectionProperties.propertyNames(); enumPropNames.hasMoreElements(); )
+            {
+                String propName = (String) enumPropNames.nextElement();
+                String propValue = connectionProperties.getProperty(propName);
+                
+                try
+                {
+                    PropertyUtils.setProperty(connectionManager, propName, propValue);
+                }
+                catch (Exception e)
+                {
+                    if (log.isDebugEnabled())
+                    {
+                        log.error("Cannot set property. " + propName + " = " + propValue, e);
+                    }
+                    else
+                    {
+                        log.error("Cannot set property. " + propName + " = " + propValue + ". {}", e);
+                    }
+                }
+            }
+        }
+        
+        httpClient = new HttpClient(connectionManager);
+    }
+    
+    public void destroy()
+    {
+        if (httpClient != null)
+        {
+            ((MultiThreadedHttpConnectionManager) httpClient.getHttpConnectionManager()).shutdown();
+        }
+        
+        httpClient = null;
+    }
+    
+    public void invoke(HttpServletRequest request, HttpServletResponse response) throws IOException
+    {
+        // proxyPathMapper can be injected by using request attribute.
+        HttpReverseProxyPathMapper proxyPathMapper = (HttpReverseProxyPathMapper) request.getAttribute(REVERSE_PROXY_PATH_MAPPER_ATTRIBUTE);
+        
+        String pathInfo = request.getPathInfo();
+        
+        if (proxyPathMapper == null)
+        {
+            proxyPathMapper = proxyPathMapperProvider.findMapper(request);
+        }
+        
+        if (proxyPathMapper == null)
+        {
+            throw new IOException("Proxy configuration is not defined for " + pathInfo);
+        }
+        
+        if (hostHeaderValue == null)
+        {
+            if (request.getServerPort() == 80)
+            {
+                hostHeaderValue = request.getServerName();
+            }
+            else
+            {
+                hostHeaderValue = request.getServerName() + ":" + request.getServerPort();
+            }
+        }
+        
+        if (localBaseURL == null)
+        {
+            localBaseURL = request.getScheme() + "://" + hostHeaderValue + request.getServletPath();
+        }
+        
+        String proxyTargetURL = proxyPathMapper.getRemoteURL(pathInfo);
+        
+        if (proxyTargetURL == null)
+        {
+            throw new IllegalStateException("Cannot translate the location path info into remote URL. " + pathInfo);
+        }
+        
+        String method = request.getMethod();
+        boolean isGetMethod = "GET".equals(method);
+        boolean isPostMethod = "POST".equals(method);
+        boolean isPostMultipartMethod = isPostMethod && ServletFileUpload.isMultipartContent(request);
+        
+        HttpMethodBase httpMethodForProxyRequest = null;
+        
+        if (isGetMethod)
+        {
+            httpMethodForProxyRequest = new GetMethod(proxyTargetURL);
+        }
+        else if (isPostMethod)
+        {
+            httpMethodForProxyRequest = new PostMethod(proxyTargetURL);
+            
+            if (isPostMultipartMethod)
+            {
+                postMultipartParams((PostMethod) httpMethodForProxyRequest, request);
+            }
+            else
+            {
+                postFormParams((PostMethod) httpMethodForProxyRequest, request);
+            }
+        }
+        else
+        {
+            throw new IOException("Unsupported method: " + method);
+        }
+        
+        if (cookiePolicy != null)
+        {
+            httpMethodForProxyRequest.getParams().setCookiePolicy(cookiePolicy);
+        }
+        
+        // redirection should be adjusted with local host header...
+        httpMethodForProxyRequest.setFollowRedirects(false);
+        
+        String queryString = request.getQueryString();
+        
+        if (queryString != null)
+        {
+            httpMethodForProxyRequest.setQueryString(queryString);
+        }
+        
+        setProxyRequestHeaders(httpMethodForProxyRequest, request);
+        
+        if (isPostMultipartMethod)
+        {
+            postMultipartParams((PostMethod) httpMethodForProxyRequest, request);
+        }
+        else if (isPostMethod)
+        {
+            postFormParams((PostMethod) httpMethodForProxyRequest, request);
+        }
+        
+        try
+        {
+            int proxyResponseCode = httpClient.executeMethod(httpMethodForProxyRequest);
+            
+            // Check if the proxy response is a redirect
+            if (proxyResponseCode >= HttpServletResponse.SC_MULTIPLE_CHOICES /* 300 */
+                && proxyResponseCode < HttpServletResponse.SC_NOT_MODIFIED /* 304 */)
+            {
+                String location = httpMethodForProxyRequest.getResponseHeader(LOCATION_HEADER).getValue();
+                
+                if (location == null)
+                {
+                    throw new IOException("Recieved status code: " + proxyResponseCode + " but no " + LOCATION_HEADER + " header was found in the response");
+                }
+                
+                // Modify the redirect to go to this proxy servlet rather that the proxied host
+                // FYI, according to rfc2616, "Location" header value must be an absolute URI.
+                String localPath = proxyPathMapper.getLocalPath(location);
+                
+                // if the current proxy path mapper cannot map the remote location to local path, then
+                // try to find out a possible path mapper instead one more...
+                if (localPath == null)
+                {
+                    HttpReverseProxyPathMapper proxyPathMapperByLocation = proxyPathMapperProvider.findMapperByRemoteURL(location);
+                    
+                    if (proxyPathMapperByLocation != null)
+                    {
+                        localPath = proxyPathMapperByLocation.getLocalPath(location);
+                    }
+                }
+                
+                String redirectLocation = null;
+                
+                if (localPath == null)
+                {
+                    if (log.isWarnEnabled())
+                    {
+                        log.warn("Cannot translate the redirect location to local path. {}", location);
+                    }
+                    
+                    redirectLocation = location;
+                }
+                else
+                {
+                    if (prependLocalBaseURL) {
+                        redirectLocation = localBaseURL + localPath;
+                    } else {
+                        redirectLocation = request.getContextPath() + request.getServletPath() + localPath;
+                    }
+                }
+                
+                response.sendRedirect(redirectLocation);
+                
+                return;
+            }
+            else if (proxyResponseCode == HttpServletResponse.SC_NOT_MODIFIED)
+            {
+                // 304 needs special handling. See:
+                // http://www.ics.uci.edu/pub/ietf/http/rfc1945.html#Code304
+                // We get a 304 whenever passed an 'If-Modified-Since'
+                // header and the data on disk has not changed; server
+                // responds w/ a 304 saying I'm not going to send the
+                // body because the file has not changed.
+                response.setIntHeader(CONTENT_LENGTH_HEADER, 0);
+                response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+                
+                return;
+            }
+            
+            // Pass the response code back to the client
+            response.setStatus(proxyResponseCode);
+            
+            String responseContentType = null;
+            
+            // Pass response headers back to the client
+            Header[] headerArrayResponse = httpMethodForProxyRequest.getResponseHeaders();
+            
+            for (Header header : headerArrayResponse)
+            {
+                response.setHeader(header.getName(), header.getValue());
+                
+                if (responseContentType == null && header.getName().equalsIgnoreCase(CONTENT_TYPE_HEADER))
+                {
+                    responseContentType = header.getValue();
+                }
+            }
+            
+            // Send the content to the client
+            sendContentToClient(request, response, httpMethodForProxyRequest, responseContentType);
+        }
+        finally
+        {
+            // be sure the connection is released back to the connection manager...
+            httpMethodForProxyRequest.releaseConnection();
+        }
+    }
+    
+    private void sendContentToClient(HttpServletRequest request, HttpServletResponse response, HttpMethodBase httpMethodForProxyRequest, String responseContentType) throws IOException
+    {
+        InputStream in = null;
+        Reader reader = null;
+        OutputStream out = null;
+        Writer writer = null;
+        
+        try 
+        {
+            in = httpMethodForProxyRequest.getResponseBodyAsStream();
+            
+            // According to javadoc of httpclient, getResponseBodyAsStream() can return null
+            // if the response has no body.
+            if (in != null)
+            {
+                out = response.getOutputStream();
+
+                RewriterController rewriterController = proxyPathMapperProvider.findRewriterController(request);
+                Rewriter rewriter = proxyPathMapperProvider.findRewriter(request);
+                
+                if (rewriterController == null || rewriter == null)
+                {
+                    IOUtils.copy(in, out);
+                    out.flush();
+                }
+                else
+                {
+                    String mimeTypeForParserAdaptor = "text/html";
+                    
+                    if (responseContentType != null)
+                    {
+                        int offset = responseContentType.indexOf(';');
+                        
+                        if (offset > 0)
+                        {
+                            mimeTypeForParserAdaptor = mimeTypeForParserAdaptor.substring(0, offset).trim();
+                        }
+                        else
+                        {
+                            mimeTypeForParserAdaptor = responseContentType;
+                        }
+                    }
+                    
+                    ParserAdaptor parserAdaptor = rewriterController.createParserAdaptor(mimeTypeForParserAdaptor);
+                    String responseCharSet = httpMethodForProxyRequest.getResponseCharSet();
+                    
+                    if (responseCharSet != null)
+                    {
+                        reader = new InputStreamReader(in, responseCharSet);
+                        writer = new OutputStreamWriter(out, responseCharSet);
+                    }
+                    else
+                    {
+                        reader = new InputStreamReader(in);
+                        writer = new OutputStreamWriter(out);
+                    }
+                    
+                    rewriter.rewrite(parserAdaptor, reader, writer);
+                    writer.flush();
+                }
+            }
+        }
+        catch (RewriterException e)
+        {
+            throw new IOException(e.getLocalizedMessage());
+        }
+        finally
+        {
+            if (reader != null)
+            {
+                try { reader.close(); } catch (Exception ce) { }
+            }
+            if (in != null)
+            {
+                try { in.close(); } catch (Exception ce) { }
+            }
+            if (writer != null)
+            {
+                try { writer.close(); } catch (Exception ce) { }
+            }
+            if (out != null)
+            {
+                try { out.close(); } catch (Exception ce) { }
+            }
+        }
+    }
+
+    private void setProxyRequestHeaders(HttpMethod httpMethodForProxyRequest, HttpServletRequest request) 
+    {
+        // Get an Enumeration of all of the header names sent by the client
+        for (Enumeration enumHeaderNames = request.getHeaderNames(); enumHeaderNames.hasMoreElements(); ) 
+        {
+            String headerName = (String) enumHeaderNames.nextElement();
+            
+            if (headerName.equalsIgnoreCase(CONTENT_LENGTH_HEADER))
+                continue;
+            
+            // As per the Java Servlet API 2.5 documentation:
+            //      Some headers, such as Accept-Language can be sent by clients
+            //      as several headers each with a different value rather than
+            //      sending the header as a comma separated list.
+            // Thus, we get an Enumeration of the header values sent by the client
+            
+            for (Enumeration enumHeaderValues = request.getHeaders(headerName); enumHeaderValues.hasMoreElements(); )
+            {
+                String headerValue = (String) enumHeaderValues.nextElement();
+                
+                // In case the proxy host is running multiple virtual servers,
+                // rewrite the Host header to ensure that we get content from
+                // the correct virtual server
+                if (headerName.equalsIgnoreCase(HOST_HEADER))
+                {
+                    headerValue = hostHeaderValue;
+                }
+                
+                Header header = new Header(headerName, headerValue);
+                
+                // Set the same header on the proxy request
+                httpMethodForProxyRequest.setRequestHeader(header);
+            }
+        }
+    }
+    
+    private void postFormParams(PostMethod httpMethodForProxyRequest, HttpServletRequest request)
+    {
+        Map<String, String[]> paramsMap = (Map<String, String[]>) request.getParameterMap();
+        List<NameValuePair> paramNameValuePairs = new ArrayList<NameValuePair>();
+        
+        for (String paramName : paramsMap.keySet())
+        {
+            String [] paramValues = paramsMap.get(paramName);
+            
+            for (String paramValue : paramValues)
+            {
+                NameValuePair nameValuePair = new NameValuePair(paramName, paramValue);
+                paramNameValuePairs.add(nameValuePair);
+            }
+        }
+        
+        httpMethodForProxyRequest.setRequestBody(paramNameValuePairs.toArray(new NameValuePair[] {}));
+    }
+    
+    private void postMultipartParams(PostMethod httpMethodForProxyRequest, HttpServletRequest request) throws IOException
+    {
+        DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
+        
+        if (maxFileUploadSize > 0)
+        {
+            fileItemFactory.setSizeThreshold(maxFileUploadSize);
+        }
+        
+        if (fileUploadDir != null)
+        {
+            fileItemFactory.setRepository(fileUploadDir);
+        }
+        
+        ServletFileUpload fileUpload = new ServletFileUpload(fileItemFactory);
+        
+        try
+        {
+            List<FileItem> fileItems = (List<FileItem>) fileUpload.parseRequest(request);
+            Part [] parts = new Part[fileItems.size()];
+            int i = 0;
+            
+            for (FileItem fileItem : fileItems)
+            {
+                if (fileItem.isFormField())
+                {
+                    parts[i] = (new StringPart(fileItem.getFieldName(), fileItem.getString()));
+                }
+                else
+                {
+                    PartSource partSource = null;
+                    
+                    if (fileItem.isInMemory())
+                    {
+                        partSource = new ByteArrayPartSource(fileItem.getName(), fileItem.get());
+                    }
+                    else
+                    {
+                        partSource = new FilePartSource(fileItem.getFieldName(), ((DiskFileItem) fileItem).getStoreLocation());
+                    }
+                    
+                    parts[i] = (new FilePart(fileItem.getFieldName(), partSource));
+                }
+                
+                i++;
+            }
+            
+            MultipartRequestEntity multipartRequestEntity = new MultipartRequestEntity(parts, httpMethodForProxyRequest.getParams());
+            httpMethodForProxyRequest.setRequestEntity(multipartRequestEntity);
+            
+            // The current content-type header (received from the client) IS of
+            // type "multipart/form-data", but the content-type header also
+            // contains the chunk boundary string of the chunks. Currently, this
+            // header is using the boundary of the client request, since we
+            // blindly copied all headers from the client request to the proxy
+            // request. However, we are creating a new request with a new chunk
+            // boundary string, so it is necessary that we re-set the
+            // content-type string to reflect the new chunk boundary string
+            httpMethodForProxyRequest.setRequestHeader(CONTENT_TYPE_HEADER, multipartRequestEntity.getContentType());
+        }
+        catch (FileUploadException fileUploadException)
+        {
+            throw new IOException(fileUploadException.getLocalizedMessage());
+        }
+    }
+    
+}
+

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/rewriter/MappingRewriterController.java
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/rewriter/MappingRewriterController.java?rev=816126&r1=816125&r2=816126&view=diff
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/rewriter/MappingRewriterController.java (original)
+++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/rewriter/MappingRewriterController.java Thu Sep 17 10:22:06 2009
@@ -18,6 +18,7 @@
 
 import java.io.File;
 import java.io.FileReader;
+import java.io.InputStream;
 import java.io.Reader;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -28,13 +29,13 @@
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.apache.portals.applications.webcontent.rewriter.html.SwingParserAdaptor;
 import org.apache.portals.applications.webcontent.rewriter.rules.Ruleset;
 import org.apache.portals.applications.webcontent.rewriter.xml.SaxParserAdaptor;
 import org.exolab.castor.mapping.Mapping;
 import org.exolab.castor.xml.Unmarshaller;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.xml.sax.InputSource;
 
@@ -247,5 +248,32 @@
 
         return ruleset;
     }
+    
+    public Ruleset loadRuleset( InputStream input )
+    {
+        Ruleset ruleset = null;
+        try
+        {
+            DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
+            DocumentBuilder builder = dbfactory.newDocumentBuilder();
+
+            InputSource source = new InputSource(input);
+
+            Document doc = builder.parse(source);
+
+            Unmarshaller unmarshaller = new Unmarshaller(this.mapper);
+
+            ruleset = (Ruleset) unmarshaller.unmarshal(doc);
+            ruleset.sync();
+            rulesets.put(ruleset.getId(), ruleset);
+
+        }
+        catch (Throwable t)
+        {
+            log.error("ForwardService: Could not unmarshal: " + input, t);
+        }
+
+        return ruleset;
+    }
 
 }
\ No newline at end of file

Modified: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/rewriter/RewriterController.java
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/rewriter/RewriterController.java?rev=816126&r1=816125&r2=816126&view=diff
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/rewriter/RewriterController.java (original)
+++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/rewriter/RewriterController.java Thu Sep 17 10:22:06 2009
@@ -16,6 +16,7 @@
  */
 package org.apache.portals.applications.webcontent.rewriter;
 
+import java.io.InputStream;
 import java.io.Reader;
 
 import org.apache.portals.applications.webcontent.rewriter.rules.Ruleset;
@@ -66,7 +67,7 @@
         throws RewriterException;
     
     /**
-     * Loads a XML-based Rewriter Ruleset given a stream to the XML configuration.
+     * Loads a XML-based Rewriter Ruleset given a reader to the XML configuration.
      * 
      * @param reader The stream to the XML configuration.
      * @return A Ruleset configuration tree.
@@ -81,4 +82,12 @@
      */
     Ruleset lookupRuleset(String id);
     
+    /**
+     * Loads a XML-based Rewriter Ruleset given a stream to the XML configuration.
+     * 
+     * @param reader The stream to the XML configuration.
+     * @return A Ruleset configuration tree.
+     */
+    Ruleset loadRuleset(InputStream input);
+       
 }

Added: portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy-urlmappings.properties
URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy-urlmappings.properties?rev=816126&view=auto
==============================================================================
--- portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy-urlmappings.properties (added)
+++ portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy-urlmappings.properties Thu Sep 17 10:22:06 2009
@@ -0,0 +1,31 @@
+# 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.
+# ------------------------------------------------------------------------
+#
+# Reverse Proxy URL Mappings Configuration
+#
+# $Id$
+#
+# ------------------------------------------------------------------------
+
+# Note: Specify the local path info prefix and remote base url after "ReverseProxyPathURL".
+#       Also, you can specify rewriting rules by putting rules mapping configuration and rules configuration 
+#       for the specific local path after "ReverseProxyRewriter".
+
+ReverseProxyPathURL    /apache/    http://www.apache.org/
+#ReverseProxyRewriter   /apache/    /WEB-INF/conf/rproxy-rewriter-rules-mapping.xml    /WEB-INF/conf/rproxy-rewriter-rules-mapping.xml
+
+ReverseProxyPathURL    /portals/   http://portals.apache.org/
+#ReverseProxyRewriter   /portals/   /WEB-INF/conf/rproxy-rewriter-rules-mapping.xml    /WEB-INF/conf/rproxy-rewriter-rules-mapping.xml

Propchange: portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy-urlmappings.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy-urlmappings.properties
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy-urlmappings.properties
------------------------------------------------------------------------------
    svn:mime-type = text/plain