001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.visualweb.jsfsupport.container;
042:
043: import java.lang.reflect.InvocationTargetException;
044: import java.util.Map;
045:
046: import javax.faces.application.Application;
047: import javax.faces.component.UIViewRoot;
048: import javax.faces.context.ResponseWriter;
049: import javax.faces.render.RenderKit;
050: import javax.faces.render.RenderKitFactory;
051: import javax.faces.FactoryFinder; //import javax.portlet.PortletContext;
052: import javax.servlet.ServletContextEvent;
053: import org.openide.ErrorManager;
054: import java.lang.reflect.Method;
055:
056: import com.sun.rave.designtime.DesignContext;
057: import javax.faces.component.UIComponent;
058: import org.netbeans.modules.visualweb.jsfsupport.render.RaveRenderKit;
059: import org.openide.util.Exceptions;
060: import org.openide.util.Lookup;
061:
062: /**
063: * FacesContainer provides a "mock" web/servlet container environment for hosting the design of a
064: * JSF application
065: *
066: * @author Robert Brewin, Carl Quinn, Tor Norbye
067: * @author Winston Prakash - Modifications to support JSF 1.2
068: * @version 1.0
069: */
070: public class FacesContainer {
071:
072: protected static boolean DISABLE_RESET_APPLICATION_MAP = false;
073:
074: static {
075: String string = System
076: .getProperty("rave.disableResetApplicationMap");
077: if (string != null) {
078: Boolean bool = Boolean.valueOf(string);
079: DISABLE_RESET_APPLICATION_MAP = bool.booleanValue();
080: }
081: }
082:
083: // State variables
084: private boolean portletContainer;
085: private RaveServletContext context;
086: private RaveServletConfig config;
087: private RaveConfigureListener configureListener;
088: private RaveFacesContext facesContext;
089: private UIViewRoot defViewRoot;
090: private ClassLoader loader;
091:
092: // XXX #6460001.
093: private static final String SYS_PROP_SAX_PARSER_FACTORY = "javax.xml.parsers.SAXParserFactory"; // NOI18N
094: private static final String SYS_PROP_DOM_PARSER_FACTORY = "javax.xml.parsers.DocumentBuilderFactory"; // NO18N
095: private static final String SAX_PARSER_FACTORY = "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"; // NOI18N
096: private static final String DOM_PARSER_FACTORY = "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"; // NOI18N
097: private static final String NB_STARTUP_SAX_FACTORY = "org.netbeans.core.startup.SAXFactoryImpl"; // NOI18N
098: private static final String NB_STARTUP_DOM_FACTORY = "org.netbeans.core.startup.DOMFactoryImpl"; // NOI18N
099:
100: /**
101: * Constructor for the <em>FacesContainer</em>. Only one such object will exist at any given
102: * time for a project. Perform the initialization of the mock runtime container (server) as well
103: * as any state variables required
104: */
105: public FacesContainer(ClassLoader cl, boolean isPortletContainer) {
106: // XXX #6460001 Hack. There could be still the NB startup factories, which would cause problem with our context class loader.
107: String origSaxProperty = System
108: .getProperty(SYS_PROP_SAX_PARSER_FACTORY);
109: String origDomProperty = System
110: .getProperty(SYS_PROP_DOM_PARSER_FACTORY);
111: System.setProperty(SYS_PROP_SAX_PARSER_FACTORY,
112: SAX_PARSER_FACTORY);
113: System.setProperty(SYS_PROP_DOM_PARSER_FACTORY,
114: DOM_PARSER_FACTORY);
115: try {
116: //Trace.enableTraceCategory("jsfsupport.container");
117: ClassLoader oldContextClassLoader = Thread.currentThread()
118: .getContextClassLoader();
119: try {
120: Thread.currentThread().setContextClassLoader(cl);
121: this .portletContainer = isPortletContainer;
122: initialize(cl);
123: } finally {
124: Thread.currentThread().setContextClassLoader(
125: oldContextClassLoader);
126: }
127: } finally {
128: // XXX #6460001. Hack. By this time the startup shouldn't be getting back, otherwise it would cause issues later.
129: if (!NB_STARTUP_SAX_FACTORY.equals(origSaxProperty)) {
130: if (origSaxProperty != null) {
131: System.setProperty(SYS_PROP_SAX_PARSER_FACTORY,
132: origSaxProperty);
133: }
134: }
135: if (!NB_STARTUP_DOM_FACTORY.equals(origDomProperty)) {
136: if (origDomProperty != null) {
137: System.setProperty(SYS_PROP_DOM_PARSER_FACTORY,
138: origDomProperty);
139: }
140: }
141: }
142: }
143:
144: /**
145: * Initilize or reinitialize this environment with a new class loader
146: *
147: * @param cl
148: */
149: public synchronized void initialize(ClassLoader cl) {
150: this .loader = cl;
151:
152: // Initialize the mock ServletContext
153: // This is not right when isPortlet is true, but too many things depend
154: // on servlet context (see the configure listeners and such) so at designtime
155: // we provide a servlet context too. Note however that the external context
156: // is initialized with the portlet context only
157: context = new RaveServletContext();
158:
159: // Initialize the mock ServletConfig object for this context
160: config = new RaveServletConfig(context);
161: try {
162: Class klass = Class
163: .forName(
164: "org.netbeans.modules.visualweb.jsfsupport.container.RaveConfigureListener",
165: true, this .getClass().getClassLoader());
166: configureListener = (RaveConfigureListener) klass
167: .newInstance();
168:
169: } catch (Throwable e) {
170: ErrorManager.getDefault().notify(e);
171: }
172: //configureListener = new RaveConfigureListener();
173:
174: // Initialize the Servlet itself
175: try {
176: new RaveServlet(config);
177: } catch (javax.servlet.ServletException ex) {
178: // Big, Bad Mojo ... TODO: recover gracefully
179: System.err.println("Failed to create Servlet"); //NOI18N
180: }
181:
182: ServletContextEvent e = new ServletContextEvent(context);
183:
184: configureListener.contextInitialized(e);
185:
186: // We no longer support portlet
187: // if (portletContainer) {
188: //
189: // facesContext = new RaveFacesContext(new RaveExternalContext(new RavePortletContext()));
190: // } else {
191: // // Initialize the Rave FacesContext object
192: // facesContext = new RaveFacesContext(new RaveExternalContext(context));
193: // }
194: facesContext = new RaveFacesContext(new RaveExternalContext(
195: context));
196: facesContext.unsetCurrentInstance();
197: defViewRoot = newViewRoot();
198: facesContext.setViewRoot(defViewRoot); // stub viewRoot to satisfy some components
199: facesContext.setCurrentInstance();
200: facesContext.setServletContext(context);
201:
202: // Wrap the default run-time render kit with a design-time render kit, which
203: // knows about design-time wrappers for renderers.
204: RenderKitFactory factory = (RenderKitFactory) FactoryFinder
205: .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
206: String id = facesContext.getViewRoot().getRenderKitId();
207: RenderKit renderKit = factory.getRenderKit(facesContext, id);
208: if (!(renderKit instanceof RaveRenderKit)) {
209: factory.addRenderKit(id, new RaveRenderKit(renderKit));
210: }
211:
212: // Initialize the JsfTagSupport URI/TLD-FacesConfig map
213: JsfTagSupport.initialize(loader);
214: }
215:
216: /**
217: * Destroy this environment, clearing out references to other resources
218: */
219: public synchronized void destroy() {
220: ClassLoader oldContextClassLoader = Thread.currentThread()
221: .getContextClassLoader();
222: try {
223: Thread.currentThread().setContextClassLoader(loader);
224: facesContext.setDesignContext(null);
225: // Remove the FacesContext current instance Cache in the Factory Finder
226: facesContext.release();
227: // Remove the Factory instance Cache in the Factory Finder
228: FactoryFinder.releaseFactories();
229: // Remove the ClassLoader Cache in the Commons Log Factory
230: releaseCommonsLogFactory();
231: // Remove the ClassLoader Cache in the FacesConfigurListener
232: configureListener.contextDestroyed(new ServletContextEvent(
233: context));
234: // Remove the ClassLoader Cache in the Woodstock Theme Reference
235: releaseThemeResource();
236: } finally {
237: Thread.currentThread().setContextClassLoader(
238: oldContextClassLoader);
239: }
240: }
241:
242: /**
243: * @return the 'mock' <em>FacesContext</em> instance, making sure it is 'current' first
244: */
245: public RaveFacesContext getFacesContext() {
246: facesContext.setCurrentInstance();
247: return facesContext;
248: }
249:
250: /**
251: * Return whether this container is a portlet container. If false, it's a servlet container.
252: * @return True iff this container is a portlet container
253: */
254: public boolean isPortletContainer() {
255: return portletContainer;
256: }
257:
258: /**
259: * @return
260: */
261: public synchronized UIViewRoot newViewRoot() {
262: ClassLoader oldContextClassLoader = Thread.currentThread()
263: .getContextClassLoader();
264: try {
265: Thread.currentThread().setContextClassLoader(loader);
266: UIViewRoot viewRoot = new UIViewRoot();
267: viewRoot.setViewId("/rave"); // TODO: Should this be something real ?
268: String renderKitId = null;
269: Application application = null;
270: application = facesContext.getApplication();
271: if (application != null) {
272: renderKitId = application.getDefaultRenderKitId();
273: }
274: if (renderKitId == null) {
275: renderKitId = RenderKitFactory.HTML_BASIC_RENDER_KIT;
276: }
277: viewRoot.setRenderKitId(renderKitId);
278: return viewRoot;
279: } catch (Exception exc) {
280: exc.printStackTrace();
281: } finally {
282: Thread.currentThread().setContextClassLoader(
283: oldContextClassLoader);
284: }
285: return null;
286: }
287:
288: /**
289: * Prepare our contexts to render UIComponent(s) from a given DesignContext, and return a usable
290: * writer.
291: * @param lc
292: * @param frag
293: * @param uiform
294: * @return
295: */
296: // public DocFragmentJspWriter beginRender(DesignContext lc, UIViewRoot viewRoot, DocumentFragment frag) {
297: public synchronized void beginRender(DesignContext lc,
298: UIViewRoot viewRoot, ResponseWriter responseWriter) {
299: ClassLoader oldContextClassLoader = Thread.currentThread()
300: .getContextClassLoader();
301: try {
302: Thread.currentThread().setContextClassLoader(loader);
303:
304: facesContext.setCurrentInstance(); // make sure the context is available to components via thread lookup
305: facesContext.setDesignContext(lc); //!CQ HACK? to have to point its state to each lc all the time
306: facesContext.setViewRoot(viewRoot); // the view root for the component tree to be rendered
307: // TODO: We need to see if we may want to only change app scope on design context being from a different project
308: if (!DISABLE_RESET_APPLICATION_MAP) {
309: facesContext.getExternalContext().getApplicationMap()
310: .clear();
311: }
312: facesContext.getExternalContext().getSessionMap().clear();
313: facesContext.getExternalContext().getRequestMap().clear();
314:
315: // DocFragmentJspWriter rw = new DocFragmentJspWriter(this, frag);
316: // facesContext.setResponseWriter(rw);
317: facesContext.setResponseWriter(responseWriter);
318:
319: Map requestMap = facesContext.getExternalContext()
320: .getRequestMap();
321: requestMap.put("com.sun.faces.FormNumber", new Integer(0));
322: requestMap.put("com.sun.faces.INVOCATION_PATH", "/rave");
323: //
324: // return rw;
325: } catch (Exception exc) {
326: exc.printStackTrace();
327: } finally {
328: Thread.currentThread().setContextClassLoader(
329: oldContextClassLoader);
330: }
331: }
332:
333: /**
334: * Finish up a component rendering run.
335: * @param rw
336: */
337: // public void endRender(DocFragmentJspWriter rw) {
338: public synchronized void endRender(ResponseWriter responseWriter) {
339: ClassLoader oldContextClassLoader = Thread.currentThread()
340: .getContextClassLoader();
341: try {
342: Thread.currentThread().setContextClassLoader(loader);
343: try {
344: // rw.flush();
345: responseWriter.flush();
346: facesContext.setViewRoot(defViewRoot); // back to the empty default one
347: /* TODONOW
348: * We need to fix issue where we do not set design context properly, we assume it was
349: * set by something else, this is not good. Reverting it back for now in order for me to
350: * be able to commit and have sanity pass. Will work on issue with Deva, Tor, Craig.
351: */
352:
353: // facesContext.setDesignContext(null);
354: facesContext.setResponseWriter(null);
355: facesContext.setResponseStream(null);
356: } catch (Exception e) {
357: }
358: } catch (Exception exc) {
359: exc.printStackTrace();
360: } finally {
361: Thread.currentThread().setContextClassLoader(
362: oldContextClassLoader);
363: }
364: }
365:
366: /**
367: * Set the ClassLoader associated with this container
368: * @param loader The ClassLoader to be used for loading resources
369: */
370: public synchronized void setClassLoader(ClassLoader loader) {
371: ClassLoader oldContextClassLoader = Thread.currentThread()
372: .getContextClassLoader();
373: try {
374: Thread.currentThread().setContextClassLoader(this .loader);
375:
376: // Cleanup.
377: if (facesContext != null) {
378: facesContext.resetApplication();
379: }
380: // Remove the Factory instance Cache in the Factory Finder
381: FactoryFinder.releaseFactories();
382: // Remove the ClassLoader Cache in the Commons Log Factory
383: releaseCommonsLogFactory();
384: // Remove the ClassLoader Cache in the FacesConfigurListener
385: configureListener.contextDestroyed(new ServletContextEvent(
386: context));
387: // Remove the ClassLoader Cache in the Woodstock Theme Reference
388: releaseThemeResource();
389: configureListener.contextDestroyed(new ServletContextEvent(
390: context));
391: } catch (Exception exc) {
392: exc.printStackTrace();
393: } finally {
394: Thread.currentThread().setContextClassLoader(
395: oldContextClassLoader);
396: }
397:
398: // set the loader
399: this .loader = loader;
400:
401: oldContextClassLoader = Thread.currentThread()
402: .getContextClassLoader();
403: try {
404: Thread.currentThread().setContextClassLoader(loader);
405: // initilize the container
406: initialize(loader);
407: } catch (Exception exc) {
408: exc.printStackTrace();
409: } finally {
410: Thread.currentThread().setContextClassLoader(
411: oldContextClassLoader);
412: }
413: }
414:
415: // Bug Fix: 125082
416: private void releaseCommonsLogFactory() {
417: ClassLoader classLoader = Lookup.getDefault().lookup(
418: ClassLoader.class);
419: try {
420: Class<?> logFactoryClass = Class.forName(
421: "com.sun.org.apache.commons.logging.LogFactory",
422: false, classLoader);
423: Method releaseFactory = logFactoryClass.getMethod(
424: "release", ClassLoader.class);
425: releaseFactory.invoke(null, loader);
426:
427: } catch (NoSuchMethodException ex) {
428: Exceptions.printStackTrace(ex);
429: } catch (SecurityException ex) {
430: Exceptions.printStackTrace(ex);
431: } catch (IllegalAccessException ex) {
432: Exceptions.printStackTrace(ex);
433: } catch (IllegalArgumentException ex) {
434: Exceptions.printStackTrace(ex);
435: } catch (InvocationTargetException ex) {
436: Exceptions.printStackTrace(ex);
437: } catch (ClassNotFoundException ex) {
438: Exceptions.printStackTrace(ex);
439: }
440: }
441:
442: // Bug Fix: 125082
443: private void releaseThemeResource() {
444: ClassLoader classLoader = Lookup.getDefault().lookup(
445: ClassLoader.class);
446: try {
447: Class<?> themeResourcesClass = Class.forName(
448: "com.sun.webui.theme.ThemeResources", false,
449: classLoader);
450: Class<?> resourceBundleThemeClass = Class.forName(
451: "com.sun.webui.theme.ResourceBundleTheme", false,
452: classLoader);
453: Method releaseThemeResource = resourceBundleThemeClass
454: .getMethod("getInstance", themeResourcesClass);
455: releaseThemeResource.invoke(null, (Object) null);
456:
457: } catch (NoSuchMethodException ex) {
458: Exceptions.printStackTrace(ex);
459: } catch (SecurityException ex) {
460: Exceptions.printStackTrace(ex);
461: } catch (IllegalAccessException ex) {
462: Exceptions.printStackTrace(ex);
463: } catch (IllegalArgumentException ex) {
464: Exceptions.printStackTrace(ex);
465: } catch (InvocationTargetException ex) {
466: Exceptions.printStackTrace(ex);
467: } catch (ClassNotFoundException ex) {
468: Exceptions.printStackTrace(ex);
469: }
470: }
471:
472: public synchronized String findComponentClass(String tagName,
473: String taglibUri) throws JsfTagSupportException {
474: String errorMessage = org.openide.util.NbBundle.getMessage(
475: FacesContainer.class, "JSF_COMPONENT_NOT_FOUND",
476: new Object[] { tagName, taglibUri });
477: try {
478: return JsfTagSupport.getInstance(taglibUri)
479: .getComponentClass(loader, tagName);
480: } catch (Exception ex) {
481: throw new JsfTagSupportException(errorMessage, ex);
482: }
483: }
484:
485: public synchronized boolean isComponentRendersChildren(
486: UIComponent comp) {
487: facesContext.setCurrentInstance();
488: ClassLoader oldContextClassLoader = Thread.currentThread()
489: .getContextClassLoader();
490: try {
491: Thread.currentThread().setContextClassLoader(loader);
492: if (comp.getRendersChildren()) {
493: return true;
494: }
495: } finally {
496: Thread.currentThread().setContextClassLoader(
497: oldContextClassLoader);
498: }
499: return false;
500: }
501: }
|