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 }