001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package org.apache.myfaces.tobago.ajax.api;
021
022 import org.apache.commons.logging.Log;
023 import org.apache.commons.logging.LogFactory;
024 import org.apache.myfaces.tobago.component.ComponentUtil;
025 import org.apache.myfaces.tobago.component.UIViewRoot;
026 import org.apache.myfaces.tobago.util.ResponseUtils;
027
028 import javax.faces.FactoryFinder;
029 import javax.faces.application.Application;
030 import javax.faces.application.ViewHandler;
031 import javax.faces.component.UIComponent;
032 import javax.faces.context.FacesContext;
033 import javax.faces.context.ResponseWriter;
034 import javax.faces.event.PhaseId;
035 import javax.faces.render.RenderKit;
036 import javax.faces.render.RenderKitFactory;
037 import javax.faces.render.Renderer;
038 import javax.servlet.http.HttpServletResponse;
039 import java.io.IOException;
040 import java.io.PrintWriter;
041 import java.util.Collections;
042 import java.util.HashMap;
043 import java.util.HashSet;
044 import java.util.Iterator;
045 import java.util.Map;
046 import java.util.Set;
047 import java.util.StringTokenizer;
048
049 public class AjaxUtils {
050
051 private static final Log LOG = LogFactory.getLog(AjaxUtils.class);
052
053 public static final String AJAX_COMPONENTS = AjaxUtils.class.getName() + ".AJAX_COMPONENTS";
054
055 public static boolean isAjaxRequest(FacesContext facesContext) {
056 Map parameterMap = facesContext.getExternalContext().getRequestParameterMap();
057 String ajaxComponentIds = (String) parameterMap.get(AjaxPhaseListener.AJAX_COMPONENT_ID);
058 return ajaxComponentIds != null;
059 }
060
061 public static void checkParamValidity(FacesContext facesContext, UIComponent uiComponent, Class compClass) {
062 if (facesContext == null) {
063 throw new NullPointerException("facesContext may not be null");
064 }
065 if (uiComponent == null) {
066 throw new NullPointerException("uiComponent may not be null");
067 }
068 //if (compClass != null && !(compClass.isAssignableFrom(uiComponent.getClass())))
069 // why isAssignableFrom with additional getClass method call if isInstance does the same?
070 if (compClass != null && !(compClass.isInstance(uiComponent))) {
071 throw new IllegalArgumentException("uiComponent : "
072 + uiComponent.getClass().getName() + " is not instance of "
073 + compClass.getName() + " as it should be");
074 }
075 }
076
077 public static void encodeAjaxComponent(FacesContext facesContext, UIComponent component) throws IOException {
078 if (facesContext == null) {
079 throw new NullPointerException("facesContext");
080 }
081 if (!component.isRendered()) {
082 return;
083 }
084 Renderer renderer = ComponentUtil.getRenderer(facesContext, component);
085 if (renderer != null && renderer instanceof AjaxRenderer) {
086 ((AjaxRenderer) renderer).encodeAjax(facesContext, component);
087 }
088 }
089
090 public static void processAjax(FacesContext facesContext, UIComponent component)
091 throws IOException {
092 if (component instanceof AjaxComponent) {
093 ((AjaxComponent) component).processAjax(facesContext);
094 } else {
095 processAjaxOnChildren(facesContext, component);
096 }
097 }
098
099 public static void processActiveAjaxComponent(FacesContext facesContext,
100 UIComponent component)
101 throws IOException {
102
103 if (component instanceof AjaxComponent) {
104 final UIViewRoot viewRoot = (UIViewRoot) facesContext.getViewRoot();
105
106 // TODO: handle phaseListeners ??
107
108 if (!facesContext.getRenderResponse()) {
109 component.processValidators(facesContext);
110 viewRoot.broadcastEventsForPhase(facesContext, PhaseId.PROCESS_VALIDATIONS);
111 } else if (LOG.isDebugEnabled()) {
112 LOG.debug("Skipping validate");
113 }
114
115 if (!facesContext.getRenderResponse()) {
116 component.processUpdates(facesContext);
117 viewRoot.broadcastEventsForPhase(facesContext, PhaseId.UPDATE_MODEL_VALUES);
118 } else if (LOG.isDebugEnabled()) {
119 LOG.debug("Skipping updates");
120 }
121
122 if (!facesContext.getRenderResponse()) {
123 viewRoot.processApplication(facesContext);
124 } else if (LOG.isDebugEnabled()) {
125 LOG.debug("Skipping application");
126 }
127
128 ((AjaxComponent) component).encodeAjax(facesContext);
129 } else {
130 LOG.error("Can't process non AjaxComponent : \""
131 + component.getClientId(facesContext) + "\" = "
132 + component.getClass().getName());
133 }
134 }
135
136 public static void processAjaxOnChildren(FacesContext facesContext,
137 UIComponent component) throws IOException {
138
139 final Iterator<UIComponent> facetsAndChildren = component.getFacetsAndChildren();
140 while (facetsAndChildren.hasNext() && !facesContext.getResponseComplete()) {
141 AjaxUtils.processAjax(facesContext, facetsAndChildren.next());
142 }
143 }
144
145 public static Set<String> getRequestPartialIds(FacesContext facesContext) {
146 Map parameterMap = facesContext.getExternalContext().getRequestParameterMap();
147 String ajaxComponentIds = (String) parameterMap.get(AjaxPhaseListener.AJAX_COMPONENT_ID);
148 if (ajaxComponentIds != null) {
149 StringTokenizer tokenizer = new StringTokenizer(ajaxComponentIds, ",");
150 Set<String> ajaxComponents = new HashSet<String>(tokenizer.countTokens());
151 while (tokenizer.hasMoreTokens()) {
152 String ajaxId = tokenizer.nextToken();
153 ajaxComponents.add(ajaxId);
154 }
155 return ajaxComponents;
156 }
157 return Collections.EMPTY_SET;
158 }
159
160 public static Map<String, UIComponent> parseAndStoreComponents(FacesContext facesContext) {
161 Map parameterMap = facesContext.getExternalContext().getRequestParameterMap();
162 String ajaxComponentIds = (String) parameterMap.get(AjaxPhaseListener.AJAX_COMPONENT_ID);
163 if (ajaxComponentIds != null) {
164 if (LOG.isDebugEnabled()) {
165 LOG.debug("ajaxComponentIds = \"" + ajaxComponentIds + "\"");
166 }
167 StringTokenizer tokenizer = new StringTokenizer(ajaxComponentIds, ",");
168 Map<String, UIComponent> ajaxComponents = new HashMap<String, UIComponent>(tokenizer.countTokens());
169 //noinspection unchecked
170 facesContext.getExternalContext().getRequestMap().put(AJAX_COMPONENTS, ajaxComponents);
171 javax.faces.component.UIViewRoot viewRoot = facesContext.getViewRoot();
172 while (tokenizer.hasMoreTokens()) {
173 String ajaxId = tokenizer.nextToken();
174 UIComponent ajaxComponent = viewRoot.findComponent(ajaxId);
175 if (ajaxComponent != null) {
176 if (LOG.isDebugEnabled()) {
177 LOG.debug("ajaxComponent for \"" + ajaxId + "\" = \"" + ajaxComponent + "\"");
178 }
179 ajaxComponents.put(ajaxId, ajaxComponent);
180 }
181 }
182 return ajaxComponents;
183 }
184 return null;
185 }
186
187 public static Map<String, UIComponent> getAjaxComponents(FacesContext facesContext) {
188 //noinspection unchecked
189 return (Map<String, UIComponent>)
190 facesContext.getExternalContext().getRequestMap().get(AJAX_COMPONENTS);
191 }
192
193 public static void ensureDecoded(FacesContext facesContext, String clientId) {
194 ensureDecoded(facesContext, facesContext.getViewRoot().findComponent(clientId));
195 }
196
197 public static void ensureDecoded(FacesContext facesContext, UIComponent component) {
198 if (component == null) {
199 LOG.warn("Ignore AjaxComponent: null");
200 return;
201 }
202 Map<String, UIComponent> ajaxComponents = getAjaxComponents(facesContext);
203 if (ajaxComponents != null) {
204 for (UIComponent uiComponent : ajaxComponents.values()) {
205 // is component or a parent of it in the list?
206 UIComponent parent = component;
207 while (parent != null) {
208 if (component == uiComponent) {
209 // nothing to do, because it was already decoded (in the list)
210 return;
211 }
212 parent = parent.getParent();
213 }
214 }
215 component.processDecodes(facesContext);
216 }
217 }
218
219 public static boolean redirect(FacesContext facesContext, String url) throws IOException {
220 if (!isAjaxRequest(facesContext)) {
221 return false;
222 }
223 ResponseWriter writer = facesContext.getResponseWriter();
224 if (writer == null) {
225 RenderKit renderKit = facesContext.getRenderKit();
226 if (renderKit == null) {
227 RenderKitFactory renderFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
228 Application application = facesContext.getApplication();
229 ViewHandler applicationViewHandler = application.getViewHandler();
230 String renderKitId = applicationViewHandler.calculateRenderKitId(facesContext);
231 renderKit = renderFactory.getRenderKit(facesContext, renderKitId);
232 }
233 writer = renderKit.createResponseWriter(((HttpServletResponse)
234 facesContext.getExternalContext().getResponse()).getWriter(), null, null);
235 }
236 ResponseUtils.ensureNoCacheHeader(facesContext);
237 writer.startElement("redirect", null);
238 writer.writeAttribute("url", url, null);
239 writer.endElement("redirect");
240 writer.flush();
241 facesContext.responseComplete();
242 return true;
243 }
244
245 public static void redirect(HttpServletResponse response, String url) throws IOException {
246 PrintWriter out = response.getWriter();
247 out.print("<redirect url=");
248 out.print(url);
249 out.println("</redirect>");
250 }
251 }