/*
 *  ====================================================================
 *  The Apache Software License, Version 1.1
 *
 *  Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 *  reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in
 *  the documentation and/or other materials provided with the
 *  distribution.
 *
 *  3. The end-user documentation included with the redistribution,
 *  if any, must include the following acknowledgment:
 *  "This product includes software developed by the
 *  Apache Software Foundation (http://www.apache.org/)."
 *  Alternately, this acknowledgment may appear in the software itself,
 *  if and wherever such third-party acknowledgments normally appear.
 *
 *  4. The names "Apache" and "Apache Software Foundation" and
 *  "Apache Jetspeed" must not be used to endorse or promote products
 *  derived from this software without prior written permission. For
 *  written permission, please contact apache@apache.org.
 *
 *  5. Products derived from this software may not be called "Apache" or
 *  "Apache Jetspeed", nor may "Apache" appear in their name, without
 *  prior written permission of the Apache Software Foundation.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 *  SUCH DAMAGE.
 *  ====================================================================
 *
 *  This software consists of voluntary contributions made by many
 *  individuals on behalf of the Apache Software Foundation.  For more
 *  information on the Apache Software Foundation, please see
 *  <http://www.apache.org/>.
 */
package org.apache.jetspeed.portal.portlets;

import org.apache.ecs.ConcreteElement;
import org.apache.ecs.StringElement;
import org.apache.jetspeed.portal.portlets.AbstractPortlet;
import org.apache.turbine.util.RunData;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;


/**  
 *   This portlet class is intended to be used for internal URLs that will 
 *   not work properly if they are part of the same request as the Jetspeed/Turbine
 *   controller servlet--Struts-mapped URLs are an example of these.
 *   <br>
 *   ServletProxyPortlet uses an application context-relative URL as an input parameter.  It uses data obtained from
 *   <code>org.apache.turbine.util.RunData</code> to construct an absolute URL and append the session id to it. Using this
 *   URL, it constructs a java.net.URL object, retrieves the content from it, and converts the content to a String.  Finally,
 *   it returns an ECS StringElement created with this String for Jetspeed to render as a portlet.  Content is returned as-is;
 *   no filtering is performed on the html before returning it.
 *   <br/><br/>
 *   This portlet accepts 3 parameters:<br/>
 *
 *   URL (required) 		  -- the web application-context relative URL (a query string may be used to pass parameters)<br/>
 *   session_token (optional) -- token key used by the web server to pass the session id on the query string<br/>
 *   protocol (optional)	  -- protocol to use to make the URL request
 *
 * @author <a href="mailto:joe.barefoot@motiva.com">Joe Barefoot</a>
 */


public class ServletProxyPortlet extends AbstractPortlet
{
    private final static int BUFFER_SIZE = 2048;

    /** The name of the parameter to hold our application context-relative URL */
    public static final String URL_PARAMETER_NAME = "URL";
	/** The name of the parameter to hold the protocol to use (optional, default is http) **/
	public static final String PROTOCOL_PARAMETER_NAME = "protocol";
	/** The name of the parameter to hold the token by which the session ID is passed on the query string (optional, default is jsessionid)
	 *  This is included to accomodate all web servers, as the token is usually different from one server to another.
	 */
	public static final String SESSION_TOKEN_PARAMETER_NAME = "session_token";

	/** The default protocol used to construct the URL -- http */
	public static final String DEFAULT_PROTOCOL = "http";
	/** The default token to use to pass the session ID on the query String -- jsessionid */
	public static final String DEFAULT_SESSION_TOKEN = "jsessionid";

    /**  Gets content by proxy (java.net.URL) from an internal URL and returns it
	 *
	 * @param rundata The RunData object for the current request
	 * @return an ECS StringElement
	 */
    public ConcreteElement getContent(RunData rundata)
    {
        String servletURL = processURL(rundata);
		if(servletURL == null)
		{
			return new StringElement("ServletInvokerPortlet:  Must specify a URL using the URL parameter");
		}
        String content;

        //  This is probably not robust for large content returns, but should work okay within an application context with small amounts of content.
        try
        {

            URL url = new URL(servletURL);
            URLConnection connection = url.openConnection();
            InputStream stream = connection.getInputStream();
            BufferedInputStream in = new BufferedInputStream(stream);
            int length = 0;
            byte[] buf = new byte[BUFFER_SIZE];
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            while ((in != null) && ((length = in.read(buf)) != -1))
            {
                // the data has already been read into buf
                out.write(buf, 0, length);
            }
            content = out.toString();
            return new StringElement(content);
        }
        catch (Exception e)
        {
            String message = "ServletInvokerPortlet: Error invoking " + servletURL + ": " + e.getMessage();
            return new StringElement(message);
        }

    }

	/**  Constructs a full URL to retrieve content from.  Override to append custom (default) query parameters, etc.
	 *
	 * @param rundata The RunData object for the current request
	 * @return  An absolute URL with the session ID appended
	 */
    protected String processURL(RunData rundata)
    {
        String servletURL = getPortletConfig().getInitParameter(URL_PARAMETER_NAME);
		if( servletURL == null) // short-circuit
		{
			return null;
		}
		String protocol = getPortletConfig().getInitParameter(PROTOCOL_PARAMETER_NAME);
		if(protocol == null)
		{
			protocol = DEFAULT_PROTOCOL;
		}
		String token = getPortletConfig().getInitParameter(SESSION_TOKEN_PARAMETER_NAME);
		if(token == null)
		{
			token = DEFAULT_SESSION_TOKEN;
		}


        String queryString = new String();
        int queryIndex = servletURL.indexOf("?");
        if(queryIndex > 0)
        {
            queryString = servletURL.substring(queryIndex);
            servletURL = servletURL.substring(0, queryIndex);
        }
        servletURL = protocol + "://" + rundata.getServerName() + ":" + rundata.getServerPort() + rundata.getContextPath() + servletURL + ";" + token + "=" + rundata.getSession().getId() + queryString;
        return servletURL;
    }

}
