2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shale.view.faces;
19  
20  import java.io.IOException;
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.Locale;
24  import java.util.Map;
25  
26  import javax.faces.FacesException;
27  import javax.faces.application.ViewHandler;
28  import javax.faces.component.UIViewRoot;
29  import javax.faces.context.FacesContext;
30  import javax.faces.el.EvaluationException;
31  import javax.faces.el.ValueBinding;
32  import javax.faces.el.VariableResolver;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.shale.util.Messages;
37  import org.apache.shale.view.Constants;
38  import org.apache.shale.view.ViewController;
39  import org.apache.shale.view.ViewControllerMapper;
40  
41  /***
42   * <p>{@link ViewViewHandler} is a custom implementation of <code>ViewHandler</code>
43   * that adds support for on-demand creation and configuration of {@link ViewController}
44   * instances, and other view related functionality.</p>
45   *
46   * $Id: ViewViewHandler.java 489926 2006-12-23 20:56:29Z craigmcc $
47   */
48  
49  public class ViewViewHandler extends ViewHandler {
50  
51  
52      // ------------------------------------------------------------- Constructor
53  
54  
55      /***
56       * <p>Create a {@link ViewViewHandler} instance that decorates the
57       * specified <code>ViewHandler</code> provided by the JSF runtime
58       * implementation.</p>
59       *
60       * @param original Original <code>ViewHandler</code> to be decorated
61       */
62      public ViewViewHandler(ViewHandler original) {
63          this.original = original;
64      }
65  
66  
67      // -------------------------------------------------------- Static Variables
68  
69  
70      /***
71       * <p>Log instance for this class.</p>
72       */
73      private static final Log log = LogFactory.getLog(ViewViewHandler.class);
74  
75  
76      /***
77       * <p>Message resources for this class.</p>
78       */
79      private static Messages messages =
80        new Messages("org.apache.shale.view.resources.Bundle",
81                     ViewViewHandler.class.getClassLoader());
82  
83  
84      // ------------------------------------------------------ Instance Variables
85  
86  
87      /***
88       * <p>Cached {@link ViewControllerMapper} we will use to translate
89       * view identifiers to the class name of a {@link ViewController}.</p>
90       */
91      private ViewControllerMapper mapper = null;
92  
93  
94      /***
95       * <p>The <code>ViewHandler</code> instance we are decorating.  All requests
96       * are delegated to this instance, before or after any special handling that
97       * is required.</p>
98       */
99      private ViewHandler original = null;
100 
101 
102     // ----------------------------------------------------- ViewHandler Methods
103 
104 
105     /*** {@inheritDoc} */
106     public Locale calculateLocale(FacesContext context) {
107         return original.calculateLocale(context);
108     }
109 
110 
111     /*** {@inheritDoc} */
112     public String calculateRenderKitId(FacesContext context) {
113         return original.calculateRenderKitId(context);
114     }
115 
116 
117     /***
118      * <p>After delegating to our original <code>ViewHandler</code>,
119      * create and initialize any {@link ViewController} associated with
120      * the specified view identifier.</p>
121      *
122      * @param context <code>FacesContext</code> for the current request
123      * @param viewId View identifier of the view to be created
124      */
125     public UIViewRoot createView(FacesContext context, String viewId) {
126         UIViewRoot view = original.createView(context, viewId);
127         setupViewController(context, view, viewId, false);
128         return view;
129     }
130 
131 
132     /*** {@inheritDoc} */
133     public String getActionURL(FacesContext context, String viewId) {
134         return original.getActionURL(context, viewId);
135     }
136 
137 
138     /*** {@inheritDoc} */
139     public String getResourceURL(FacesContext context, String path) {
140         return original.getResourceURL(context, path);
141     }
142 
143 
144     /*** {@inheritDoc} */
145     public void renderView(FacesContext context, UIViewRoot view)
146       throws IOException, FacesException {
147         original.renderView(context, view);
148     }
149 
150 
151     /***
152      * <p>After delegating to our original <code>ViewHandler</code>,
153      * create and initialize any {@link ViewController} associated with
154      * the specified view identifier.</p>
155      *
156      * @param context <code>FacesContext</code> for the current request
157      * @param viewId View identifier of the view to be restored
158      */
159     public UIViewRoot restoreView(FacesContext context, String viewId) {
160         UIViewRoot view = original.restoreView(context, viewId);
161         setupViewController(context, view, viewId, true);
162         return view;
163     }
164 
165 
166     /*** {@inheritDoc} */
167     public void writeState(FacesContext context) throws IOException {
168         original.writeState(context);
169     }
170 
171 
172     // --------------------------------------------------------- Private Methods
173 
174 
175     /***
176      * <p>Return the {@link ViewControllerMapper} instance we will use to
177      * map view identifiers to class names of the corresponding
178      * {@link ViewController} class.</p>
179      *
180      * @param context <code>FacesContext</code> for the current request
181      */
182     private ViewControllerMapper getViewControllerMapper(FacesContext context) {
183 
184         // DEPRECATED - respect the context init parameter if specified
185         if (mapper == null) {
186             mapper = getViewControllerMapperInstance(context);
187         }
188 
189         // DEPRECATED - return cached instance from context init parameter
190         if (mapper != null) {
191             return mapper;
192         }
193 
194         // Live lookup of the appropriate mapper as a managed bean
195         ValueBinding vb = context.getApplication().createValueBinding
196           ("#{" + Constants.VIEW_MAPPER + "}");
197         ViewControllerMapper vcm = (ViewControllerMapper) vb.getValue(context);
198         return vcm;
199 
200     }
201 
202 
203     /***
204      * <p>Create and return the custom configured {@link ViewControllerMapper}
205      * instance we will use for this application, or <code>null</code>
206      * if there is no such instance.</p>
207      *
208      * @param context <code>FacesContext</code> for the current request
209      *
210      * @deprecated As of version 1.0.3, replace the application scoped
211      *  managed bean at FacesConstants.VIEW_MAPPER instead of using the
212      *  deprecated context initialization parameter mentioned here
213      */
214     private ViewControllerMapper getViewControllerMapperInstance
215             (FacesContext context) {
216 
217         String className =
218           context.getExternalContext().getInitParameter(Constants.VIEW_CONTROLLER_MAPPER);
219         if (className == null) {
220             return null;
221         }
222         ClassLoader cl = Thread.currentThread().getContextClassLoader();
223         if (cl == null) {
224             cl = this.getClass().getClassLoader();
225         }
226         try {
227             Class clazz = cl.loadClass(className);
228             return (ViewControllerMapper) clazz.newInstance();
229         } catch (ClassCastException e) {
230             throw new FacesException
231               (messages.getMessage("view.vcmCast",
232                                    new Object[] { className }), e);
233         } catch (ClassNotFoundException e) {
234             throw new FacesException
235               (messages.getMessage("view.vcmClass",
236                                    new Object[] { className }), e);
237         } catch (IllegalAccessException e) {
238             throw new FacesException
239               (messages.getMessage("view.vcmAccess",
240                                    new Object[] { className }), e);
241         } catch (InstantiationException e) {
242             throw new FacesException
243               (messages.getMessage("view.vcmInstantiate",
244                                    new Object[] { className }), e);
245         }
246 
247     }
248 
249 
250     /***
251      * <p>Create and initialize an appropriate {@link ViewController} instance
252      * associated with the specified view, which was just created or just
253      * restored.</p>
254      *
255      * @param context <code>FacesContext</code> for the current request
256      * @param view <code>UIViewRoot</code> just created or restored
257      *  (or <code>null</code> if there was no such view)
258      * @param viewId of the <code>UIViewRoot</code> just created or
259      *  restored
260      * @param postBack <code>true</code> if this is a post back to
261      *  an existing view
262      */
263     private void setupViewController(FacesContext context, UIViewRoot view,
264                                      String viewId, boolean postBack) {
265 
266         // Is there actually a view for us to procses?
267         if (view == null) {
268             return;
269         }
270 
271         // Cache the postback flag so that it can be used inside
272         // the AbstractViewcontroller implementation
273         if (postBack) {
274             context.getExternalContext().getRequestMap().
275               put(FacesConstants.VIEW_POSTBACK, Boolean.TRUE);
276         }
277 
278         // Its not the responsibilty of createView method to set the viewId of the view
279         // (See JSF 1.1 spec. pg. 7-16)
280         //String viewId = view.getViewId();
281         if (log.isDebugEnabled()) {
282             log.debug("setupViewController(" + viewId + "," + postBack + ")");
283         }
284 
285         // Map our view identifier to a corresponding managed bean name
286         ViewControllerMapper viewControllerMapper = getViewControllerMapper(context);
287         if (viewControllerMapper == null) {
288             log.warn(messages.getMessage("view.noViewControllerMapper"));
289             return;
290         }
291         String viewName = viewControllerMapper.mapViewId(viewId);
292 
293         // Retrieve an existing instance, or one created and configured by
294         // the managed bean facility
295         Object vc = null;
296         VariableResolver vr =
297             context.getApplication().getVariableResolver();
298         try {
299             vc = vr.resolveVariable(context, viewName);
300             if (vc == null) {
301                 if (log.isDebugEnabled()) {
302                     log.debug(messages.getMessage("view.noViewController",
303                                                   new Object[] { viewId, viewName }));
304                 }
305                 // We are navigating to a page with no ViewControllerMapper
306                 // compatible managed bean defined.  Therefore, clear the
307                 // flag that would otherwise cause an originating ViewController's
308                 // prerender() method to be called
309                 context.getExternalContext().getRequestMap().remove
310                         (FacesConstants.VIEW_NAME_RENDERED);
311                 return;
312             }
313         } catch (EvaluationException e) {
314             log.warn(messages.getMessage("view.evalException",
315                                           new Object[] { viewId, viewName }), e);
316             return;
317         }
318 
319         // Set the postBack property on a ViewController instance
320         if (vc instanceof ViewController) {
321             ((ViewController) vc).setPostBack(postBack);
322         }
323 
324         // Schedule this instance for later processing as needed
325         Map map = context.getExternalContext().getRequestMap();
326         map.put(FacesConstants.VIEW_NAME_RENDERED, viewName);
327         List list = (List) map.get(FacesConstants.VIEWS_INITIALIZED);
328         if (list == null) {
329             list = new ArrayList();
330             map.put(FacesConstants.VIEWS_INITIALIZED, list);
331         }
332         list.add(vc);
333 
334     }
335 
336 
337     /***
338      * <p>Return the {@link ViewControllerCallbacks} instance we
339      * will use.</p>
340      *
341      * @param context <code>FacesContext</code> for the current request
342      *
343      * @since 1.0.1
344      */
345     private ViewControllerCallbacks getViewControllerCallbacks(FacesContext context) {
346 
347         ValueBinding vb = context.getApplication().createValueBinding
348           ("#{" + FacesConstants.VIEW_CALLBACKS + "}");
349         return (ViewControllerCallbacks) vb.getValue(context);
350 
351     }
352 
353 
354 }