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