org.apache.myfaces.custom.ajax.api.AjaxDecodePhaseListener.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.myfaces.custom.ajax.api.AjaxDecodePhaseListener.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.myfaces.custom.ajax.api;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.component.html.ext.UIComponentPerspective;
import org.apache.myfaces.custom.ajax.util.AjaxRendererUtils;
import org.apache.myfaces.custom.inputAjax.HtmlCommandButtonAjax;
import org.apache.myfaces.custom.util.URIComponentUtils;
import org.apache.myfaces.shared_tomahawk.component.ExecuteOnCallback;
import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlResponseWriterImpl;
import org.apache.myfaces.shared_tomahawk.renderkit.html.util.FormInfo;

import javax.faces.application.StateManager;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * This short circuits the life cycle and applies updates to affected components only
 * <p/>
 * User: treeder
 * Date: Oct 26, 2005
 * Time: 6:03:21 PM
 */
public class AjaxDecodePhaseListener implements PhaseListener {
    private static Log log = LogFactory.getLog(AjaxDecodePhaseListener.class);

    /**
     * Some Ajax components might do special decoding at apply request values phase
     *
     * @return PhaseId The AJAX decode phase listener will be invoked before apply request values phase.
     */
    public PhaseId getPhaseId() {
        return PhaseId.APPLY_REQUEST_VALUES;
    }

    public void beforePhase(PhaseEvent event) {
        log.debug("In AjaxDecodePhaseListener beforePhase");
        FacesContext context = event.getFacesContext();
        Map externalRequestMap = context.getExternalContext().getRequestParameterMap();

        if (externalRequestMap.containsKey("affectedAjaxComponent")) {
            UIViewRoot root = context.getViewRoot();
            String affectedAjaxComponent = (String) context.getExternalContext().getRequestParameterMap()
                    .get("affectedAjaxComponent");
            UIComponent ajaxComponent = root.findComponent(affectedAjaxComponent);
            //checking if ajaxComp is inside a dataTable - necessary for non JSF 1.2 MyFaces implementation
            if (ajaxComponent instanceof UIComponentPerspective) {
                UIComponentPerspective componentPerspective = (UIComponentPerspective) ajaxComponent;
                ajaxComponent = (UIComponent) componentPerspective.executeOn(context, new ExecuteOnCallback() {
                    public Object execute(FacesContext facesContext, UIComponent ajaxComponent) {
                        handleAjaxRequest(ajaxComponent, facesContext);

                        return ajaxComponent;
                    }
                });
            } else {
                handleAjaxRequest(ajaxComponent, context);
            }

            StateManager stateManager = context.getApplication().getStateManager();
            if (!stateManager.isSavingStateInClient(context)) {
                stateManager.saveSerializedView(context);
            }
            context.responseComplete();
        }
    }

    private void handleAjaxRequest(UIComponent ajaxComponent, FacesContext facesContext) {
        String updateOnly = (String) facesContext.getExternalContext().getRequestParameterMap().get("updateOnly");
        if (updateOnly == null) {
            // then decode
            decodeAjax(ajaxComponent, facesContext);
            facesContext.getViewRoot().processApplication(facesContext);
        }
        // else only update
        encodeAjax(ajaxComponent, facesContext);
    }

    private void decodeAjax(UIComponent ajaxComponent, FacesContext context) {

        String affectedAjaxComponent = (String) context.getExternalContext().getRequestParameterMap()
                .get("affectedAjaxComponent");
        if (ajaxComponent == null) {
            String msg = "Component with id [" + affectedAjaxComponent + "] not found in view tree.";
            log.error(msg);
            throw new ComponentNotFoundException(msg);
        }
        log.debug("affectedAjaxComponent: " + ajaxComponent + " - " + ajaxComponent.getId());
        //todo: refactor this completely - should be somewhere else, but definitely not
        //in the general phase listener
        if (ajaxComponent instanceof HtmlCommandButtonAjax) {
            // special treatment for this one, it will try to update the entire form
            // 1. get surrounding form
            //String elname = (String) requestMap.get("elname");
            FormInfo fi = RendererUtils.findNestingForm(ajaxComponent, context);
            UIComponent form = fi.getForm();
            //System.out.println("FOUND FORM: " + form);
            if (form != null) {
                form.processDecodes(context);
                //if(context.getRenderResponse())
                form.processValidators(context);
                //else log.debug("Decode failed on Ajax Decode");
                //if(context.getRenderResponse())
                form.processUpdates(context);
                //else log.debug("Validation failed on Ajax Decode");
                //System.out.println("DONE!");
            }
        } else if (ajaxComponent instanceof AjaxComponent) {
            try {
                // Now let the component decode this request
                ((AjaxComponent) ajaxComponent).decodeAjax(context);
            } catch (Exception e) {
                log.error("Exception while decoding ajax-request", e);
            }
        } else {
            log.error("Found component is no ajaxComponent : " + RendererUtils.getPathToComponent(ajaxComponent));
        }
    }

    private void encodeAjax(UIComponent component, FacesContext context) {
        ServletResponse response = (ServletResponse) context.getExternalContext().getResponse();
        ServletRequest request = (ServletRequest) context.getExternalContext().getRequest();
        UIViewRoot viewRoot = context.getViewRoot();
        Map requestMap = context.getExternalContext().getRequestParameterMap();

        String charset = (String) requestMap.get("charset");

        /* Handle character encoding as of section 2.5.2.2 of JSF 1.1:
         * At the beginning of the render-response phase, the ViewHandler must ensure
         * that the response Locale is set to that of the UIViewRoot, for exampe by
         * calling ServletResponse.setLocale() when running in the servlet environment.
         * Setting the response Locale may affect the response character encoding.
         *
         * Since there is no 'Render Response' phase for AJAX requests, we have to handle
         * this manually.
         */
        response.setLocale(viewRoot.getLocale());

        if (component instanceof DeprecatedAjaxComponent) {
            try {
                String contentType = getContentType("text/xml", charset);
                response.setContentType(contentType);

                StringBuffer buff = new StringBuffer();
                buff.append("<?xml version=\"1.0\"?>\n");
                buff.append("<response>\n");

                PrintWriter out = response.getWriter();
                out.print(buff);

                // imario@apache.org: setup response writer, otherwise the component will fail with an NPE. I dont know why this worked before.
                context.setResponseWriter(
                        new HtmlResponseWriterImpl(out, contentType, request.getCharacterEncoding()));

                if (component instanceof HtmlCommandButtonAjax) {
                    buff = new StringBuffer();
                    buff.append("<triggerComponent id=\"");
                    buff.append(component.getClientId(context));
                    buff.append("\" />\n");
                    out.print(buff);

                    // special treatment for this one, it will try to update the entire form
                    // 1. get surrounding form
                    //String elname = (String) requestMap.get("elname");
                    FormInfo fi = RendererUtils.findNestingForm(component, context);
                    UIComponent form = fi.getForm();
                    //System.out.println("FOUND FORM: " + form);
                    if (form != null) {
                        // special case, add responses from all components in form
                        encodeChildren(form, context, requestMap);
                    }
                } else if (component instanceof AjaxComponent) {
                    // let component render xml response
                    // NOTE: probably don't need an encodeAjax in each component, but leaving it in until that's for sure
                    ((AjaxComponent) component).encodeAjax(context);
                } else {
                    // just get latest value
                    AjaxRendererUtils.encodeAjax(context, component);
                }
                // end response
                out.print("</response>");
                out.flush();
            } catch (IOException e) {
                log.error("Exception while rendering ajax-response", e);
            }
        } else if (component instanceof AjaxComponent) {
            try {
                if (context.getResponseWriter() == null) {
                    String contentType = getContentType("text/html", charset);
                    response.setContentType(contentType);
                    PrintWriter writer = response.getWriter();
                    context.setResponseWriter(
                            new HtmlResponseWriterImpl(writer, contentType, response.getCharacterEncoding()));
                }

                ((AjaxComponent) component).encodeAjax(context);
            } catch (IOException e) {
                log.error("Exception while rendering ajax-response", e);
            }
        }

    }

    private String getContentType(String contentType, String charset) {
        if (charset == null || charset.trim().length() == 0)
            return contentType;
        else
            return contentType + ";charset=" + charset;
    }

    private void encodeChildren(UIComponent form, FacesContext context, Map requestMap) throws IOException {
        List formChildren = form.getChildren();
        for (int i = 0; i < formChildren.size(); i++) {
            UIComponent uiComponent = (UIComponent) formChildren.get(i);
            //System.out.println("component id: " + uiComponent.getClientId(context));
            // only if it has a matching id in the request list
            if (requestMap.containsKey(uiComponent.getClientId(context))) {
                //System.out.println("FOUND COMPONENT SO ENCODING AJAX");
                AjaxRendererUtils.encodeAjax(context, uiComponent);
            }
            // recurse
            encodeChildren(uiComponent, context, requestMap);
        }
    }

    /**
     * spit out each name/value pair
     * THIS IS IN HASHMAPUTILS, BUT FOR SOME REASON, ISN'T GETTING INTO THE JARS
     */
    public static String mapToString(Map map) {
        Set entries = map.entrySet();
        Iterator iter = entries.iterator();
        StringBuffer buff = new StringBuffer();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            buff.append("[" + entry.getKey() + "," + entry.getValue() + "]\n");
        }
        return buff.toString();
    }

    public static Object getValueForComponent(FacesContext context, UIComponent component) {
        String possibleClientId = component.getClientId(context);

        if (!context.getExternalContext().getRequestParameterMap().containsKey(possibleClientId)) {
            possibleClientId = (String) context.getExternalContext().getRequestParameterMap()
                    .get("affectedAjaxComponent");

            log.debug("affectedAjaxComponent: " + possibleClientId);
            UIViewRoot root = context.getViewRoot();
            UIComponent ajaxComponent = root.findComponent(possibleClientId);
            if (ajaxComponent instanceof UIComponentPerspective) {
                UIComponentPerspective componentPerspective = (UIComponentPerspective) ajaxComponent;

                ajaxComponent = (UIComponent) componentPerspective.executeOn(context, new ExecuteOnCallback() {
                    public Object execute(FacesContext facesContext, UIComponent uiComponent) {
                        return uiComponent;
                    }
                });
            }

            if (ajaxComponent != component) {
                log.error("No value found for this component : " + possibleClientId);
                return null;
            }
        }

        Object encodedValue = context.getExternalContext().getRequestParameterMap().get(possibleClientId);

        if (encodedValue instanceof String) {
            return URIComponentUtils.decodeURIComponent((String) encodedValue);
        } else
            return encodedValue;
    }

    public void afterPhase(PhaseEvent event) {

    }

}