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

import java.lang.reflect.*;
import oracle.portal.utils.xml.v2.*;
import oracle.portal.provider.v2.PortletException;
import oracle.portal.provider.v2.render.PortletRenderRequest;
import oracle.portal.provider.v2.utils.ValidationError;

/**
 * JavaRenderer has responsibility for responding to a {@link
 * PortletRenderRequest} by dynamically invoking an arbitrarily named method on
 * an instance of an arbitrary 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.v2.render.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 definition is a {@link
 * oracle.portal.provider.v2.DefaultProviderDefinition}, 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 invocation 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
    {
        if (mInstance == null)
        {
            try
            {
                createInstance();
            }
            catch (Exception e)
            {
                throw new PortletException(e);
            }
        }
        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;
    }

    /**
     * Validates the internal state of this <code>JavaRenderer</code>
     * instance. Errors found are reported using the supplied {@link
     * oracle.portal.provider.v2.utils.ValidationError} instance.
     *
     * @param context The context of the calling instance.
     * @param errors The current list of errors.
     */
    public void validate(String context, ValidationError errors)
    {
        String ctx = ValidationError.newContext(context, "JavaRenderer", null);

        // call super type
        super.validate(ctx, errors);

        // Check the validity of the member variables
        if (mClassName == null)
        {
            errors.addError(ctx, "Class name must not be null", null);
        }

        if (mRenderMethod == null)
        {
            errors.addError(ctx, "Render method must not be null", null);
        }

        if (mClassName != null && mRenderMethod != null)
        {
            try
            {
                createInstance();
            }
            catch (ClassNotFoundException e)
            {
                errors.addError(ctx, "Class not found", mClassName);
            }
            catch (NoSuchMethodException e)
            {
                errors.addError(ctx, "No such method", mRenderMethod); 
            }
            catch (Exception e)
            {
                errors.addError(ctx, "Instantiotion error", e.getMessage());
            }
        }
    }

    private void createInstance() throws Exception
    {
        // We have to use the the current thread's 'context class loader'
        // (i.e. the web app's class loader) to load the class, so that it
        // can be picked up from within the web app
        Class rendererClass = Class.forName(mClassName, true,
                                            Thread.currentThread().getContextClassLoader());
        mMethod = rendererClass.getMethod(mRenderMethod, sParamTypes);
        mInstance = rendererClass.newInstance();
    }

}
