org.ajax4jsf.context.AjaxContextImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ajax4jsf.context.AjaxContextImpl.java

Source

/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.ajax4jsf.context;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.StateManager;
import javax.faces.application.ViewHandler;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.AbortProcessingException;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.ajax4jsf.Messages;
import org.ajax4jsf.application.AjaxViewHandler;
import org.ajax4jsf.renderkit.AjaxContainerRenderer;
import org.ajax4jsf.renderkit.AjaxRendererUtils;
import org.ajax4jsf.renderkit.RendererUtils;
import org.ajax4jsf.renderkit.RendererUtils.HTML;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This class incapsulated
 * 
 * @author asmirnov@exadel.com (latest modification by $Author: alexsmirnov $)
 * @version $Revision: 1.1.2.7 $ $Date: 2007/02/08 19:07:16 $
 * 
 */
public class AjaxContextImpl extends AjaxContext {

    public static final String SERVLET_ERROR_EXCEPTION_ATTRIBUTE = "javax.servlet.error.exception";

    private static final Log log = LogFactory.getLog(AjaxContext.class);

    Set<String> ajaxAreasToRender = new HashSet<String>();

    Set<String> ajaxAreasToProcess = null;

    Set<String> ajaxRenderedAreas = new LinkedHashSet<String>();

    boolean ajaxRequest = false;

    boolean selfRender = false;

    Integer viewSequence = new Integer(1);

    String submittedRegionClientId = null;

    String ajaxSingleClientId = null;

    ViewIdHolder viewIdHolder = null;

    Map<String, Object> responseDataMap = new HashMap<String, Object>();

    Map<String, Object> commonAjaxParameters = new HashMap<String, Object>();

    Object oncomplete = null;

    public void release() {

        ajaxAreasToRender = new HashSet<String>();

        ajaxAreasToProcess = null;

        ajaxRenderedAreas = new LinkedHashSet<String>();

        ajaxRequest = false;

        selfRender = false;

        viewSequence = new Integer(1);

        submittedRegionClientId = null;

        viewIdHolder = null;

        responseDataMap = new HashMap<String, Object>();

        commonAjaxParameters = new HashMap<String, Object>();

    }

    /* (non-Javadoc)
     * @see org.ajax4jsf.context.AjaxContext#decode(javax.faces.context.FacesContext)
     */
    @Override
    public void decode(FacesContext context) {
        ExternalContext externalContext = context.getExternalContext();
        if (null == externalContext.getRequestMap().get(SERVLET_ERROR_EXCEPTION_ATTRIBUTE)) {
            Map<String, String> requestParameterMap = externalContext.getRequestParameterMap();
            String ajaxRegionId = requestParameterMap.get(AjaxContainerRenderer.AJAX_PARAMETER_NAME);
            setSubmittedRegionClientId(ajaxRegionId);
            setAjaxRequest(null != ajaxRegionId);
            setAjaxSingleClientId(requestParameterMap.get(AjaxRendererUtils.AJAX_SINGLE_PARAMETER_NAME));
        } else {
            // Error page is always serviced as non-ajax.
            setAjaxRequest(false);
            setSubmittedRegionClientId(null);
            setAjaxSingleClientId(null);
        }
    }

    /**
     * @param context
     * @throws AbortProcessingException
     */
    public void renderAjax(FacesContext context) throws FacesException {
        if (log.isDebugEnabled()) {
            log.debug(Messages.getMessage(Messages.RENDER_AJAX_REQUEST, getSubmittedRegionClientId()));
        }
        try {
            // Just in case...
            setSelfRender(true);
            setAjaxRequest(true);
            // create response writer.
            ExternalContext extContext = context.getExternalContext();
            RenderKit renderKit = context.getRenderKit();
            String encoding;
            // Depends if we talk about servlets, portlets, ...
            if (extContext.getRequest() instanceof ServletRequest) {
                ServletRequest request = (ServletRequest) extContext.getRequest();
                ServletResponse response = (ServletResponse) extContext.getResponse();
                // Setup encoding and content type
                String contentType = "text/xml";
                // get the encoding - must be setup by faces context or filter.
                encoding = request.getCharacterEncoding();
                if (encoding == null) {
                    encoding = "UTF-8";
                }
                response.setContentType(contentType + ";charset=" + encoding);
            } else
                encoding = "UTF-8";

            PrintWriter servletWriter;
            servletWriter = getWriter(extContext);
            ResponseWriter writer = renderKit.createResponseWriter(servletWriter, null, encoding);
            context.setResponseWriter(writer);
            // make response
            writer.startDocument();
            encodeAjaxBegin(context);
            context.getViewRoot().encodeAll(context);
            saveViewState(context);
            encodeAjaxEnd(context);
            writer.endDocument();
            writer.flush();
            writer.close();
            servletWriter.close();
            // Save tree state.
        } catch (IOException e) {
            throw new FacesException(
                    Messages.getMessage(Messages.RENDERING_AJAX_REGION_ERROR, getSubmittedRegionClientId()), e);
        } finally {
            context.responseComplete();
            // component.setRendererType(defaultRenderer);
        }
    }

    /**
     * Encode declaration for AJAX response. Render &lt;html&gt;&lt;body&gt;
     * 
     * @param context
     * @throws IOException
     */
    public void encodeAjaxBegin(FacesContext context) throws IOException {
        UIViewRoot viewRoot = context.getViewRoot();
        // AjaxContainer ajax = (AjaxContainer) component;
        ResponseWriter out = context.getResponseWriter();
        // DebugUtils.traceView("ViewRoot in AJAX Page encode begin");
        out.startElement(HTML.HTML_ELEMENT, viewRoot);
        Locale locale = viewRoot.getLocale();
        out.writeAttribute(HTML.lang_ATTRIBUTE, locale.toString(), "lang");
        out.startElement(HTML.BODY_ELEMENT, viewRoot);
    }

    /**
     * End encoding of AJAX response. Render tag with included areas and close
     * &lt;/body&gt;&lt;/html&gt;
     * 
     * @param context
     * @throws IOException
     */
    public void encodeAjaxEnd(FacesContext context) throws IOException {
        // AjaxContainer ajax = (AjaxContainer) component;
        ResponseWriter out = context.getResponseWriter();
        // DebugUtils.traceView("ViewRoot in AJAX Page encode begin");
        out.endElement(HTML.BODY_ELEMENT);
        out.endElement(HTML.HTML_ELEMENT);
    }

    public void saveViewState(FacesContext context) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        StateManager stateManager = context.getApplication().getStateManager();
        Object serializedView = stateManager.saveView(context);
        if (null != serializedView && null != writer) {
            StringWriter bufWriter = new StringWriter();
            ResponseWriter tempWriter;
            tempWriter = writer.cloneWithWriter(bufWriter);
            context.setResponseWriter(tempWriter);
            stateManager.writeState(context, serializedView);
            tempWriter.flush();
            if (bufWriter.getBuffer().length() > 0) {
                context.getExternalContext().getRequestMap().put(AjaxViewHandler.SERIALIZED_STATE_KEY,
                        bufWriter.toString());
            }
            context.setResponseWriter(writer);
        }
    }

    protected RenderKit getRenderKit(FacesContext context) {
        RenderKit renderKit = context.getRenderKit();
        if (null == renderKit) {
            String renderKitId = context.getApplication().getViewHandler().calculateRenderKitId(context);
            RenderKitFactory factory = (RenderKitFactory) FactoryFinder
                    .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
            renderKit = factory.getRenderKit(context, renderKitId);
        }
        return renderKit;
    }

    /**
     * @return Returns the ajaxRequest.
     */
    public boolean isAjaxRequest() {
        return ajaxRequest;
    }

    /**
     * @param ajaxRequest
     *            The ajaxRequest to set.
     */
    public void setAjaxRequest(boolean ajaxRequest) {
        this.ajaxRequest = ajaxRequest;
    }

    /**
     * @return Returns the ajaxAreasToRender.
     */
    public Set<String> getAjaxAreasToRender() {
        return this.ajaxAreasToRender;
    }

    /**
     * @return the ajaxAreasToProcess
     */
    @Override
    public Set<String> getAjaxAreasToProcess() {
        return ajaxAreasToProcess;
    }

    /**
     * @param ajaxAreasToProcess the ajaxAreasToProcess to set
     */
    @Override
    public void setAjaxAreasToProcess(Set<String> ajaxAreasToProcess) {
        this.ajaxAreasToProcess = ajaxAreasToProcess;
    }

    /**
     * Add affected regions's ID to ajaxView component.
     * 
     * @param component
     */
    public void addRegionsFromComponent(UIComponent component) {
        // First step - find parent ajax view
        Set<String> ajaxRegions = AjaxRendererUtils.getAjaxAreas(component);
        // if (ajaxRegions == null){
        // FacesContext context = FacesContext.getCurrentInstance();
        // ajaxRegions = AjaxRendererUtils.getAbsoluteId(context,component);
        // }
        if (log.isDebugEnabled()) {
            log.debug(Messages.getMessage(Messages.INVOKE_AJAX_REGION_LISTENER, component.getId()));
        }
        if (ajaxRegions != null) {
            for (Iterator<String> iter = ajaxRegions.iterator(); iter.hasNext();) {
                String id = iter.next().toString();
                ajaxAreasToRender.add(convertId(component, id));
            }
        }
        // Is that component limit to list ?
        if (Boolean.TRUE.equals(component.getAttributes().get("limitToList"))) {
            setLimitToList(true);
        }
    }

    /** 
     * Add IDs of regions to process 
     * 
     * @see org.ajax4jsf.context.AjaxContext#addAreasToProcessFromComponent(javax.faces.component.UIComponent)
     */
    @Override
    public void addAreasToProcessFromComponent(FacesContext context, UIComponent component) {
        RendererUtils rendererUtils = RendererUtils.getInstance();

        Set<String> areasToProcess = AjaxRendererUtils.getAjaxAreasToProcess(component);
        if (areasToProcess != null) {
            Set<String> convertedAreaIds = new HashSet<String>();

            for (String areaId : areasToProcess) {
                UIComponent areaComponent = rendererUtils.findComponentFor(component, areaId);
                if (areaComponent != null) {
                    convertedAreaIds.add(areaComponent.getClientId(context));
                } else {
                    convertedAreaIds.add(areaId);
                }
            }

            if (this.ajaxAreasToProcess == null) {
                this.ajaxAreasToProcess = convertedAreaIds;
            } else {
                this.ajaxAreasToProcess.addAll(convertedAreaIds);
            }
        }
    }

    public void addComponentToAjaxRender(UIComponent component) {
        this.ajaxAreasToRender.add(AjaxRendererUtils.getAbsoluteId(component));
    }

    public void addComponentToAjaxRender(UIComponent base, String id) {
        this.ajaxAreasToRender.add(convertId(base, id));
    }

    /**
     * Test for relative id of target components. Attempt convert to absolute.
     * For use as argument for
     * {@link RendererUtils#findComponentFor(UIComponent, String)}
     * 
     * @param component
     * @param id
     * @return
     */
    private String convertId(UIComponent component, String id) {
        if (id.charAt(0) == NamingContainer.SEPARATOR_CHAR) {
            return id;
        }
        if (null == component) {
            throw new NullPointerException("Base component for search re-rendered compnnent is null");
        }
        UIComponent target = RendererUtils.getInstance().findComponentFor(component, id);
        if (null != target) {
            return AjaxRendererUtils.getAbsoluteId(target);
        }
        log.warn("Target component for id " + id + " not found");
        return id;
    }

    /**
     * @return Returns the ajaxRenderedAreas.
     */
    public Set<String> getAjaxRenderedAreas() {
        return ajaxRenderedAreas;
    }

    public void addRenderedArea(String id) {
        ajaxRenderedAreas.add(id);
    }

    public boolean removeRenderedArea(String id) {
        return ajaxRenderedAreas.remove(id);
    }

    /**
     * @return Returns the submittedClientId.
     */
    public String getSubmittedRegionClientId() {
        return this.submittedRegionClientId;
    }

    /**
     * @param submittedClientId
     *            The submittedClientId to set.
     */
    @Override
    public void setSubmittedRegionClientId(String submittedClientId) {
        this.submittedRegionClientId = submittedClientId;
    }

    /**
     * @return the ajaxSingleClientId
     */
    @Override
    public String getAjaxSingleClientId() {
        return ajaxSingleClientId;
    }

    /**
     * @param ajaxSingleClientId the ajaxSingleClientId to set
     */
    @Override
    public void setAjaxSingleClientId(String ajaxSingleClientId) {
        this.ajaxSingleClientId = ajaxSingleClientId;
    }

    /**
     * @return Returns the selfRender.
     */
    public boolean isSelfRender() {
        return selfRender;
    }

    /**
     * @param selfRender
     *            The selfRender to set.
     */
    public void setSelfRender(boolean selfRender) {
        this.selfRender = selfRender;
    }

    /**
     * @return the vievIdHolder
     */
    public ViewIdHolder getViewIdHolder() {
        return viewIdHolder;
    }

    /**
     * @param viewIdHolder
     *            the vievIdHolder to set
     */
    public void setViewIdHolder(ViewIdHolder viewIdHolder) {
        this.viewIdHolder = viewIdHolder;
    }

    /**
     * @return the responseData
     */
    public Object getResponseData() {
        return responseDataMap.get(RESPONSE_DATA_KEY);
    }

    /**
     * @param responseData
     *            the responseData to set
     */
    public void setResponseData(Object responseData) {
        this.responseDataMap.put(RESPONSE_DATA_KEY, responseData);
    }

    /**
     * @return the responseDataMap
     */
    public Map<String, Object> getResponseDataMap() {
        return responseDataMap;
    }

    /**
     * Gives back the writer of a Response object.
     * 
     * @param extContext
     *            The external context.
     * @return The writer of the response.
     * @throws FacesException
     *             If the response object has no getWriter() method.
     */
    protected PrintWriter getWriter(ExternalContext extContext) throws FacesException {
        PrintWriter writer = null;
        Object response = extContext.getResponse();
        try {
            Method gW = response.getClass().getMethod("getWriter", new Class[0]);
            writer = (PrintWriter) gW.invoke(response, new Object[0]);
        } catch (Exception e) {
            throw new FacesException(e);
        }
        return writer;
    }

    public String getAjaxActionURL(FacesContext context) {
        // Check arguments
        if (null == context) {
            throw new NullPointerException("Faces context for build AJAX Action URL is null");
        }
        UIViewRoot viewRoot = context.getViewRoot();
        if (null == viewRoot) {
            throw new NullPointerException("Faces view tree for build AJAX Action URL is null");
        }
        String viewId = viewRoot.getViewId();
        if (null == viewId) {
            throw new NullPointerException("View id for build AJAX Action URL is null");
        }
        if (!viewId.startsWith("/")) {
            throw new IllegalArgumentException("Illegal view Id for build AJAX Action URL: " + viewId);
        }
        ViewHandler viewHandler = context.getApplication().getViewHandler();
        String actionURL = viewHandler.getActionURL(context, viewId);
        // HACK - check for a Jboss PortletBridge implementation. If present, append DirectLink attribute to url.
        // TODO - how to detect portlet application ?
        if (null != context.getExternalContext().getApplicationMap()
                .get("org.jboss.portletbridge.application.PortletStateHolder")) {
            // Mark Ajax action url as transparent with jsf-portlet bridge.
            actionURL = actionURL + ((actionURL.lastIndexOf('?') > 0) ? "&" : "?")
                    + "javax.portlet.faces.DirectLink=true";

        }
        return context.getExternalContext().encodeActionURL(actionURL);
    }

    /**
     * @return the commonAjaxParameters
     */
    public Map<String, Object> getCommonAjaxParameters() {
        return commonAjaxParameters;
    }

    /**
     * @return the oncomplete
     */
    public Object getOncomplete() {
        return oncomplete;
    }

    /**
     * @param oncomplete
     *            the oncomplete to set
     */
    public void setOncomplete(Object oncomplete) {
        this.oncomplete = oncomplete;
    }

}