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 }