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 }