KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > icesoft > faces > facelets > D2DFaceletViewHandler


1 /*
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * "The contents of this file are subject to the Mozilla Public License
5  * Version 1.1 (the "License"); you may not use this file except in
6  * compliance with the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS"
10  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
11  * License for the specific language governing rights and limitations under
12  * the License.
13  *
14  * The Original Code is ICEfaces 1.5 open source software code, released
15  * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
16  * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
17  * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
18  *
19  * Contributor(s): _____________________.
20  *
21  * Alternatively, the contents of this file may be used under the terms of
22  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
23  * License), in which case the provisions of the LGPL License are
24  * applicable instead of those above. If you wish to allow use of your
25  * version of this file only under the terms of the LGPL License and not to
26  * allow others to use your version of this file under the MPL, indicate
27  * your decision by deleting the provisions above and replace them with
28  * the notice and other provisions required by the LGPL License. If you do
29  * not delete the provisions above, a recipient may use your version of
30  * this file under either the MPL or the LGPL License."
31  *
32  */

33
34 package com.icesoft.faces.facelets;
35
36 import com.icesoft.faces.application.D2DViewHandler;
37 import com.icesoft.faces.context.BridgeFacesContext;
38 import com.sun.facelets.Facelet;
39 import com.sun.facelets.FaceletFactory;
40 import com.sun.facelets.compiler.Compiler;
41 import com.sun.facelets.compiler.SAXCompiler;
42 import com.sun.facelets.compiler.TagLibraryConfig;
43 import com.sun.facelets.impl.DefaultFaceletFactory;
44 import com.sun.facelets.impl.DefaultResourceResolver;
45 import com.sun.facelets.impl.ResourceResolver;
46 import com.sun.facelets.tag.TagDecorator;
47 import com.sun.facelets.tag.TagLibrary;
48 import com.sun.facelets.tag.jsf.ComponentSupport;
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
51
52 import javax.faces.FacesException;
53 import javax.faces.application.ViewHandler;
54 import javax.faces.component.UIComponent;
55 import javax.faces.component.UIViewRoot;
56 import javax.faces.context.ExternalContext;
57 import javax.faces.context.FacesContext;
58 import javax.faces.context.ResponseWriter;
59 import java.io.FileNotFoundException JavaDoc;
60 import java.io.IOException JavaDoc;
61 import java.net.URL JavaDoc;
62 import java.util.HashMap JavaDoc;
63 import java.util.Iterator JavaDoc;
64 import java.util.ArrayList JavaDoc;
65
66 /**
67  * <B>D2DViewHandler</B> is the ICEfaces Facelet ViewHandler implementation
68  *
69  * @see javax.faces.application.ViewHandler
70  */

71 public class D2DFaceletViewHandler extends D2DViewHandler {
72
73     //Facelets parameter constants
74
public final static long DEFAULT_REFRESH_PERIOD = 2;
75     public final static String JavaDoc PARAM_REFRESH_PERIOD = "facelets.REFRESH_PERIOD";
76     public final static String JavaDoc PARAM_SKIP_COMMENTS = "facelets.SKIP_COMMENTS";
77     public final static String JavaDoc PARAM_VIEW_MAPPINGS = "facelets.VIEW_MAPPINGS";
78     public final static String JavaDoc PARAM_LIBRARIES = "facelets.LIBRARIES";
79     public final static String JavaDoc PARAM_DECORATORS = "facelets.DECORATORS";
80     public final static String JavaDoc PARAM_RESOURCE_RESOLVER =
81             "facelets.RESOURCE_RESOLVER";
82
83     // Log instance for this class
84
private static Log log = LogFactory.getLog(D2DFaceletViewHandler.class);
85
86     protected FaceletFactory faceletFactory;
87
88     public D2DFaceletViewHandler() {
89     }
90
91     public D2DFaceletViewHandler(ViewHandler delegate) {
92         super(delegate);
93     }
94
95     protected void faceletInitialize() {
96         try {
97             if (faceletFactory == null) {
98                 com.sun.facelets.compiler.Compiler c = new SAXCompiler();
99                 initializeCompiler(c);
100                 faceletFactory = createFaceletFactory(c);
101             }
102         }
103         catch (Throwable JavaDoc t) {
104             if (log.isErrorEnabled()) {
105                 log.error("Failed initializing facelet instance", t);
106             }
107         }
108     }
109
110
111     protected void initializeCompiler(Compiler JavaDoc c) {
112         FacesContext ctx = FacesContext.getCurrentInstance();
113         ExternalContext ext = ctx.getExternalContext();
114
115         // Use a TagLibrary to create UIXhtmlComponents from all xhtml Tags
116
c.addTagLibrary(new UIXhtmlTagLibrary());
117         c.addTagDecorator(new UIXhtmlTagDecorator());
118         
119         c.addTagDecorator(new JspTagDetector());
120
121         // Load libraries
122
String JavaDoc paramLibraries = ext.getInitParameter(PARAM_LIBRARIES);
123         if (paramLibraries != null) {
124             paramLibraries = paramLibraries.trim();
125             String JavaDoc[] paramLibrariesArray = paramLibraries.split(";");
126             for (int i = 0; i < paramLibrariesArray.length; i++) {
127                 try {
128                     URL JavaDoc url = ext.getResource(paramLibrariesArray[i]);
129                     if (url == null) {
130                         throw new FileNotFoundException JavaDoc(paramLibrariesArray[i]);
131                     }
132                     TagLibrary tagLibrary = TagLibraryConfig.create(url);
133                     c.addTagLibrary(tagLibrary);
134                     if (log.isDebugEnabled()) {
135                         log.debug("Loaded library: " + paramLibrariesArray[i]);
136                     }
137                 }
138                 catch (IOException JavaDoc e) {
139                     if (log.isWarnEnabled()) {
140                         log.warn("Problem loading library: " + paramLibrariesArray[i], e);
141                     }
142                 }
143             }
144         }
145
146         // Load decorators
147
String JavaDoc paramDecorators = ext.getInitParameter(PARAM_DECORATORS);
148         if (paramDecorators != null) {
149             paramDecorators = paramDecorators.trim();
150             String JavaDoc[] paramDecoratorsArray = paramDecorators.split(";");
151             for (int i = 0; i < paramDecoratorsArray.length; i++) {
152                 try {
153                     Class JavaDoc tagDecoratorClass = Class.forName(paramDecoratorsArray[i]);
154                     TagDecorator tagDecorator = (TagDecorator)
155                             tagDecoratorClass.newInstance();
156                     c.addTagDecorator(tagDecorator);
157                     if (log.isDebugEnabled()) {
158                         log.debug("Loaded decorator: " +
159                                 paramDecoratorsArray[i]);
160                     }
161                 }
162                 catch (Exception JavaDoc e) {
163                     if (log.isWarnEnabled()) {
164                         log.warn("Problem loading decorator: " +
165                                 paramDecoratorsArray[i], e);
166                     }
167                 }
168             }
169         }
170
171         // Load whether to skip comments or not. For our hierarchial
172
// UIComponent tree, we have to throw away most useless text nodes,
173
// so this is a bit redundant getting the parameter. But who knows,
174
// things might change later, so best to preserve this code.
175
String JavaDoc paramSkipComments =
176                 ext.getInitParameter(PARAM_SKIP_COMMENTS);
177         // Default is true. I think this behaviour has changed over time
178
// is stock Facelets builds from 1.0.x to 1.1.x
179
if (paramSkipComments != null && paramSkipComments.equals("false")) {
180             c.setTrimmingComments(false);
181         }
182
183         // This has to be true, otherwise table or other container
184
// UIComponents will have text children, when they're
185
// expecting only real UIComponents
186
c.setTrimmingWhitespace(true);
187         c.setTrimmingComments(true);
188         c.setTrimmingXmlDeclarations(true);
189         c.setTrimmingDoctypeDeclarations(true);
190     }
191
192     protected FaceletFactory createFaceletFactory(Compiler JavaDoc c) {
193         long refreshPeriod = DEFAULT_REFRESH_PERIOD;
194         FacesContext ctx = FacesContext.getCurrentInstance();
195         String JavaDoc paramRefreshPeriod = ctx.getExternalContext().getInitParameter(
196                 PARAM_REFRESH_PERIOD);
197         if (paramRefreshPeriod != null && paramRefreshPeriod.length() > 0) {
198             try {
199                 refreshPeriod = Long.parseLong(paramRefreshPeriod);
200             }
201             catch (NumberFormatException JavaDoc nfe) {
202                 if (log.isWarnEnabled()) {
203                     log.warn("Problem parsing refresh period: " +
204                             paramRefreshPeriod, nfe);
205                 }
206             }
207         }
208
209         ResourceResolver resourceResolver = null;
210         String JavaDoc paramResourceResolver = ctx.getExternalContext().getInitParameter(
211                 PARAM_RESOURCE_RESOLVER);
212         if (paramResourceResolver != null && paramResourceResolver.length() > 0) {
213             try {
214                 Class JavaDoc resourceResolverClass = Class.forName(
215                         paramResourceResolver,
216                         true,
217                         Thread.currentThread().getContextClassLoader());
218                 resourceResolver = (ResourceResolver)
219                         resourceResolverClass.newInstance();
220             }
221             catch (Exception JavaDoc e) {
222                 throw new FacesException("Problem initializing ResourceResolver: " +
223                         paramResourceResolver, e);
224             }
225         }
226         if (resourceResolver == null)
227             resourceResolver = new DefaultResourceResolver();
228         
229         resourceResolver = preChainResourceResolver(resourceResolver);
230         
231         return new DefaultFaceletFactory(c, resourceResolver, refreshPeriod);
232     }
233     
234     /**
235      * When D2DFaceletViewHandler is setting up the ResourceResolver for
236      * Facelets, it uses this callback to allow for any subclass to
237      * define a ResourceResolver of higher precedence, that would have
238      * first crack at resolving resources, and then could delegate to
239      * the standard mechanism.
240      *
241      * @param after The standard ResourceResolver that Facelets would ordinarily use
242      * @return Either the new pre-chained ResourceResolver if one is being added,
243      * or just the given one if nothing is being chained in
244      */

245     protected ResourceResolver preChainResourceResolver(ResourceResolver after) {
246         return after;
247     }
248
249
250     protected String JavaDoc getRenderedViewId(FacesContext context, String JavaDoc actionId) {
251         ExternalContext extCtx = context.getExternalContext();
252         String JavaDoc viewId = actionId;
253         if (extCtx.getRequestPathInfo() == null) {
254             String JavaDoc facesSuffix = actionId.substring(actionId.lastIndexOf('.'));
255             String JavaDoc viewSuffix = context.getExternalContext()
256                     .getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
257             viewId = actionId.replaceFirst(facesSuffix, viewSuffix);
258         }
259         return viewId;
260     }
261
262
263     protected void renderResponse(FacesContext facesContext) throws IOException JavaDoc {
264         if (log.isTraceEnabled()) {
265             log.trace("renderResponse(FC)");
266         }
267         BridgeFacesContext context = (BridgeFacesContext) facesContext;
268         try {
269             clearSession(context);
270             ResponseWriter responseWriter = context.createAndSetResponseWriter();
271
272             UIViewRoot viewToRender = context.getViewRoot();
273             String JavaDoc renderedViewId =
274                     getRenderedViewId(context, viewToRender.getViewId());
275             viewToRender.setViewId(renderedViewId);
276             if (viewToRender.getId() == null) {
277                 viewToRender.setId(viewToRender.createUniqueId());
278             }
279
280             ComponentSupport.removeTransient(viewToRender);
281
282             // grab our FaceletFactory and create a Facelet
283
faceletInitialize();
284             Facelet f = null;
285             FaceletFactory.setInstance(faceletFactory);
286             try {
287                 f = faceletFactory.getFacelet(viewToRender.getViewId());
288             } finally {
289                 FaceletFactory.setInstance(null);
290             }
291
292             // Populate UIViewRoot
293
f.apply(context, viewToRender);
294
295             verifyUniqueComponentIds(context, viewToRender);
296
297             // Uses D2DViewHandler logging
298
tracePrintComponentTree(context);
299
300             responseWriter.startDocument();
301             renderResponse(context, viewToRender);
302             responseWriter.endDocument();
303         }
304         catch (Exception JavaDoc e) {
305             if (log.isErrorEnabled()) {
306                 log.error("Problem in renderResponse: " + e.getMessage(), e);
307             }
308             throw new FacesException("Problem in renderResponse: " + e.getMessage(), e);
309         }
310     }
311
312     protected static void removeTransient(UIComponent c) {
313         UIComponent d, e;
314         if (c.getChildCount() > 0) {
315             for (Iterator JavaDoc itr = c.getChildren().iterator(); itr.hasNext();) {
316                 d = (UIComponent) itr.next();
317                 if (d.getFacets().size() > 0) {
318                     for (Iterator JavaDoc jtr = d.getFacets().values().iterator(); jtr
319                             .hasNext();) {
320                         e = (UIComponent) jtr.next();
321                         if (e.isTransient()) {
322                             jtr.remove();
323                         } else {
324                             D2DFaceletViewHandler.removeTransient(e);
325                         }
326                     }
327                 }
328                 if (d.isTransient()) {
329                     itr.remove();
330                 } else {
331                     D2DFaceletViewHandler.removeTransient(d);
332                 }
333             }
334         }
335         if (c.getFacets().size() > 0) {
336             for (Iterator JavaDoc itr = c.getFacets().values().iterator(); itr
337                     .hasNext();) {
338                 d = (UIComponent) itr.next();
339                 if (d.isTransient()) {
340                     itr.remove();
341                 } else {
342                     D2DFaceletViewHandler.removeTransient(d);
343                 }
344             }
345         }
346     }
347     
348     /**
349      * For performance reasons, when there aren't id collisions
350      * we want this to be as fast as possible. When there are
351      * collisions, then we'll take some extra time to do a second
352      * pass to provide more information
353      * It could have all been done in one pass, but that would penalise
354      * the typical case, where there are not duplicate ids
355      *
356      * @param comp UIComponent to recurse down through, searching for
357      * duplicate ids. Should be the UIViewRoot
358      */

359     protected static void verifyUniqueComponentIds(
360         FacesContext context, UIComponent comp)
361     {
362         if (!log.isDebugEnabled())
363             return;
364         
365         HashMap JavaDoc ids = new HashMap JavaDoc(512);
366         ArrayList JavaDoc duplicateIds = new ArrayList JavaDoc(256);
367         quicklyDetectDuplicateComponentIds(comp, ids, duplicateIds);
368         
369         if(!duplicateIds.isEmpty()) {
370             HashMap JavaDoc duplicateIds2comps = new HashMap JavaDoc(512);
371             compileDuplicateComponentIds(comp, duplicateIds2comps, duplicateIds);
372             reportDuplicateComponentIds(context, duplicateIds2comps, duplicateIds);
373         }
374     }
375     
376     /**
377      * Do the least amount of work to find if there are any duplicate ids,
378      * with the assumption being that we won't typically find any
379      * We also mention any null ids, just to be safe
380      *
381      * @param comp UIComponent to recurse down through, searching for
382      * duplicate ids.
383      * @param ids HashMap<String id, String id> allows for detecting
384      * if an id has already been encountered or not
385      * @param duplicateIds ArrayList<String id> duplicate ids encountered
386      * as we recurse down
387      */

388     private static void quicklyDetectDuplicateComponentIds(
389         UIComponent comp, HashMap JavaDoc ids, ArrayList JavaDoc duplicateIds)
390     {
391         String JavaDoc id = comp.getId();
392         if (id == null) {
393             log.debug("UIComponent has null id: " + comp);
394         } else {
395             if (ids.containsKey(id)) {
396                 if(!duplicateIds.contains(id))
397                     duplicateIds.add(id);
398             } else {
399                 ids.put(id, id);
400             }
401         }
402         Iterator JavaDoc children = comp.getFacetsAndChildren();
403         while (children.hasNext()) {
404             UIComponent child = (UIComponent) children.next();
405             quicklyDetectDuplicateComponentIds(child, ids, duplicateIds);
406         }
407     }
408     
409     /**
410      * Make a list of every UIComponent that has a duplicate id, as found
411      * in the duplicateIds parameter.
412      *
413      * @param comp UIComponent to recurse down through, searching for
414      * duplicate ids.
415      * @param duplicateIds2comps HashMap< String id, ArrayList<UIComponent> >
416      * save every UIComponent with one of the
417      * duplicate ids, so we can list them all
418      * @param duplicateIds ArrayList<String id> duplicate ids encountered
419      * before
420      */

421     private static void compileDuplicateComponentIds(
422         UIComponent comp, HashMap JavaDoc duplicateIds2comps, ArrayList JavaDoc duplicateIds)
423     {
424         String JavaDoc id = comp.getId();
425         if (id != null && duplicateIds.contains(id)) {
426             ArrayList JavaDoc duplicateComps = (ArrayList JavaDoc) duplicateIds2comps.get(id);
427             if(duplicateComps == null) {
428                 duplicateComps = new ArrayList JavaDoc();
429                 duplicateIds2comps.put(id, duplicateComps);
430             }
431             duplicateComps.add(comp);
432         }
433         Iterator JavaDoc children = comp.getFacetsAndChildren();
434         while (children.hasNext()) {
435             UIComponent child = (UIComponent) children.next();
436             compileDuplicateComponentIds(child, duplicateIds2comps, duplicateIds);
437         }
438     }
439     
440     /**
441      * Given a list of duplicate ids, and a mapping to each list of
442      * UIComponents sharing each id, log this info so that the user
443      * can most easily debug their application.
444      *
445      * @param duplicateIds2comps HashMap< String id, ArrayList<UIComponent> >
446      * for each duplicated id, the UIComponents
447      * sharing that id
448      * @param duplicateIds ArrayList<String id> duplicate ids encountered
449      * before
450      */

451     private static void reportDuplicateComponentIds(
452         FacesContext context, HashMap JavaDoc duplicateIds2comps, ArrayList JavaDoc duplicateIds)
453     {
454         // We don't simply iterate over duplicateIds2comps's keys, since that
455
// sequence is will probably not be very useful, whereas duplicateIds
456
// is sequenced in the order that we encountered the ids in the
457
// component tree, and thus in the source .xhtml file.
458

459         int numDuplicateIds = duplicateIds.size();
460         log.debug("There were " + numDuplicateIds + " ids found which are duplicates, meaning that multiple UIComponents share that same id");
461         for(int i = 0; i < numDuplicateIds; i++) {
462             String JavaDoc id = (String JavaDoc) duplicateIds.get(i);
463             ArrayList JavaDoc duplicateComps = (ArrayList JavaDoc) duplicateIds2comps.get(id);
464             StringBuffer JavaDoc sb = new StringBuffer JavaDoc(512);
465             sb.append("Duplicate id: ");
466             sb.append(id);
467             sb.append(". Number of UIComponents sharing that id: ");
468             sb.append(Integer.toString(duplicateComps.size()));
469             sb.append('.');
470             for(int c = 0; c < duplicateComps.size(); c++) {
471                 UIComponent comp = (UIComponent) duplicateComps.get(c);
472                 sb.append("\n clientId: ");
473                 sb.append(comp.getClientId(context));
474                 if(comp.isTransient())
475                     sb.append(". TRANSIENT");
476                 sb.append(". component: ");
477                 sb.append(comp.toString());
478             }
479             log.debug(sb.toString());
480         }
481     }
482 }
483
Popular Tags