/*  Copyright (c) 2000, 2002 by Oracle Corporation. All Rights Reserved. */

package oracle.portal.provider.v1.http; 

import oracle.portal.provider.v1.PortletRenderer; 
import oracle.portal.provider.v1.ContentFilter; 
import oracle.portal.provider.v1.Provider; 
import oracle.portal.provider.v1.ExternalPrincipal;
import oracle.portal.utils.xml.v1.DefaultNodeHandler; 
import oracle.portal.provider.v1.PortletRenderRequest; 
import oracle.portal.provider.v1.PortletException; 
import oracle.portal.provider.v1.PortletRendererUtil; 
import oracle.portal.provider.v1.PortletNotFoundException; 
import oracle.portal.provider.v1.AccessControlException; 
import oracle.portal.provider.v1.AuthenticationException; 
import oracle.portal.provider.v1.ProviderSession; 
import oracle.portal.provider.v1.URLPortletParameter; 
import oracle.portal.provider.v1.PortletParameter; 
import oracle.portal.utils.v1.MultiByteEncoder; 
import oracle.portal.utils.xml.v1.NodeHandlerException; 
import oracle.portal.utils.xml.v1.InitializableXMLObject; 
import oracle.portal.provider.v1.ProviderSubscriber; 
import oracle.portal.provider.v1.ProviderException; 
import oracle.portal.utils.xml.v1.XMLUtil;
import oracle.webdb.utils.SimpleStringBuffer;
import java.io.PrintWriter; 
import java.io.InputStream; 
import java.io.ByteArrayInputStream; 
import java.io.FileOutputStream;
import java.io.InputStreamReader; 
import java.io.BufferedReader; 
import java.io.IOException; 
import HTTPClient.Cookie; 
import HTTPClient.HTTPConnection; 
import HTTPClient.HTTPResponse;
import HTTPClient.NVPair;
import HTTPClient.AuthorizationInfo;
import HTTPClient.URI; 
import HTTPClient.AuthorizationInfo; 
import HTTPClient.ModuleException;
import java.util.StringTokenizer; 
import java.util.Properties; 
import java.util.Enumeration; 
import java.io.File; 
import java.io.IOException; 
import java.io.OutputStreamWriter; 
import java.io.ByteArrayOutputStream;
import java.io.BufferedInputStream; 
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException; 
import java.util.Calendar; 
import java.util.Hashtable; 
import java.util.Date;
import java.text.DateFormat;
import java.net.URL;
import java.net.MalformedURLException;

/**
 * <p><b> URLRenderer </b> is responsible for portletizing url contents
 * specified through provider.xml. It opens an HTTPConnection to the url
 * specified and reads the contents from it. The read contents are then written
 * to the Portlet Renderer's output stream.</p>
 *
 * <p> It uses sophisticated filters to massage the contents of url. There can be
 * two types of filters depending on the requirement. HtmlFilter, a light weight
 * filter can be used for basic filtering and XmlFilter for advanced operations.
 * </p>
 *
 */
public class URLRenderer extends BaseManagedRenderer 
{ 
    //Values of Rendering Mode Tag's Child Elements
    private URL mPageUrl = null;
    
    //Value of <contentType> Tag
    private String contentType = null; 
    
    //Value of <redirectUrlFieldName> tag
    private String redirectUrlFieldName = null; 
    
    //Filter Class Object
    private ContentFilter mFilter = null; 
    
    //Debug level
    private int debugLevel = 0; 
    
    // store subscriber id
    private String subscriberID = null; 
    
    // points to provider.xml folder
    private String subscriberMapFilePath = null; 
    
    // create new a file per subscriber name under provider ID folder
    private File subscriberFile = null; 
    
    // Subscriber Details 
    private OutputStreamWriter fileWriter = null; 

    private File providerFolder = null; 

    //Filter type to be used. Deprecated.
    private String filterType = null; 

    // Character encoding to be applied to URL Content.
    private String mCharacterEncoding = null; 

    //Invalidation Cache
    private boolean mUseInvalidationCache = false;
    /**
     * The default character encoding for renderer output
     */
    public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";

    /**
     * Constant key for getting character encoding from the HTTP Response.
     * Usage - pr.getAttribute(URLRenderer.CHAR_ENCODING);
     */
    public static final String CHAR_ENCODING = "url.charset";

    //Oracle Wallet related settings.
    private String mWalletLocation = null;
    private String mWalletPassword = null;
    
    public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
    public static final String END_CERT = "-----END CERTIFICATE-----";

    /**
     * <p>
     * Method responsible for rendering 
     * Portlet's are responsible for ensuring the request's user has sufficient
     * rights to receive this rendering.</p>
     *
     * @param pr ProviderRequest for this rendering.
     * @exception PortletException,AccessControlException
     *
     * @see PortletException (@link oracle.portal.provider.v1.PortletException)
     * @see AccessControlException 
     *      (@link oracle.portal.provider.v1.AccessControlException)
     */
    public void renderBody(PortletRenderRequest pr) 
        throws PortletException, AccessControlException 
    { 
        //Read debug level from zone.properties.
        debugLevel = UrlServicesUtils.readDebugLevel(pr); 
        String authenticatedUrlContent = null; 
        Provider provider = pr.getProvider(); 
        UrlServicesUtils.logMessage(provider,"renderBody : "+ 
        "Entering render ********************************",debugLevel, 
        UrlServicesUtils.BASIC_LOGGING); 
        UrlServicesUtils.logMessage(provider,"renderBody : DebugLevel = "+ 
        debugLevel,debugLevel); 
        String user = pr.getUser().getName(); 
        ProviderSubscriber providerSubscriber = provider.getSubscriber(); 
        if (providerSubscriber != null) 
        { 
            subscriberID = providerSubscriber.getId(); 
            UrlServicesUtils.logMessage(provider,"URLRenderer:renderBody -- "+ 
            "subscriberID -- " + subscriberID,debugLevel); 
        } 
        Hashtable ht = new Hashtable();
        if ( subscriberID != null) 
        { 
            ht = new Hashtable(); 
            ht.put("Timestamp",Calendar.getInstance().getTime().toString()); 
            ht.put("username",user); 
            ht.put("ProviderId",Long.toString(provider.getProviderId())); 
            ht.put("portletID",Long.toString(pr.getPortlet().getId())); 
            ht.put("status","Show Portlet"); 
            UrlServicesUtils.logMessage(provider,"URLRenderer:renderBody -- "+ 
            "ht contains -- " + ht,debugLevel); 
            subscriberMapFilePath = provider.getProviderRepositoryPath()+ 
            File.separatorChar + subscriberID; 
            providerFolder = new File(subscriberMapFilePath); 
            if (!providerFolder.exists()) 
            { 
                providerFolder.mkdir(); 
            } 
            subscriberFile = new File( providerFolder.toString()+ 
            providerFolder.separator + Long.toString(provider.getProviderId())); 
            UrlServicesUtils.logMessage(provider,"URLRenderer:renderBody -- "+ 
            "subscriberFile -- " + subscriberFile.toString(),debugLevel); 
            this.storeSubscriberInfo(pr,subscriberFile,ht); 
        } // Checking SubscriberID providerSubscriber
        
        //Read Customized parameters from personalization object,
        //if at all it is present.
        URLPersonalizationObject data = getCustomizationData(pr);
        
        String paramList = null; 
        boolean isQualified = true;

        if (data != null) 
        { 
            isQualified = 
                XMLUtil.getBooleanValue(
                    data.getString(URLPersonalizationObject.QUALIFY_PARAMETERS));
        } 
        //Variable to store the read URL Content.
        StringBuffer strUrlContent = new StringBuffer(); 
        String finalPageUrl = getPageUrl(); 

        String inlinePageUrl = getPageUrl(pr, debugLevel);
        
        //Make ISO-8859-1 as default character encoding
        pr.setAttribute(URLRenderer.CHAR_ENCODING, DEFAULT_CHARACTER_ENCODING);
        
        //See if pageUrl has been passed through Request header.
        //If true, then the request is for rendering this link inlie.
        //So, ignore any parameters passed.
        if (inlinePageUrl != null) 
        { 
            finalPageUrl = inlinePageUrl; 
        } 
        else  
        { 
            //Read portlet page parameter information.
            paramList = readPortletPageParameters(pr, isQualified); 
            UrlServicesUtils.logMessage(pr.getProvider(), 
                "Params fetched through readPortletPageParameters : " + paramList, 
                debugLevel); 
            
            //If page parameter list is null, check out if there are any
            //customized paramters available
            if (paramList == null && data != null) 
            { 
                paramList = this.readCustomizedPortletParameters(pr, data); 
                UrlServicesUtils.logMessage(pr.getProvider(), 
                "Params fetched through readCustomizedPortletParameters : "+ 
                paramList, debugLevel); 
            } 
            
            //As a last try, try to see if there are any pre-populated parameter
            //values.
            if (paramList == null) 
            { 
                paramList = this.readPrepopulatedParameters(pr); 
                UrlServicesUtils.logMessage(pr.getProvider(), 
                "Params fetched through readPrepopulatedParameters : "+ 
                paramList, debugLevel); 
            } 

            //If mandatory parameters are missing and
            //there is no request of inline rendering flash an error message.
            if (paramList == null && (inlinePageUrl == null) ) 
            { 
                StringBuffer errorLine = new StringBuffer(); 
                errorLine.append("This portlet expects some mandatory parameters. "); 
                errorLine.append("Please provide them using the form given below :"); 
                String parameterPane = 
                    UrlServicesUtils.showPortletParameters(pr, isQualified); 
                showErrorMessage(pr, errorLine.toString()); 
                renderContents(pr, parameterPane, debugLevel, 
                  UrlServicesUtils.HIGHER_LOGGING); 
                return; 
            } 
            String pageUrl = getPageUrl();
            
            //Check if the given page URL already has parameters.
            //If true append to the available list of parameters.
            //Else add new parameter list.
            if (paramList != null && paramList.length() > 0) 
            { 
                finalPageUrl = 
                    HttpPortletRendererUtil.parameterizeLink(pageUrl, paramList);
            } 
        } 
        
        //Check if the URL is specified or not
        if (finalPageUrl == null || finalPageUrl.length() == 0) 
        { 
            UrlServicesUtils.logMessage(provider, 
                "Please specify a Valid <pageURL> in provider.xml", debugLevel); 
            throw new AccessControlException("Please specify a valid <pageURL> "+ 
                "in provider.xml"); 
        } 

        URL tURL = null;
        //Read URL Content from the URL specified in XML file.
        InputStream urlStream = null;
        PrintWriter writer = null;
        if(!finalPageUrl.equalsIgnoreCase(getPageUrl()))
        {
            try
            {
                writer = pr.getWriter();
                tURL = new URL(finalPageUrl);
                urlStream = getURLStream(pr, tURL);
            }
            catch(MalformedURLException me)
            {
                UrlServicesUtils.logMessage(provider, 
                    "Improper URL - " + finalPageUrl, debugLevel);
                renderErrorMessage(pr,"Improper URL specified for pageUrl tag.");
                return;
            }

            catch(IOException ie)
            {
                UrlServicesUtils.logMessage(provider, 
                    "Improper URL - " + finalPageUrl, debugLevel);
                renderErrorMessage(pr,
                    new SimpleStringBuffer(40)
                        .append("Could not read from {")
                        .append(tURL.toString())
                        .append("}. Please verify your proxy settings in provider.xml")
                        .toString());
                return;
            }
        }
        else
        {
            urlStream = getURLStream(pr, getParsedPageUrl());         
        }
        UrlServicesUtils.logMessage(provider,"UrlStream received = " + urlStream, 
        debugLevel); 
        
        //Instantiate the Filter class specified in class attribute of <pageName>,
        //based on current mode.
        try  
        { 
            ContentFilter filter = (ContentFilter) getFilter();
            if ( filter != null) 
            { 
                if (urlStream != null) 
                { 
                    UrlServicesUtils.logMessage(provider, "renderBody:filter "+ 
                    " is not null ", debugLevel); 
                    
                    //Catch IOException that might occur
                    try  
                    { 
                        filter.filterContent(pr, urlStream); 
                    } 
                    catch (IOException ie) 
                    { 
                        UrlServicesUtils.logMessage(provider, 
                        "renderBody : Exception occured while filtering the URL Content.", 
                        debugLevel); 
                    } 
                    UrlServicesUtils.logMessage(provider, 
                    "renderBody:After succesful completion of filtering:", 
                    debugLevel); 
                } 
            } 
            else  
            { 
                UrlServicesUtils.logMessage(provider, 
                    "renderBody: No filter is being used ", 
                    debugLevel); 
                StringBuffer finalUrlBuffer = new StringBuffer(); 
                String encoding = null;

                //See if default encoding is specified through provider.xml
                //Else read from content-type header.
                if(getCharSet() == null)
                {
                    encoding = (String)pr.getAttribute(CHAR_ENCODING);
                }
                else
                {
                    encoding = UrlServicesUtils.convertIANAtoJava(getCharSet());
                    pr.setAttribute(URLRenderer.CHAR_ENCODING, encoding);
                }
                
                //Convert the urlstream to a StringBuffer
                finalUrlBuffer = 
                    UrlServicesUtils.convertStreamToStringBuffer(
                        urlStream,
                        encoding,
                        pr.getProvider(),
                        debugLevel); 
                if (finalUrlBuffer == null) 
                { 
                    UrlServicesUtils.logMessage(provider, 
                    "renderBody : Exception while reading from input stream.", 
                    debugLevel); 
                    
                    throw new AccessControlException("Exception while reading from "+ 
                    "input stream."); 
                } 
                
                String finalURLContent = finalUrlBuffer.toString(); 
                UrlServicesUtils.logMessage(provider, 
                "renderBody:Before calling render contents", 
                debugLevel); 
                renderContents(
                    pr,
                    finalURLContent,
                    debugLevel, 
                    UrlServicesUtils.HTML_CONTENTS_LOGGING); 
            } 
        } //end of try block
        catch (IOException e) 
        { 
            UrlServicesUtils.logMessage(provider, 
            "renderBody: Generic Exception occured in Renderer", 
            e,debugLevel); 
            throw new AccessControlException("renderBody: Generic Exception "+ 
            "occured in Renderer"); 
        } 
        finally
        {
            if(urlStream != null)
            {
                try { urlStream.close(); }
                catch(IOException ie) {}
            }
        }
        UrlServicesUtils.logMessage(provider, 
            "renderBody:Before Leaving URLRenderer", 
            debugLevel, 
            UrlServicesUtils.BASIC_LOGGING); 
    } // end of renderBody
    
    /**
     * <p> Method which connects to the given content url and returns the
     * corresponding input stream. Care has been taken for two types of
     * authentication.
     * <p> In case of form based authentication, cookies stored in this user
     * context, through provider will be submitted to the connection and then 
     * input stream is fetched.
     *
     * <p> In case of basic authentication, authorization information is 
     * already specified in the provider's initSession. As we are operating
     * under the same context of HTTPClient from Renderer also, this 
     * authentication information still holds good.
     *
     * @param <p> pr PortletRenderRequest Object needed for getting Session object
     * as well as cookies.</p>
     * @param URL content URL from which input stream is to be read.
     * @return InputStream Inputstream to the specified URL
     */
    protected InputStream getURLStream(PortletRenderRequest pr, 
        URL contentURL) 
    throws PortletException 
    { 
        //Provider for logging messages
        Provider p = pr.getProvider(); 
        UrlServicesUtils.logMessage(p, "getURLStream Inside", debugLevel, 
            UrlServicesUtils.BASIC_LOGGING); 
        
        //Variable to store the read URL Content.
        StringBuffer strUrlContent = new StringBuffer(); 
        
        //Variable to store error messages.
        String logMessage = "";
        ProviderSession session = pr.getUser().getSession();
        UrlServicesUtils.logMessage(p, "getURLStream:Session = " + session, 
            debugLevel); 
        InputStream urlStream = null; 
        
        UrlServicesUtils.logMessage(p, "getURLStream : URL Constructed = "+ 
            contentURL, debugLevel); 
        //Set proxy related authentication information
        ProxyInformation httpProxyInformation = 
            (ProxyInformation)((URLProvider)p).getProxyInfo();
        boolean isProxyAuthSet = false;
        String proxyUser = null;
        String proxyPassword = null;
        NVPair pUserInfo = null;
        
        if (httpProxyInformation != null) 
        { 
            UrlServicesUtils.logMessage(p,
                "setting http proxy authentication information", 
                debugLevel); 
            proxyUser = httpProxyInformation.getProxyUser(); 
            proxyPassword = httpProxyInformation.getProxyPassword(); 
            //Add proxy authorization information if needed.
            if(proxyUser != null && proxyPassword != null)
            {
                pUserInfo = new NVPair(proxyUser, proxyPassword);
                URLAuthHandler.addAuthorization(p, pUserInfo);
            }
        } 

        HTTPConnection connection = null; 

        try
        {
            connection = new HTTPConnection(contentURL); 
            //Read proxy information.
            ((URLProvider)p).readProxyInformation(connection); 
            
            if(session != null)
            {
                connection.setContext(session.getId());
            }
            else
            {
                //As HTTPConnection cannot be set to more than one context at a 
                //time, proxy authentication works only for simple portlets
                //which are neither basic authenticated nor use SSO feature.
                //As this feature is still a beta feature, we need to come 
                //up with a work around for this.
                connection.setContext(String.valueOf(p.getProviderId()));
            }
        }
        catch(IOException e)
        {
            UrlServicesUtils.logMessage(p, 
                "Error in creating HTTPConnection.",
                debugLevel);
        }
        try
        {
            String walletPath = getWalletLocation();
            if(walletPath != null)
            {
                parseCerts(walletPath, connection);
            }
            else
            {
                UrlServicesUtils.logMessage(p, 
                    "Wallet unspecified.",
                    debugLevel);
            }
        }
        catch(IOException e)
        {
            UrlServicesUtils.logMessage(p, 
                "Could not parse the wallet.",
                debugLevel);
        }
        HTTPResponse response = null;
        try 
        { 
            response = connection.Get(contentURL.getFile());
            String encoding = null;

            if(getCharSet() != null)
            {
                encoding = UrlServicesUtils.convertIANAtoJava(getCharSet());
            }

            //Get encoding from content-type and make it your 
            //ProviderRequest's encoding.
            else
            {
                encoding = UrlServicesUtils.getEncodingFromContentType(
                             response.getHeader("Content-Type"));
            }

            pr.setAttribute(URLRenderer.CHAR_ENCODING, encoding);
            UrlServicesUtils.logMessage(p, 
                "Character encoding from content-type = " + encoding,
                debugLevel);
            urlStream = response.getInputStream();
        } 
        catch (ModuleException me) 
        { 
            String msg = "Exception while reading response from URL : " +
                         contentURL;

            //Log message and render the same on to client side.
            UrlServicesUtils.logMessage(p, msg, debugLevel);
            throw new PortletException(me.fillInStackTrace());
        } 
        
        
        catch (UnsupportedEncodingException ue) 
        { 
            String msg = "Encoding specified through header field is not " +
                         "supported.";

            //Log message and render the same on to client side.
            UrlServicesUtils.logMessage(p, msg, debugLevel);
            throw new PortletException(ue.fillInStackTrace());
        } 
        catch (IOException ie) 
        { 
            String msg = "Exception while reading content from inputstream.";
            //Log message and render the same on to client side.
            UrlServicesUtils.logMessage(p, msg, debugLevel);
            throw new PortletException(ie.fillInStackTrace());
        } 
        finally
        {
            if(pUserInfo != null)
            {
                URLAuthHandler.removeAuthorization(
                    String.valueOf(p.getProviderId()));
            }
        }
        
        UrlServicesUtils.logMessage(p, 
            "getURLStream: After getInputStream"+ 
            urlStream, 
            debugLevel); 

        UrlServicesUtils.logMessage(p, 
            "getUrlStream: Before returning Input Stream ...", debugLevel); 
        return urlStream; 
    } // end of getURLStream
    
    /**
     * Specifies the URL for fetching content
     * for a given rendering mode.
     * @param pageUrl value of URL for getting content
     */
    public void setPageUrl(String pageUrl) throws PortletException
    { 
        //Check if we received https URL. If true, set relevant HTTPClient 
        //protocol handler.
        if( pageUrl.toLowerCase().startsWith("https"))
        {
            String handler = System.getProperty("java.protocol.handler.pkgs");
            if(handler == null || !handler.equals("HTTPClient"))
            {
                System.setProperty("java.protocol.handler.pkgs", "HTTPClient");
            }
        }

        if (pageUrl.endsWith("/")) 
        {
            pageUrl = pageUrl.substring(0, pageUrl.length() - 1); 
        }
        try
        {
            mPageUrl = new URL(pageUrl);
        }
        catch(MalformedURLException me)
        {
            throw new PortletException("Improper pageUrl specified.");
        }
    } 
    
    /**
     * Returns the URL specified in the corresponding set method.
     * @retun String pageUrl
     */
    public String getPageUrl() 
    { 
        return mPageUrl.toString();
    } 
   
    /**
     * Sets the type of filter.This variable is not used from now on.
     * Still kept just to ensure provider.xml backward compatibility.
     */
    public void setFilterType(String type) 
    { 
        this.filterType = type; 
    } 
    
    /**
     * Sets the filter class for this rendering mode.
     * @param filter PortletFilter object.
     */
    public void setFilter(Object filter) throws PortletException 
    { 
        mFilter = (ContentFilter) filter;
    } 
    
    /**
     * Returns the Filter Class specified for a given
     * rendering mode.
     * @return Object Specified PortletFilter object.
     */
    public Object getFilter() 
    { 
        return mFilter; 
    } 
    
    /**
     * Helper method to write Subscriber info.
     */
    private void storeSubscriberInfo(PortletRenderRequest pr, 
    File fileName,Hashtable hashtable) 
    { 
        try  
        { 
            fileWriter = new OutputStreamWriter(
                             new FileOutputStream(fileName.toString()), "UTF-8"); 
            fileWriter.write(hashtable.get("Timestamp").toString() + "\t"); 
            fileWriter.write(hashtable.get("username").toString() + "\t"); 
            fileWriter.write(hashtable.get("ProviderId").toString() + "\t"); 
            fileWriter.write(hashtable.get("portletID").toString() + "\t\t"); 
            fileWriter.write(hashtable.get("status").toString()); 
            fileWriter.write("\n"); 
            fileWriter.close(); 
        } 
        catch (IOException e) 
        { 
            // File doesn't exist.
            UrlServicesUtils.logMessage(pr.getProvider(), 
            "storeSubscriberInfo :storeDetails: "+ 
            "Exception " + e,debugLevel); 
        } 
    } // storeSubscriberInfo method
    
    
    
    //Reads parameters passed through portlet's request header.
    private String readPortletPageParameters(
        PortletRenderRequest pr, 
        boolean isQualified) throws PortletException
    { 
        StringBuffer paramList = new StringBuffer(); 
        PortletParameter[] params = pr.getPortlet().getInputParameters(); 
        for (int i = 0, j = 0; (params != null) && i < params.length; i++) 
        { 
            URLPortletParameter parameter = (URLPortletParameter)params[i]; 
            String name = parameter.getName(); 
            String value = ""; 
            if (isQualified) 
            { 
                value = pr.getQualifiedParameter(name); 
            } 
            else  
            { 
                value = pr.getParameter(name); 
            } 
            if ( value == null || value.length() == 0 ) 
            { 
                if (parameter.isMandatory()) 
                { 
                    return null; 
                } 
                else  
                { 
                    
                    //Still this parameter is not customized. Read it from pre-
                    //populated values, if one exists.
                    if (parameter.getValue() != null) 
                    { 
                        value = parameter.getValue(); 
                    } 
                } 
            } 
            if (name.length() > 0 && value != null && value.length() > 0) 
            { 
                //Findout if it the first name,value pair. If so, no need of '&'
                if (j != 0) 
                {
                    paramList.append("&"); 
                }
                else  
                {
                    j++; 
                    paramList.append(
                        makeNVPair(name, value, pr.getURLCharSet())
                    ); 
                }
            } 
        } //end of for
        return paramList.toString(); 
    } 
    
    //Reads parameter information customized by teh user.
    private String readCustomizedPortletParameters(
        PortletRenderRequest pr, 
        URLPersonalizationObject data) throws PortletException
    { 
        StringBuffer paramList = new StringBuffer(); 
        PortletParameter params[] = pr.getPortlet().getInputParameters(); 
        int i = 0; 
        int j = 0; 
        for (; params != null && i < params.length; i++) 
        { 
            URLPortletParameter parameter = (URLPortletParameter)params[i]; 
            String name = parameter.getName(); 
            String value = data.getString( 
            URLPersonalizationObject.PORTLET_PARAMETER_VALUE + (i + 1)); 
            if ((parameter.isMandatory()) && 
                (value == null || value.length() == 0)) 
            { 
                if (parameter.getValue() != null) 
                { 
                    value = parameter.getValue(); 
                    data.putString( 
                    URLPersonalizationObject.PORTLET_PARAMETER_VALUE + (i + 1), 
                    value); 
                } 
                else  
                { 
                    return null; 
                } 
            } 
            if (name.length() == 0 || value == null || value.length() == 0) 
            continue; 
            if (j != 0) 
            paramList.append("&"); 
            else  
            j++; 
            paramList.append(makeNVPair(name, value, pr.getURLCharSet())); 
        } 
        return paramList.toString(); 
    } 
    
    //Reads parameters specified through provider.xml
    private String readPrepopulatedParameters(PortletRenderRequest pr) 
        throws PortletException
    { 
        StringBuffer paramList = new StringBuffer(); 
        PortletParameter params[] = pr.getPortlet().getInputParameters(); 
        int i = 0; 
        int j = 0; 
        for (; params != null && i < params.length; i++) 
        { 
            URLPortletParameter parameter = (URLPortletParameter)params[i]; 
            String name = parameter.getName(); 
            String value = parameter.getValue(); 
            if (parameter.isMandatory() && value == null) 
            { 
                return null; 
            } 
            if (name.length() == 0 || value == null || value.length() == 0) 
            continue; 
            if (j != 0) 
            paramList.append("&"); 
            else  
            j++; 
            paramList.append(makeNVPair(name, value, pr.getURLCharSet())); 
        } 
        return paramList.toString(); 
    } 
    
    protected String makeNVPair(String name, String value, String encoding) 
        throws PortletException
    { 
        StringBuffer nvPair = new StringBuffer(); 
        try
        {
            nvPair.append(
                MultiByteEncoder.encode(name, encoding));
            nvPair.append("="); 
            nvPair.append(
                MultiByteEncoder.encode(value, encoding));
        }
        catch(UnsupportedEncodingException ue)
        {
            throw new PortletException(ue.fillInStackTrace());
        }
        return nvPair.toString(); 
    } 
    
    //Shows error messages in portlet body.
    private void showErrorMessage(PortletRenderRequest pr, String message) 
    { 
        try  
        { 
            PrintWriter writer = pr.getWriter(); 
            PortletRendererUtil.renderError(pr, writer, pr.getLocale(), message);
        } 
        catch (IOException ie) 
        { 
            UrlServicesUtils.logMessage(pr.getProvider(), 
               "Error in showErrorMessage method.",
               UrlServicesUtils.BASIC_LOGGING);
        } 
    } 
    
    /**
     *  Sets the Default IANA character encoding to be used for this renderer's
     *  pages.
     *
     *  @param charSet IANA character encoding to be used for this renderer's
     *  pages
     */
    public void setCharSet(String encoding)
    {
        mCharacterEncoding = encoding;
    }

    /**
     *  Gets the Default IANA character encoding to be used for this renderer's
     *  pages. If a character encoding hasn't been set explicitly on this
     *  <code>URLRenderer</code>, then by default ISO-8859-1 will be used.
     *
     *  @return IANA character encoding to be used for this renderer's pages
     */
    public String getCharSet()
    {
        if (mCharacterEncoding == null)
        {
            return null;
        }
        return mCharacterEncoding;
    }
    /**
     * <p>Overriding super class's prepareResponse method for implementing
     * Invalidation Based Caching.
     * @param pr PortletRenderRequest to determine if the portlet has been 
     * customized.
     */
    public boolean prepareResponse(PortletRenderRequest pr)
        throws PortletException
    {
        if(!super.prepareResponse(pr))
        {
            return false;
        }

        //Check if invalidation renderering option is selected or not.
        if(!getUseInvalidationCaching())
        {
            return true;
        }

        String version = HttpPortletRendererUtil.getCachedVersion(pr);
        try
        {
            Object obj = PortletRendererUtil.getEditData(pr);
            if(version != null)
            {
                HttpPortletRendererUtil.useCachedVersion(pr);
                return false;
            }
        }
        //In case there is no customization data, IOException will be 
        //thrown. If there is already a cached version existent, use
        //the same.
        catch(IOException ioexception) 
        { 
            if(version != null)
            {
                HttpPortletRendererUtil.useCachedVersion(pr);
                return false;
            }
        }
        URLPersonalizationObject data = getCustomizationData(pr);
        Date lastEdited = URLEditRenderer.getLastEditTime(data);
        if(lastEdited == null)
            return true;
        if(version != null && Long.parseLong(version) == lastEdited.getTime())
        {
            HttpPortletRendererUtil.useCachedVersion(pr);
            return false;
        } 
        else
        {
            HttpPortletRendererUtil.setCachedVersion(pr, 
                String.valueOf(lastEdited.getTime()), 1);
            return true;
        }
    }

    /**
     * Convenience method to retrieve user customization data.
     * If not found, creates a new instance with default values and returns it.
     */
    protected static URLPersonalizationObject getCustomizationData(
      PortletRenderRequest pr) throws AccessControlException
    {
        // Retrieve the data object for later use.
        URLPersonalizationObject data = null; 
        try  
        { 
            data= 
              (URLPersonalizationObject)PortletRendererUtil.getEditData(pr); 
        } 
        catch (IOException ie) 
        { 
            data = new URLPersonalizationObject(); 
            data.setDefaultValues(pr); 
        } 
        return data;
    }
    /**
     * Returns flag which indicates renderer to use invalidation based
     * caching or not.
     * @return boolean Enable caching or not (true/false)
     */
    public boolean getUseInvalidationCaching()
    {
        return mUseInvalidationCache;
    }
    /**
     * Sets flag which indicates renderer to use invalidation based
     * caching or not.
     * @param useInCache Enable caching or not (true/false)
     */
    public void setUseInvalidationCaching(String useInCache)
    {   
        mUseInvalidationCache = XMLUtil.getBooleanValue(useInCache.trim());
    }
    /**
     * renderContents is a private helper method, which renders the
     * portlet with the specified url content.
     */
    public void renderContents(PortletRenderRequest pr, 
        String urlContent, 
        int debugLevel, 
        int type) 
    throws PortletException 
    { 
        PrintWriter out = null; 
        try  
        { 
            out = pr.getWriter(); 
        } 
        catch (IOException e) 
        { 
            Throwable t = e.fillInStackTrace(); 
            throw new PortletException(t); 
        } 
        UrlServicesUtils.logMessage(pr.getProvider(), 
            "renderContents:getCharacterEncoding() = " + 
            pr.getCharacterEncoding(),
            debugLevel); 
        UrlServicesUtils.logMessage(pr.getProvider(), 
            "******************renderContents:Contents "+ 
            " to be rendered *********** ", 
            debugLevel,
            type); 
        UrlServicesUtils.logMessage(pr.getProvider(),urlContent,debugLevel,type); 
        UrlServicesUtils.logMessage(pr.getProvider(), 
        "renderContents: Before rendering body of portlet", 
        debugLevel); 
        out.println(urlContent); 
    } 

    /**
     * Returns URL object constructed out of string url, passed through 
     * provider.xml. This eliminates the need to frequently parse the 
     * string to get URL object.
     *
     * @return URL object constructed out of string specified in provider.xml
     */
    public URL getParsedPageUrl()
    {
        return mPageUrl;
    }

    /**
     * Sets the location to the Wallet, exported to the text format, 
     * created using Oracle Wallet Manager. A typical wallet contains 
     * the user certificate, followed by the private key, the certificate 
     * chain, and any other trusted certificates. Oracle Java SSL uses 
     * this wallet in order to perform SSL handshake successfully between 
     * SSL client and the application.
     *
     * @param walletLocation location of the wallet exported to text format.
     * @see 
     *    <a href="http://otn.oracle.com/tech/java/oc4j/doc_library/902/servicesjun02/ohttps2.htm"> Oracle Java SSL.</a>
     */
    public void setWalletLocation(String walletLocation)
    {
        mWalletLocation = walletLocation;
    }

    /**
     * Returns the wallet location, exported to text format.
     * @return String wallet location.
     */
    public String getWalletLocation()
    {
        return mWalletLocation;
    }

    /** 
     * Returns the wallet password.
     * @return String The wallet password.
     */
    public String getWalletPassword()
    {
        return mWalletPassword;
    }
    /**
     * Sets the password for the walle, created through Oracle Wallet Manager.
     * @param walletPassword Password for the wallet.
     */
    public void setWalletPassword(String walletPassword)
    {
        mWalletPassword = walletPassword;
    }
    
    /**
     * Method first tries to read inline rendering url value from 
     * the request. Returns null if none present.
     *
     * Sub classes can override this method to provide their own Urls.
     * @param pr PortletRendererRequest
     * @param debugLevel Debug level
     */
    protected String getPageUrl(PortletRenderRequest pr, int debugLevel)
        throws PortletException
    {
        String inlinePageUrl = 
            pr.getQualifiedParameter(UrlServicesUtils.PAGE_URL);
        
        UrlServicesUtils.logMessage(pr.getProvider(), 
            "Inline pageurl received from request = " + inlinePageUrl, 
            debugLevel); 

        return inlinePageUrl;
    }
    private void renderErrorMessage(PortletRenderRequest pr, String message)
    {
        try
        {
            PortletRendererUtil.renderError(
                    pr, 
                    pr.getWriter(), 
                    pr.getLocale(), 
                    message);
        }
        catch(IOException ie)
        {
        }
        
    }
    /**
     * Method parses the wallet, extracts individual certificates and 
     * establishes trust points for each certificate. The beginning of
     * each certificate is denoted by <code>BEGIN_CERT</code> variable 
     * and it ends with a string denoted by <code>END_CERT<code> variable.
     */
    private void parseCerts(String walletPath, HTTPConnection connection) 
        throws IOException
    {
        FileInputStream fis = new FileInputStream(walletPath);
        byte[] bytes = new byte[32767];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int numRead = 0;
        while (fis.available() > 0)
        {
            numRead = fis.read(bytes);
            baos.write(bytes,0,numRead);
        }
        String walletContent = baos.toString();
        int bindex = 0;
        int eindex = -1;
        String cert = "";
        while(true)
        {
            bindex = walletContent.indexOf(BEGIN_CERT, bindex);
            if(bindex == -1)
            {
                break;
            }
            eindex = walletContent.indexOf(END_CERT, bindex);
            if(bindex == -1)
            {
                System.out.println("Could not parse wallet. End of certificate not found.");
                break;
            }
            cert = walletContent.substring(bindex, eindex+END_CERT.length()+1);

            //Establish trust point.
            connection.addTrustPoint(cert);
            bindex = bindex + BEGIN_CERT.length();
        }
    } //parseCerts
    

} //end of URLRenderer class