View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.component;
21  
22  import org.apache.myfaces.tobago.ajax.AjaxUtils;
23  import org.apache.myfaces.tobago.context.ClientProperties;
24  import org.apache.myfaces.tobago.internal.ajax.AjaxInternalUtils;
25  import org.apache.myfaces.tobago.internal.ajax.AjaxResponseRenderer;
26  import org.apache.myfaces.tobago.internal.component.AbstractUIPage;
27  import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
28  import org.apache.myfaces.tobago.util.ApplyRequestValuesCallback;
29  import org.apache.myfaces.tobago.util.ComponentUtils;
30  import org.apache.myfaces.tobago.util.FacesVersion;
31  import org.apache.myfaces.tobago.util.ProcessValidationsCallback;
32  import org.apache.myfaces.tobago.util.TobagoCallback;
33  import org.apache.myfaces.tobago.util.UpdateModelValuesCallback;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  import javax.faces.FacesException;
38  import javax.faces.component.ContextCallback;
39  import javax.faces.component.UIComponent;
40  import javax.faces.context.FacesContext;
41  import javax.faces.event.AbortProcessingException;
42  import javax.faces.event.FacesEvent;
43  import javax.faces.event.PhaseId;
44  import java.io.IOException;
45  import java.util.ArrayList;
46  import java.util.ConcurrentModificationException;
47  import java.util.List;
48  import java.util.ListIterator;
49  import java.util.Locale;
50  import java.util.Map;
51  
52  /**
53   * @deprecated since 2.0.0
54   */
55  @Deprecated
56  public class UIViewRoot extends javax.faces.component.UIViewRoot {
57  
58    private static final Logger LOG = LoggerFactory.getLogger(UIViewRoot.class);
59  
60    private static final TobagoCallback APPLY_REQUEST_VALUES_CALLBACK = new ApplyRequestValuesCallback();
61    private static final ContextCallback PROCESS_VALIDATION_CALLBACK = new ProcessValidationsCallback();
62    private static final ContextCallback UPDATE_MODEL_VALUES_CALLBACK = new UpdateModelValuesCallback();
63  
64    private List<FacesEvent> events;
65  
66  // XXX check if TOBAGO-811 is still an issue
67  //  private int nextUniqueId;
68  
69    @Override
70    public void setLocale(final Locale locale) {
71      super.setLocale(locale);
72      final ClientProperties clientProperties = ClientProperties.getInstance(getFacesContext());
73      clientProperties.setLocale(locale);
74      clientProperties.updateUserAgent(getFacesContext());
75    }
76  
77  /*
78  // XXX check if TOBAGO-811 is still an issue
79  
80    @Override
81    public Object saveState(FacesContext facesContext) {
82      if (FacesVersion.supports12()) {
83        return super.saveState(facesContext);
84      } else {
85        Object[] state = new Object[2];
86        state[0] = super.saveState(facesContext);
87        state[1] = nextUniqueId;
88        return state;
89      }
90    }
91  
92    @Override
93    public void restoreState(FacesContext facesContext, Object o) {
94      if (FacesVersion.supports12()) {
95        super.restoreState(facesContext, o);
96      } else {
97        Object[] state = (Object[]) o;
98        super.restoreState(facesContext, state[0]);
99        nextUniqueId = (Integer) state[1];
100     }
101   }
102 
103   @Override
104   public String createUniqueId() {
105     if (FacesVersion.supports12()) {
106       return super.createUniqueId();
107     } else {
108       ExternalContext extCtx = FacesContext.getCurrentInstance().getExternalContext();
109       return extCtx.encodeNamespace(UNIQUE_ID_PREFIX + nextUniqueId++);
110     }
111   }
112 */
113 
114   // XXX begin of JSF 2.0 like code
115 
116   public void broadcastEventsForPhase(final FacesContext context, final PhaseId phaseId) {
117     broadcastForPhase(phaseId);
118     if (context.getRenderResponse() || context.getResponseComplete()) {
119       clearEvents();
120     }
121   }
122   
123 // -----------------------------------------------------------------------------
124 // -----------------------------------------------------------------------------
125 //
126 //  The following code is copied from myfaces implementation!
127 //  In suns jsf-api 1.1.01 are the events not cleared if renderResponse is true
128 //  after processUpdates, seems to be a bug. This is fixed at least in
129 //  Nightly Snapshot from 15.08.2005, but not in stable yet.
130 //  Events are private member of UIViewRoot, so we have to copy anny code
131 //  accessing them.
132 //
133   // TODO: remove if fixed in stable release! In 1.1_02 this seems to be fixed.
134 
135   public void queueEvent(final FacesEvent event) {
136     if (event == null) {
137       throw new NullPointerException("event");
138     }
139     if (events == null) {
140       events = new ArrayList<FacesEvent>();
141     }
142     events.add(event);
143   }
144 
145 
146   private void broadcastForPhase(final PhaseId phaseId) {
147     if (events == null) {
148       return;
149     }
150 
151     boolean abort = false;
152 
153     final int phaseIdOrdinal = phaseId.getOrdinal();
154     for (final ListIterator<FacesEvent> listiterator = events.listIterator(); listiterator.hasNext();) {
155       final FacesEvent event = listiterator.next();
156       final int ordinal = event.getPhaseId().getOrdinal();
157       if (ordinal == PhaseId.ANY_PHASE.getOrdinal() || ordinal == phaseIdOrdinal) {
158         final UIComponent source = event.getComponent();
159         try {
160           source.broadcast(event);
161         } catch (final FacesException e) {
162           Throwable fe = e;
163           while (fe != null) {
164             if (fe instanceof AbortProcessingException) {
165               if (LOG.isTraceEnabled()) {
166                 LOG.trace("AbortProcessingException caught!");
167               }
168               // abort event processing
169               // Page 3-30 of JSF 1.1 spec: "Throw an AbortProcessingException, to tell the JSF implementation
170               //  that no further broadcast of this event, or any further events, should take place."
171               abort = true;
172               break;
173             }
174             fe = fe.getCause();
175           }
176           if (!abort) {
177             throw e;
178           } else {
179             break;
180           }
181         } finally {
182 
183           try {
184             listiterator.remove();
185           } catch (final ConcurrentModificationException cme) {
186             final int eventIndex = listiterator.previousIndex();
187             events.remove(eventIndex);
188             //listiterator = events.listIterator();
189           }
190         }
191       }
192     }
193 
194     if (abort) {
195       // TODO: abort processing of any event of any phase or just of any event of the current phase???
196       clearEvents();
197     }
198   }
199 
200 
201   private void clearEvents() {
202     events = null;
203   }
204 
205 
206   @Override
207   public void processDecodes(final FacesContext context) {
208     if (context == null) {
209       throw new NullPointerException("context");
210     }
211     final Map<String, UIComponent> ajaxComponents = AjaxInternalUtils.parseAndStoreComponents(context);
212     if (ajaxComponents != null) {
213       // first decode the page
214       final AbstractUIPage page = ComponentUtils.findPage(context);
215       page.decode(context);
216       page.markSubmittedForm(context);
217       FacesContextUtils.setAjax(context, true);
218 
219       // decode the action if actionComponent not inside one of the ajaxComponents
220       // otherwise it is decoded there
221       decodeActionComponent(context, page, ajaxComponents);
222 
223       // and all ajax components
224       for (final Map.Entry<String, UIComponent> entry : ajaxComponents.entrySet()) {
225         FacesContextUtils.setAjaxComponentId(context, entry.getKey());
226         invokeOnComponent(context, entry.getKey(), APPLY_REQUEST_VALUES_CALLBACK);
227       }
228     } else {
229       super.processDecodes(context);
230     }
231     broadcastForPhase(PhaseId.APPLY_REQUEST_VALUES);
232     if (context.getRenderResponse() || context.getResponseComplete()) {
233       clearEvents();
234     }
235   }
236 
237   private void decodeActionComponent(final FacesContext facesContext, final AbstractUIPage page, final Map<String,
238       UIComponent> ajaxComponents) {
239     final String actionId = page.getActionId();
240     UIComponent actionComponent = null;
241     if (actionId != null) {
242       actionComponent = findComponent(actionId);
243       if (actionComponent == null && FacesVersion.supports20() && FacesVersion.isMyfaces()) {
244         final String bugActionId = actionId.replaceAll(":\\d+:", ":");
245         try {
246           actionComponent = findComponent(bugActionId);
247           //LOG.info("command = \"" + actionComponent + "\"", new Exception());
248         } catch (final Exception e) {
249           // ignore
250         }
251       }
252     }
253     if (actionComponent == null) {
254       return;
255     }
256     for (final UIComponent ajaxComponent : ajaxComponents.values()) {
257       UIComponent component = actionComponent;
258       while (component != null) {
259         if (component == ajaxComponent) {
260           return;
261         }
262         component = component.getParent();
263       }
264     }
265     invokeOnComponent(facesContext, actionId, APPLY_REQUEST_VALUES_CALLBACK);
266   }
267 
268 
269   @Override
270   public void processValidators(final FacesContext context) {
271     if (context == null) {
272       throw new NullPointerException("context");
273     }
274 
275     final Map<String, UIComponent> ajaxComponents = AjaxInternalUtils.getAjaxComponents(context);
276     if (ajaxComponents != null) {
277       for (final Map.Entry<String, UIComponent> entry : ajaxComponents.entrySet()) {
278         FacesContextUtils.setAjaxComponentId(context, entry.getKey());
279         invokeOnComponent(context, entry.getKey(), PROCESS_VALIDATION_CALLBACK);
280       }
281     } else {
282       super.processValidators(context);
283     }
284     broadcastForPhase(PhaseId.PROCESS_VALIDATIONS);
285     if (context.getRenderResponse() || context.getResponseComplete()) {
286       clearEvents();
287     }
288   }
289 
290   @Override
291   public void processUpdates(final FacesContext context) {
292     if (context == null) {
293       throw new NullPointerException("context");
294     }
295     final Map<String, UIComponent> ajaxComponents = AjaxInternalUtils.getAjaxComponents(context);
296     if (ajaxComponents != null) {
297       for (final Map.Entry<String, UIComponent> entry : ajaxComponents.entrySet()) {
298         invokeOnComponent(context, entry.getKey(), UPDATE_MODEL_VALUES_CALLBACK);
299       }
300     } else {
301       super.processUpdates(context);
302     }
303     broadcastForPhase(PhaseId.UPDATE_MODEL_VALUES);
304     if (context.getRenderResponse() || context.getResponseComplete()) {
305       clearEvents();
306     }
307   }
308 
309   @Override
310   public void processApplication(final FacesContext context) {
311     if (context == null) {
312       throw new NullPointerException("context");
313     }
314     broadcastForPhase(PhaseId.INVOKE_APPLICATION);
315     if (context.getRenderResponse() || context.getResponseComplete()) {
316       clearEvents();
317     }
318   }
319 
320   // XXX end of JSF 2.0 like code
321 
322   @Override
323   public boolean getRendersChildren() {
324     if (AjaxUtils.isAjaxRequest(FacesContext.getCurrentInstance())) {
325       return true;
326     } else {
327       return super.getRendersChildren();
328     }
329   }
330 
331   @Override
332   public void encodeChildren(final FacesContext context) throws IOException {
333     if (AjaxUtils.isAjaxRequest(context)) {
334       new AjaxResponseRenderer().renderResponse(context);
335 
336     } else {
337       super.encodeChildren(context);
338     }
339   }
340 }