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 }