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