// Copyright (c) 2001 Oracle Corporation
package oracle.portal.provider.v1.http;

import java.io.*;
import oracle.portal.provider.v1.*;
import oracle.portal.utils.xml.v1.*;

/**
 * When maintained by a {@link oracle.portal.provider.v1.RenderManager}, a
 * <code>FileRenderer</code> renders a single page's contents from a static
 * file.<p>
 *
 * The properties <code>appPath</code>, <code>appRoot</code>,
 * <code>contentType</code>, <code>pageExpires</code>, <code>charSet</code> may
 * be set at page level or defaulted at {@link
 * oracle.portal.provider.v1.RenderManager} level.  However the page name must
 * be set at page level via a call to <code>{@link #setName(String)}</code> or
 * by use of the <code>&ltname&gt</code> <code>provider.xml</code> tag.
 *
 */
public class FileRenderer extends Page
{
    public static final int CHAR_BUFFER_SIZE = 8192;

    private char[] mCache = null;
    private long mLastModified = -1;

    /**
     * Null Constructor. The {@link BaseManagedRenderer#preInitialize(Object)}
     * method should be called after creating an object with this constructor
     * and the {@link BaseManagedRenderer#postInitialize()} method should be
     * called after setting up page properties with the various accessor
     * methods. Normally a {@link DefaultNodeHandler} takes care of these
     * details.
     */
    public FileRenderer()
    {
    }

    /**
     * Creates and initializes a <code>FileRenderer</code> owned by the given
     * {@link PortletPageLocator}, describing a page with the given name.
     * @param parent the <code>PortletPageLocator</code> that owns this page
     * @param pageName the <code>appPath</code> relative name of the renderer's
     * page
     * @exception PortletException if an error occurs while initializing the
     * object
     */
    public FileRenderer(PortletPageLocator parent, String pageName) throws PortletException
    {
        try
        {
            preInitialize(parent);
            setName(pageName);
            postInitialize();
        }
        catch(NodeHandlerException e)
        {
            throw new PortletException(e);
        }
    }

    /**
     * Render the specified page's contents using the current appRoot and page
     * name. File Renderer does this by copying the contents of the static file
     * to the output stream.
     *
     * @param pr a PortletRenderRequest
     * @exception PortletException if an error occurs while rendering.
     */
    public void renderBody(PortletRenderRequest pr)
        throws PortletException
    {
        try
        {
            // Obtain the writer for this request.
            Writer out = pr.getWriter();

            // Obtain the file.
            File file = new File(getPageRealPath(pr));

            // Cache the File's contents to avoid reading the file with each
            // subsequent request, unless the file has been modified in the
            // meantime.
            if (mCache == null || (mLastModified != file.lastModified()))
            {
                InputStreamReader isr = null;
                CharArrayWriter cacheOut = null;
                try
                {
                    BufferedInputStream bis =
                        new BufferedInputStream(new FileInputStream(file),
                                                CHAR_BUFFER_SIZE * 2);
                    isr = new InputStreamReader(bis, getCharSet());
                    cacheOut = new CharArrayWriter();
                    char[] buf = new char[CHAR_BUFFER_SIZE];
                    int read = 0;
                    while ((read = isr.read(buf)) != -1)
                    {
                        cacheOut.write(buf, 0, read);
                    }
                    mCache = cacheOut.toCharArray();
                    mLastModified = file.lastModified();
                }
                finally
                {
                    // Close any streams we have opened.
                    try
                    {
                        if (isr != null)
                        {
                            isr.close();
                        }
                        if (cacheOut != null)
                        {
                            cacheOut.close();
                        }
                    }
                    catch (IOException e)
                    {
                        // throw this one away.
                    }
                }
            }

            // Write the contents of the cache to the required output stream.
            out.write(mCache);
        }
        catch (IOException ioe)
        {
            throw new PortletException(ioe);
        }
    }

}
