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.util.FastStringWriter; 028 import org.apache.myfaces.tobago.renderkit.html.HtmlConstants; 029 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes; 030 031 import javax.faces.FactoryFinder; 032 import javax.faces.application.StateManager; 033 import javax.faces.component.UIComponent; 034 import javax.faces.component.UIViewRoot; 035 import javax.faces.context.ExternalContext; 036 import javax.faces.context.FacesContext; 037 import javax.faces.context.ResponseWriter; 038 import javax.faces.event.PhaseEvent; 039 import javax.faces.event.PhaseId; 040 import javax.faces.event.PhaseListener; 041 import javax.faces.render.RenderKit; 042 import javax.faces.render.RenderKitFactory; 043 import javax.servlet.http.HttpServletResponse; 044 import java.io.IOException; 045 import java.io.PrintWriter; 046 import java.util.Map; 047 048 /** 049 * !! adapted copy of sandbox org.apache.myfaces.custom.ajax.api.AjaxPhaseListener !! 050 */ 051 public class AjaxPhaseListener implements PhaseListener { 052 private static final Log LOG = LogFactory.getLog(AjaxPhaseListener.class); 053 public static final String AJAX_COMPONENT_ID = "affectedAjaxComponent"; 054 055 public static final String CODE_SUCCESS = "<status code=\"200\"/>"; 056 public static final String CODE_NOT_MODIFIED = "<status code=\"304\"/>"; 057 public static final String CODE_RELOAD_REQUIRED = "<status code=\"309\"/>"; 058 public static final String TOBAGO_AJAX_STATUS_CODE = "org.apache.myfaces.tobago.StatusCode"; 059 060 public static Object getValueForComponent( 061 FacesContext facesContext, UIComponent component) { 062 String possibleClientId = component.getClientId(facesContext); 063 064 final Map requestParameterMap 065 = facesContext.getExternalContext().getRequestParameterMap(); 066 if (requestParameterMap.containsKey(possibleClientId)) { 067 return requestParameterMap.get(possibleClientId); 068 } else { 069 possibleClientId = (String) requestParameterMap.get(AJAX_COMPONENT_ID); 070 071 UIViewRoot root = facesContext.getViewRoot(); 072 073 UIComponent ajaxComponent = root.findComponent(possibleClientId); 074 075 if (ajaxComponent == component) { 076 return requestParameterMap.get(possibleClientId); 077 } else { 078 LOG.error("No value found for this component : " + possibleClientId); 079 return null; 080 } 081 } 082 } 083 084 085 public void afterPhase(PhaseEvent event) { 086 087 if (event.getPhaseId().getOrdinal() != PhaseId.APPLY_REQUEST_VALUES.getOrdinal()) { 088 return; 089 } 090 091 FacesContext facesContext = event.getFacesContext(); 092 093 final ExternalContext externalContext = facesContext.getExternalContext(); 094 if (externalContext.getRequestParameterMap().containsKey(AJAX_COMPONENT_ID)) { 095 try { 096 if (LOG.isDebugEnabled()) { 097 LOG.debug("AJAX: componentID found :" 098 + externalContext.getRequestParameterMap().get(AJAX_COMPONENT_ID)); 099 } 100 101 RequestUtils.ensureEncoding(externalContext); 102 ResponseUtils.ensureNoCacheHeader(externalContext); 103 final UIViewRoot viewRoot = facesContext.getViewRoot(); 104 FastStringWriter content = new FastStringWriter(1024 * 10); 105 RenderKitFactory renderFactory = (RenderKitFactory) 106 FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); 107 RenderKit renderKit = renderFactory.getRenderKit( 108 facesContext, viewRoot.getRenderKitId()); 109 ResponseWriter contentWriter = renderKit.createResponseWriter(content, null, null); 110 facesContext.setResponseWriter(contentWriter); 111 112 AjaxUtils.processAjax(facesContext, viewRoot); 113 114 FastStringWriter jsfState = new FastStringWriter(); 115 ResponseWriter jsfStateWriter = contentWriter.cloneWithWriter(jsfState); 116 facesContext.setResponseWriter(jsfStateWriter); 117 118 final StateManager stateManager 119 = facesContext.getApplication().getStateManager(); 120 StateManager.SerializedView serializedView = stateManager.saveSerializedView(facesContext); 121 stateManager.writeState(facesContext, serializedView); 122 123 String stateValue = jsfState.toString(); 124 if (stateValue.length() > 0) { 125 // in case of inputSuggest jsfState.lenght is 0 126 // inputSuggest is a special case, because the form is not included in request. 127 contentWriter.startElement(HtmlConstants.SCRIPT, null); 128 contentWriter.writeAttribute(HtmlAttributes.TYPE, "text/javascript", null); 129 contentWriter.flush(); 130 contentWriter.write("Tobago.replaceJsfState(\""); 131 contentWriter.write(StringUtils.replace(StringUtils.replace(stateValue, "\"", "\\\""), "\n", "")); 132 contentWriter.write("\");"); 133 contentWriter.endElement(HtmlConstants.SCRIPT); 134 } 135 136 writeAjaxResponse(facesContext, content.toString()); 137 facesContext.responseComplete(); 138 139 } catch (IOException e) { 140 LOG.error("Exception while processing Ajax", e); 141 } 142 } 143 } 144 145 private void writeAjaxResponse(FacesContext facesContext, String content) 146 throws IOException { 147 148 ExternalContext externalContext = facesContext.getExternalContext(); 149 StringBuilder buf = new StringBuilder(content); 150 151 if (LOG.isDebugEnabled()) { 152 LOG.debug("Size of AjaxResponse:\n" + buf.length() 153 + " = 0x" + Integer.toHexString(buf.length())); 154 } 155 if (facesContext.getExternalContext().getRequestMap().containsKey(TOBAGO_AJAX_STATUS_CODE)) { 156 buf.insert(0, facesContext.getExternalContext().getRequestMap().get(TOBAGO_AJAX_STATUS_CODE)); 157 } else { 158 buf.insert(0, CODE_SUCCESS); 159 } 160 161 buf.insert(0, Integer.toHexString(buf.length()) + "\r\n"); 162 buf.append("\r\n" + 0 + "\r\n\r\n"); 163 164 //TODO: fix this to work in PortletRequest as well 165 if (externalContext.getResponse() instanceof HttpServletResponse) { 166 final HttpServletResponse httpServletResponse 167 = (HttpServletResponse) externalContext.getResponse(); 168 httpServletResponse.addHeader("Transfer-Encoding", "chunked"); 169 PrintWriter responseWriter = httpServletResponse.getWriter(); 170 // buf.delete(buf.indexOf("<"), buf.indexOf(">")+1); 171 responseWriter.print(buf.toString()); 172 responseWriter.flush(); 173 responseWriter.close(); 174 } 175 } 176 177 public void beforePhase(PhaseEvent event) { 178 179 if (event.getPhaseId().getOrdinal() != PhaseId.RENDER_RESPONSE.getOrdinal()) { 180 return; 181 } 182 183 try { 184 FacesContext facesContext = event.getFacesContext(); 185 final ExternalContext externalContext = facesContext.getExternalContext(); 186 final Map requestParameterMap = externalContext.getRequestParameterMap(); 187 if (requestParameterMap.containsKey(AJAX_COMPONENT_ID)) { 188 LOG.error("Ignoring AjaxRequest without valid component tree!"); 189 190 final String message = ResourceManagerUtil.getPropertyNotNull( 191 facesContext, "tobago", "tobago.ajax.response.error"); 192 193 writeAjaxResponse(facesContext, message); 194 195 facesContext.responseComplete(); 196 } 197 198 } catch (IOException e) { 199 LOG.error("Exception while processing Ajax", e); 200 } 201 } 202 203 public PhaseId getPhaseId() { 204 return PhaseId.ANY_PHASE; 205 //return PhaseId.RESTORE_VIEW; 206 // return PhaseId.INVOKE_APPLICATION; 207 // return PhaseId.APPLY_REQUEST_VALUES; 208 } 209 210 }