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

import java.lang.reflect.*;
import oracle.portal.utils.xml.v1.*;
import oracle.portal.provider.v1.*;

/**
 * JavaRenderer has responsibility for responding to a {@link
 * PortletRenderRequest} by dynamically invoking an arbitrarily named method on
 * an instance of an arbitrary Java class. The only restrictions are that the
 * class must have a null constructor, and the named method must take a {@link
 * PortletRenderRequest} as the only parameter.<p>
 *
 * The properties <code>contentType</code> and <code>pageExpires</code> may be
 * set explicitly on a JavaRenderer, or inherited from defaults set at {@link
 * oracle.portal.provider.v1.RenderManager} level. However the class and method
 * names must be set at JavaRenderer level via calls to <code>{@link
 * #setClassName(String)}</code> and <code>{@link
 * #setRenderMethod(String)}</code>. Once these strings have been set, it is
 * important that the {@link #postInitialize()} method is called to complete
 * initialization. If the provider is a {@link
 * oracle.portal.provider.v1.http.DefaultProvider}, these calls may be
 * initiated as a result of <code>&lt;className></code> and
 * <code>&lt;renderMethod></code> declarations in the provider XML registry.<p>
 */
public class JavaRenderer extends BaseManagedRenderer
    implements InitializableXMLObject
{
    private String mClassName = null;
    private String mRenderMethod = null;
    private Method mMethod = null;
    private Object mInstance = null;

    static final Class[] sParamTypes = { PortletRenderRequest.class };

    /**
     * Null constructor.
     */
    public JavaRenderer()
    {
    }

    /**
     * Renders the request using dynamic invokation of the preset method on an
     * instance of the preset class. The method must take a {@link
     * PortletRenderRequest} as its only parameter.
     *
     * @param pr a PortletRenderRequest
     * @exception PortletException if dynamic invocation of the method fails
     */
    public void renderBody(PortletRenderRequest pr) throws PortletException
    {
        try
        {
            Object[] param = { pr };
            mMethod.invoke(mInstance, param);
        }
        catch (Exception e)
        {
            throw new PortletException(e);
        }
    }

    /**
     * Sets the fully-qualified class name of the object used to render this
     * request.
     *
     * @param className fully-qualified class name.
     */
    public void setClassName(String className)
    {
        mClassName = className;
    }

    /**
     * Gets the fully-qualified class name of the object used to render this
     * request.
     *
     * @return the fully-qualified class name of the object used to render this
     * request.
     */
    public String getClassName()
    {
        return mClassName;
    }

    /**
     * Sets the name of the method to be called on an instance of the preset
     * class to render a request.
     *
     * @param methodName a method name.
     */
    public void setRenderMethod(String methodName)
    {
        mRenderMethod = methodName;
    }

    /**
     * Gets the name of the method called on an instance of the preset class to
     * render a request.
     *
     * @return a method name.
     */
    public String getRenderMethod()
    {
        return mRenderMethod;
    }

    /**
     * Performs the steps required to initialize this JavaRenderer after
     * it has been constructed. Currently, this method does nothing.
     * @param o the object that will become this object's parent
     * @exception NodeHandlerException if an error occurs while initializing
     * this application object
     */
    public void preInitialize(Object o) throws NodeHandlerException
    {
        super.preInitialize(o);
    }

    /**
     * Performs the extra steps required to initialize and validate this
     * JavaRenderer once its properties have been set with the appropriate
     * accessor methods. In this implementation, the method attempts to
     * instantiate an instance of the preset class, and locate the preset
     * method within the class.
     * @exception NodeHandlerException if the class or method are not set, or
     * an error occurs while initializing the preset class or locating the
     * preset method.
     */
    public void postInitialize() throws NodeHandlerException
    {
        super.postInitialize();
        if(mClassName == null || mRenderMethod == null)
        {
            throw new NodeHandlerException("Renderer improperly configured. " + 
                                           "Missing either a className " +
                                           "or a methodName");
        }
        try
        {
            Class rendererClass = Class.forName(mClassName);
            mMethod = rendererClass.getMethod(mRenderMethod, sParamTypes);
            mInstance = rendererClass.newInstance();
        }
        catch (Exception e)
        {
            throw new NodeHandlerException(e);
        }
    }
}
