001 package org.apache.myfaces.tobago.ajax.api; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one or more 005 * contributor license agreements. See the NOTICE file distributed with 006 * this work for additional information regarding copyright ownership. 007 * The ASF licenses this file to You under the Apache License, Version 2.0 008 * (the "License"); you may not use this file except in compliance with 009 * the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020 import org.apache.commons.logging.Log; 021 import org.apache.commons.logging.LogFactory; 022 import org.apache.commons.lang.StringUtils; 023 024 import org.apache.myfaces.tobago.context.ResourceManagerUtil; 025 import org.apache.myfaces.tobago.util.RequestUtils; 026 import org.apache.myfaces.tobago.util.ResponseUtils; 027 import org.apache.myfaces.tobago.renderkit.html.HtmlConstants; 028 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes; 029 030 import javax.faces.FactoryFinder; 031 import javax.faces.application.StateManager; 032 import javax.faces.component.UIComponent; 033 import javax.faces.component.UIViewRoot; 034 import javax.faces.context.ExternalContext; 035 import javax.faces.context.FacesContext; 036 import javax.faces.context.ResponseWriter; 037 import javax.faces.event.PhaseEvent; 038 import javax.faces.event.PhaseId; 039 import javax.faces.event.PhaseListener; 040 import javax.faces.render.RenderKit; 041 import javax.faces.render.RenderKitFactory; 042 import javax.servlet.http.HttpServletResponse; 043 import java.io.IOException; 044 import java.io.PrintWriter; 045 import java.io.StringWriter; 046 import java.util.Map; 047 048 /** 049 * 050 * !! adapted copy of sandbox org.apache.myfaces.custom.ajax.api.AjaxPhaseListener !! 051 * 052 */ 053 public class AjaxPhaseListener implements PhaseListener { 054 private static final Log LOG = LogFactory.getLog(AjaxPhaseListener.class); 055 public static final String AJAX_COMPONENT_ID = "affectedAjaxComponent"; 056 057 public static final String CODE_SUCCESS = "<status code=\"200\"/>"; 058 public static final String CODE_NOT_MODIFIED = "<status code=\"304\"/>"; 059 public static final String CODE_RELOAD_REQUIRED = "<status code=\"309\"/>"; 060 public static final String TOBAGO_AJAX_STATUS_CODE = "org.apache.myfaces.tobago.StatusCode"; 061 062 public static Object getValueForComponent( 063 FacesContext facesContext, UIComponent component) { 064 String possibleClientId = component.getClientId(facesContext); 065 066 final Map requestParameterMap 067 = facesContext.getExternalContext().getRequestParameterMap(); 068 if (requestParameterMap.containsKey(possibleClientId)) { 069 return requestParameterMap.get(possibleClientId); 070 } else { 071 possibleClientId = (String) requestParameterMap.get(AJAX_COMPONENT_ID); 072 073 UIViewRoot root = facesContext.getViewRoot(); 074 075 UIComponent ajaxComponent = root.findComponent(possibleClientId); 076 077 if (ajaxComponent == component) { 078 return requestParameterMap.get(possibleClientId); 079 } else { 080 LOG.error("No value found for this component : " + possibleClientId); 081 return null; 082 } 083 } 084 } 085 086 087 public void afterPhase(PhaseEvent event) { 088 089 if (event.getPhaseId().getOrdinal() != PhaseId.APPLY_REQUEST_VALUES.getOrdinal()) { 090 return; 091 } 092 093 FacesContext facesContext = event.getFacesContext(); 094 095 final ExternalContext externalContext = facesContext.getExternalContext(); 096 if (externalContext.getRequestParameterMap().containsKey(AJAX_COMPONENT_ID)) { 097 try { 098 if (LOG.isDebugEnabled()) { 099 LOG.debug("AJAX: componentID found :" 100 + externalContext.getRequestParameterMap().get(AJAX_COMPONENT_ID)); 101 } 102 103 RequestUtils.ensureEncoding(externalContext); 104 ResponseUtils.ensureNoCacheHeader(externalContext); 105 final UIViewRoot viewRoot = facesContext.getViewRoot(); 106 StringWriter content = new StringWriter(); 107 RenderKitFactory renderFactory = (RenderKitFactory) 108 FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); 109 RenderKit renderKit = renderFactory.getRenderKit( 110 facesContext, viewRoot.getRenderKitId()); 111 ResponseWriter contentWriter = renderKit.createResponseWriter(content, null, null); 112 facesContext.setResponseWriter(contentWriter); 113 114 AjaxUtils.processAjax(facesContext, viewRoot); 115 116 117 118 StringWriter jsfState = new StringWriter(); 119 ResponseWriter jsfStateWriter = contentWriter.cloneWithWriter(jsfState); 120 facesContext.setResponseWriter(jsfStateWriter); 121 122 final StateManager stateManager 123 = facesContext.getApplication().getStateManager(); 124 StateManager.SerializedView serializedView = stateManager.saveSerializedView(facesContext); 125 stateManager.writeState(facesContext, serializedView); 126 127 String stateValue = jsfState.toString(); 128 if (stateValue.length() > 0) { 129 // in case of inputSuggest jsfState.lenght is 0 130 // inputSuggest is a special case, because the form is not included in request. 131 contentWriter.startElement(HtmlConstants.SCRIPT, null); 132 contentWriter.writeAttribute(HtmlAttributes.TYPE, "text/javascript", null); 133 contentWriter.flush(); 134 contentWriter.write("Tobago.replaceJsfState(\""); 135 contentWriter.write(StringUtils.replace(StringUtils.replace(stateValue, "\"", "\\\""), "\n", "")); 136 contentWriter.write("\");"); 137 contentWriter.endElement(HtmlConstants.SCRIPT); 138 } 139 140 writeAjaxResponse(facesContext, content.toString()); 141 facesContext.responseComplete(); 142 143 } catch (IOException e) { 144 LOG.error("Exception while processing Ajax", e); 145 } 146 } 147 } 148 149 private void writeAjaxResponse(FacesContext facesContext, String content) 150 throws IOException { 151 152 ExternalContext externalContext = facesContext.getExternalContext(); 153 StringBuilder buf = new StringBuilder(content); 154 155 if (LOG.isDebugEnabled()) { 156 LOG.debug("Size of AjaxResponse:\n" + buf.length() 157 + " = 0x" + Integer.toHexString(buf.length())); 158 } 159 if (facesContext.getExternalContext().getRequestMap().containsKey(TOBAGO_AJAX_STATUS_CODE)) { 160 buf.insert(0, facesContext.getExternalContext().getRequestMap().get(TOBAGO_AJAX_STATUS_CODE)); 161 } else { 162 buf.insert(0, CODE_SUCCESS); 163 } 164 165 buf.insert(0, Integer.toHexString(buf.length()) + "\r\n"); 166 buf.append("\r\n" + 0 + "\r\n\r\n"); 167 168 //TODO: fix this to work in PortletRequest as well 169 if (externalContext.getResponse() instanceof HttpServletResponse) { 170 final HttpServletResponse httpServletResponse 171 = (HttpServletResponse) externalContext.getResponse(); 172 httpServletResponse.addHeader("Transfer-Encoding", "chunked"); 173 PrintWriter responseWriter = httpServletResponse.getWriter(); 174 // buf.delete(buf.indexOf("<"), buf.indexOf(">")+1); 175 responseWriter.print(buf.toString()); 176 responseWriter.flush(); 177 responseWriter.close(); 178 } 179 } 180 181 public void beforePhase(PhaseEvent event) { 182 183 if (event.getPhaseId().getOrdinal() != PhaseId.RENDER_RESPONSE.getOrdinal()) { 184 return; 185 } 186 187 try { 188 FacesContext facesContext = event.getFacesContext(); 189 final ExternalContext externalContext = facesContext.getExternalContext(); 190 final Map requestParameterMap = externalContext.getRequestParameterMap(); 191 if (requestParameterMap.containsKey(AJAX_COMPONENT_ID)) { 192 LOG.error("Ignoring AjaxRequest without valid component tree!"); 193 194 final String message = ResourceManagerUtil.getPropertyNotNull( 195 facesContext, "tobago", "tobago.ajax.response.error"); 196 197 writeAjaxResponse(facesContext, message); 198 199 facesContext.responseComplete(); 200 } 201 202 } catch (IOException e) { 203 LOG.error("Exception while processing Ajax", e); 204 } 205 } 206 207 public PhaseId getPhaseId() { 208 return PhaseId.ANY_PHASE; 209 //return PhaseId.RESTORE_VIEW; 210 // return PhaseId.INVOKE_APPLICATION; 211 // return PhaseId.APPLY_REQUEST_VALUES; 212 } 213 214 }