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