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