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