package faqapp.struts.actions;

import java.rmi.RemoteException;
import java.io.IOException;
import javax.ejb.CreateException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import org.apache.struts.action.*;

import faqapp.AppSessionFacade;
import faqapp.AppSessionFacadeHome;
import faqapp.struts.forms.FAQAbstractForm;

/**
 * Abstract implementation of Struts action, specific to the FAQApp.
 * This class implements common behavior across all action implementations
 * for the FAQApp. It implements process logic based on the HTTP request
 * action (create/update/save/query) and delegates execution to the appropriate
 * business logic. It also implements common error handling,
 */
public abstract class FAQAbstractAction extends Action {
    final String SUCCESS = "success";
    final String WARNING = "warning";
    final String ERROR = "error";
    final String ACTION = "action";
    final String CREATE = "create";
    final String UPDATE = "update";
    final String SAVE = "save";
    final String QUERY = "query";
    final String ID = "id";
    final String ASF = "asf";
    final String ASF_LOOKUP = "java:comp/env/ejb/AppSessionFacadeHome";

    /**
     * Process the specified HTTP request, and create the corresponding HTTP
     * response (or forward to another web component that will create it).
     * Return an <code>ActionForward</code> instance describing where and how
     * control should be forwarded, or <code>null</code> if the response has
     * already been completed.
     *
     * @param mapping The ActionMapping used to select this instance
     * @param form The optional ActionForm bean for this request (if any)
     * @param request The HTTP request we are processing
     * @param response The HTTP response we are creating
     *
     * @exception java.io.IOException if an input/output error occurs
     * @exception javax.servlet.ServletException if a servlet exception occurs
     */
    public final ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                 HttpServletResponse response) throws IOException, ServletException {
        try {
            String forward = null;
            String action;
            AppSessionFacade asf = getAppSessionFacade(request.getSession());

            /**
             * Implement generic perform() logic based on HTTP request
             * action (create/update/save/query) and dispatch to the
             * corresponding business processing methods for action type.
             */

            /* Get action parameter from either form or request */
            FAQAbstractForm af = (FAQAbstractForm)form;
            if (af != null) {
                action = af.getAction();
            } else {
                action = request.getParameter(ACTION);
            }

            /* Call appropriate processing method based on action parameter */
            if (CREATE.equals(action)) {
                forward = create(form, request, asf);
            } else if (UPDATE.equals(action)) {
                forward = update(form, request, asf);
            } else if (SAVE.equals(action)) {
                /**
                 * Validate HTTP request/form data before
                 * processing appropriate create/update logic.
                 */
                ActionErrors errors = validate(form, request, asf);
                if (errors.size() > 0) {
                    request.setAttribute(Action.ERROR_KEY, errors);
                    /**
                     * Even though we encountered validation errors, foward to SUCCESS
                     * in order to display errors using <html:errors/> in JSP view.
                     */
                    forward = SUCCESS;
                } else {
                    /**
                     * Decide whether to create or update underlying business object.
                     * If object ID exist, update object, if not, create it.
                     */
                    if (af.getId() == 0) {
                        forward = saveCreate(form, request, asf);
                    } else {
                        forward = saveUpdate(form, request, asf);
                    }
                }
            } else if (QUERY.equals(action)) {
                forward = query(form, request, asf);
            }
            if (forward == null) throw new Exception("FAQAbstractAction: Unknown Action!");

            /**
             * Forward control to the specified URI, based on outcome of
             * executed business logic (create/update/save/query)
             */
            return (mapping.findForward(forward));
        } catch (Exception e) {
            request.setAttribute("javax.servlet.jsp.jspException", e);
            /* Forward control to the specified error/exception URI */
            return (mapping.findForward(ERROR));
        }
    }

    private AppSessionFacade getAppSessionFacade(HttpSession session)
            throws RemoteException, CreateException, NamingException {
        /* Try to get AppSessionFacade from HTTP session */
        AppSessionFacade asf = (AppSessionFacade) session.getAttribute(ASF);

        /**
         * Test whether AppSessionFacade was successfully retrieved
         * from HTTP session, if not, create it
         */
        if (asf == null) {
            /* Get a reference to the AppSessionFacade Object */
            InitialContext ic = new InitialContext();
            Object objref = ic.lookup(ASF_LOOKUP);
            AppSessionFacadeHome home = (AppSessionFacadeHome) PortableRemoteObject.narrow(
                    objref, AppSessionFacadeHome.class);
            asf = home.create();
            /* Store AppSessionFacade in HTTP session */
            session.setAttribute(ASF, asf);
        }
        return asf;
    }

    abstract ActionErrors validate(ActionForm form, HttpServletRequest request, AppSessionFacade asf) throws Exception;
    abstract String create(ActionForm form, HttpServletRequest request, AppSessionFacade asf) throws Exception;
    abstract String update(ActionForm form, HttpServletRequest request, AppSessionFacade asf) throws Exception;
    abstract String saveCreate(ActionForm form, HttpServletRequest request, AppSessionFacade asf) throws Exception;
    abstract String saveUpdate(ActionForm form, HttpServletRequest request, AppSessionFacade asf) throws Exception;
    abstract String query(ActionForm form, HttpServletRequest request, AppSessionFacade asf) throws Exception;
}

