001 package org.apache.myfaces.tobago.component; 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.myfaces.tobago.context.ClientProperties; 021 import org.apache.myfaces.tobago.context.ResourceManagerImpl; 022 import org.apache.commons.logging.Log; 023 import org.apache.commons.logging.LogFactory; 024 025 import javax.faces.component.UIComponent; 026 import javax.faces.context.FacesContext; 027 import javax.faces.event.AbortProcessingException; 028 import javax.faces.event.FacesEvent; 029 import javax.faces.event.PhaseId; 030 import javax.faces.FacesException; 031 import java.util.ArrayList; 032 import java.util.List; 033 import java.util.ListIterator; 034 import java.util.Locale; 035 import java.util.Map; 036 037 /* 038 * User: weber 039 * Date: Jun 13, 2005 040 * Time: 5:19:31 PM 041 */ 042 public class UIViewRoot extends javax.faces.component.UIViewRoot { 043 044 private static final Log LOG = LogFactory.getLog(UIViewRoot.class); 045 private static final String EVENT_LIST_KEY = UIViewRoot.class.getName() + ".EventList"; 046 047 private ResourceManagerImpl.CacheKey rendererCacheKey; 048 049 private ClientProperties clientProperties; 050 051 public static final int ANY_PHASE_ORDINAL = PhaseId.ANY_PHASE.getOrdinal(); 052 053 054 /** 055 * <p>Create a new {@link UIViewRoot} instance with default property 056 * values.</p> 057 */ 058 public UIViewRoot() { 059 super(); 060 updateRendererCachePrefix(); 061 } 062 063 public ClientProperties getClientProperties() { 064 return clientProperties; 065 } 066 067 public void setClientProperties(final ClientProperties clientProperties) { 068 this.clientProperties = clientProperties; 069 updateRendererCachePrefix(); 070 } 071 072 public void setLocale(final Locale locale) { 073 super.setLocale(locale); 074 updateRendererCachePrefix(); 075 } 076 077 public ResourceManagerImpl.CacheKey getRendererCacheKey() { 078 return rendererCacheKey; 079 } 080 081 082 public void updateRendererCachePrefix() { 083 rendererCacheKey = ResourceManagerImpl.getRendererCacheKey( 084 clientProperties != null ? clientProperties.getId() : "null", getLocale()); 085 // LOG.info("updateRendererCachePrefix :" + rendererCachePrefix); 086 } 087 088 public void broadcastEventsForPhase(final FacesContext context, final PhaseId phaseId) { 089 broadcastForPhase(phaseId); 090 if (context.getRenderResponse() || context.getResponseComplete()) { 091 clearEvents(context); 092 } 093 } 094 095 // ----------------------------------------------------------------------------- 096 // ----------------------------------------------------------------------------- 097 // 098 // The following code is copied from myfaces implementation! 099 // In suns jsf-api 1.1.01 are the events not cleared if renderResponse is true 100 // after processUpdates, seems to be a bug. This is fixed at least in 101 // Nightly Snapshot from 15.08.2005, but not in stable yet. 102 // Events are private member of UIViewRoot, so we have to copy anny code 103 // accessing them. 104 // 105 // TODO: remove if fixed in stable release! 106 107 public void queueEvent(final FacesEvent event) { 108 if (event == null) { 109 throw new NullPointerException("event"); 110 } 111 getEvents(FacesContext.getCurrentInstance(), true).add(event); 112 } 113 114 115 private void broadcastForPhase(final PhaseId phaseId) { 116 List<FacesEvent> events = getEvents(FacesContext.getCurrentInstance(), false); 117 if (events == null) { 118 return; 119 } 120 121 boolean abort = false; 122 123 int phaseIdOrdinal = phaseId.getOrdinal(); 124 for (ListIterator listiterator = events.listIterator(); listiterator.hasNext();) { 125 FacesEvent event = (FacesEvent) listiterator.next(); 126 int ordinal = event.getPhaseId().getOrdinal(); 127 if (ordinal == ANY_PHASE_ORDINAL 128 || ordinal == phaseIdOrdinal) { 129 UIComponent source = event.getComponent(); 130 try { 131 source.broadcast(event); 132 } catch (FacesException e) { 133 Throwable fe = e; 134 while (fe != null) { 135 if (fe instanceof AbortProcessingException) { 136 if (LOG.isTraceEnabled()) { 137 LOG.trace("AbortProcessingException caught!"); 138 } 139 // abort event processing 140 // Page 3-30 of JSF 1.1 spec: "Throw an AbortProcessingException, to tell the JSF implementation 141 // that no further broadcast of this event, or any further events, should take place." 142 abort = true; 143 break; 144 } 145 fe = fe.getCause(); 146 } 147 if (!abort) { 148 throw e; 149 } else { 150 break; 151 } 152 } finally { 153 listiterator.remove(); 154 } 155 } 156 } 157 158 if (abort) { 159 // TODO: abort processing of any event of any phase or just of any event of the current phase??? 160 clearEvents(FacesContext.getCurrentInstance()); 161 } 162 } 163 164 165 private void clearEvents(final FacesContext context) { 166 context.getExternalContext().getRequestMap().remove(EVENT_LIST_KEY); 167 } 168 169 170 public void processDecodes(final FacesContext context) { 171 if (context == null) { 172 throw new NullPointerException("context"); 173 } 174 super.processDecodes(context); 175 broadcastForPhase(PhaseId.APPLY_REQUEST_VALUES); 176 if (context.getRenderResponse() || context.getResponseComplete()) { 177 clearEvents(context); 178 } 179 } 180 181 public void processValidators(final FacesContext context) { 182 if (context == null) { 183 throw new NullPointerException("context"); 184 } 185 super.processValidators(context); 186 broadcastForPhase(PhaseId.PROCESS_VALIDATIONS); 187 if (context.getRenderResponse() || context.getResponseComplete()) { 188 clearEvents(context); 189 } 190 } 191 192 public void processUpdates(final FacesContext context) { 193 if (context == null) { 194 throw new NullPointerException("context"); 195 } 196 super.processUpdates(context); 197 broadcastForPhase(PhaseId.UPDATE_MODEL_VALUES); 198 if (context.getRenderResponse() || context.getResponseComplete()) { 199 clearEvents(context); 200 } 201 } 202 203 public void processApplication(final FacesContext context) { 204 if (context == null) { 205 throw new NullPointerException("context"); 206 } 207 broadcastForPhase(PhaseId.INVOKE_APPLICATION); 208 if (context.getRenderResponse() || context.getResponseComplete()) { 209 clearEvents(context); 210 } 211 } 212 213 private List<FacesEvent> getEvents(final FacesContext context, boolean create) { 214 final Map requestMap = context.getExternalContext().getRequestMap(); 215 List<FacesEvent> events = (List<FacesEvent>) requestMap.get(EVENT_LIST_KEY); 216 if (events == null && create) { 217 events = new ArrayList<FacesEvent>(); 218 requestMap.put(EVENT_LIST_KEY, events); 219 } 220 return events; 221 } 222 }