View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Blacksmith, Inc.
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
17  */
18  
19  package net.wotonomy.web;
20  
21  import java.lang.reflect.Constructor;
22  import java.util.List;
23  
24  import javax.servlet.ServletException;
25  import javax.servlet.http.HttpServlet;
26  import javax.servlet.http.HttpServletRequest;
27  import javax.servlet.http.HttpServletResponse;
28  
29  import net.wotonomy.foundation.NSArray;
30  import net.wotonomy.foundation.NSDictionary;
31  import net.wotonomy.foundation.NSMutableDictionary;
32  import net.wotonomy.web.util.BrowserLauncher;
33  
34  import org.mortbay.http.HttpListener;
35  import org.mortbay.http.HttpServer;
36  import org.mortbay.jetty.servlet.ServletHandler;
37  import org.mortbay.util.InetAddrPort;
38  
39  /***
40  * A pure java implementation of WOApplication. <br><br>
41  *
42  * The application is responsible for creating and managing sessions
43  * and dispatching requests to the appropriate handlers. <br><br>
44  *
45  * This implementation extends HttpServlet, so the application itself 
46  * is a servlet and can be configured and managed as such by the servlet 
47  * container.
48  *
49  * @author michael@mpowers.net
50  * @author $Author: cgruber $
51  * @version $Revision: 905 $
52  */
53  public class WOApplication 
54  	extends HttpServlet
55  {
56      /***
57      * A tricky way to allow multiple WOApplications
58      * in the same servlet container.
59      */
60      static private ThreadLocal threadLocal;
61  	//static private WOApplication application;
62      
63      /***
64      * Determines application-wide page caching.
65      * Pages may individually prevent caching.
66      */
67      static private boolean cachingEnabled = false;    
68      private static boolean autoOpenInBrowser = true;
69      
70      private String name;
71  	private WORequestHandler defaultRequestHandler;
72      private NSMutableDictionary requestHandlers;
73      private WOSessionStore sessionStore;
74      private WOResourceManager resourceManager;
75      private boolean pageRefreshOnBacktrack;
76      private int pageCacheSize;
77      private int permanentPageCacheSize;
78  
79      public static final String WOApplicationWillFinishLaunchingNotification
80  		= "WOApplicationWillFinishLaunchingNotification";
81      public static final String WOApplicationDidFinishLaunchingNotification
82  		= "WOApplicationDidFinishLaunchingNotification";
83      public static final String WOGarbageCollectionPeriodKey
84  		= "WOGarbageCollectionPeriodKey";
85          
86      static String _DirectActionRequestHandlerKey = "_DirectActionRequestHandlerKey";
87      static String _ComponentRequestHandlerKey = "_ComponentRequestHandlerKey";
88      static String _ResourceRequestHandlerKey = "_ResourceRequestHandlerKey";
89      static String WOPort = "WOPort";
90      static String WOSMTPHost = "WOSMTPHost";
91      static final String ELEMENT_CLASS = "elementClass";
92  
93      public WOApplication ()
94      {
95          if ( threadLocal == null )
96          {
97              threadLocal = new ThreadLocal();
98          }
99          threadLocal.set( this );
100         
101     	//application = this;
102         resourceManager = createResourceManager();
103         requestHandlers = new NSMutableDictionary();
104         defaultRequestHandler = new WODirectActionRequestHandler();
105         registerRequestHandler( defaultRequestHandler, directActionRequestHandlerKey() );
106         registerRequestHandler( new WOComponentRequestHandler(), componentRequestHandlerKey() );
107         registerRequestHandler( new WOResourceRequestHandler(), resourceRequestHandlerKey() );
108         sessionStore = WOSessionStore.serverSessionStore();
109         
110         pageRefreshOnBacktrack = true;
111         pageCacheSize = 30;
112         permanentPageCacheSize = 30;
113         
114         threadLocal.set( null );
115     }
116     
117 	/***
118 	* Dispatches the request and updates the specified response
119 	* as appropriate.  This implementation creates a new WORequest
120     * and WOContext from the request, sends the response to the 
121     * appropriate WORequestHandler, and then updates the servlet
122     * response from the resulting WOResponse.
123 	*/
124     protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
125     	throws ServletException, java.io.IOException
126     {
127         threadLocal.set( this );
128 
129 	    WORequest request = new WORequest( req, this );
130         WOResponse response = dispatchRequest( request );
131         response.generateServletResponse( resp );
132     }
133 
134 	/***
135 	* Handles post requests by calling doGet(), since the framework
136 	* handles both gets and posts similarly.  Override to handle
137 	* post requests in a different manner.
138 	*/
139     protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
140     	throws ServletException, java.io.IOException
141     {
142     	doGet( req, resp );
143 	}
144 	
145 	// obtaining attributes
146 	
147 	/***
148 	* Returns the singleton instance of this application.
149 	*/
150     public static WOApplication application()
151     {
152         return (WOApplication) threadLocal.get();
153     	//return application; 
154     }
155     
156 	/***
157 	* Returns the name of the application.  This implementation returns
158 	* the name the jar file or directory from which the class was loaded,
159     * with no extensions.
160 	*/
161     public String name()
162     {
163         if ( name == null )
164         {
165             name = path();
166             int i;
167             if ( name.endsWith( "/" ) )
168             { // path
169                 name = name.substring( 0, name.length() - 1 );
170             }
171             else
172             { // jar file
173                 i = name.lastIndexOf( '.' );
174                 if ( i != -1 ) name = name.substring( 0, i );
175             }
176             i = name.lastIndexOf( '/' );
177             if ( i != -1 ) name = name.substring( i+1 );
178         }
179         return name;
180     }
181 
182     /***
183     * Returns the absolute path to the application on the local file system.
184     * Note that the application might be embedded inside of a jar file.
185     */
186     public String path ()
187     {
188         return getClass().getProtectionDomain().getCodeSource().getLocation().toString();
189     }
190     
191     /***
192     * Returns the path to the application on the local file system
193     * relative to the server's document root.
194     */
195     public String baseURL ()
196     {
197         String root = getServletContext().getRealPath( "/" );
198         String result = path();
199         if ( result.endsWith("/") )
200         { // path
201             if ( result.startsWith( root ) )
202             { // relative to root
203                 result = result.substring( root.length() );
204             }
205             // else leave as absolute reference
206         }
207         // jar or war file: leave as absolute reference
208         return result;
209     }
210     
211     // concurrent request handling
212     
213     /***
214     * Returns whether this application allows request
215     * to be handled concurrently.  
216     * This implementation returns true.
217     * Subclasses may override to return false to force
218     * single-threaded request handling, although this
219     * is not implemented.
220     */
221     public boolean allowsConcurrentRequestHandling ()
222     {
223         return true;
224     }
225     
226     /***
227     * Returns whether this application allows request
228     * to be handled concurrently.  
229     * This implementation returns true.
230     */
231     public boolean adaptorsDispatchRequestsConcurrently ()
232     {
233         return true;
234     }
235     
236     /***
237     * Returns whether this application allows request
238     * to be handled concurrently.  
239     * This implementation returns true.
240     */
241     public boolean isConcurrentRequestHandlingEnabled ()
242     {
243         return true;
244     }
245     
246     // handling requests
247 
248 	/***
249 	* Invoked first in the request-response cycle.
250 	* Override to perform any kind of initialization at the
251 	* start of a request.  This implementation does nothing.
252 	*/
253     public void awake ()
254     {
255 
256     }
257 
258     /***
259     * Invoked to start the first phase of the request-response cycle,
260     * after all calls to awake() have been completed.
261     */
262     public void takeValuesFromRequest (WORequest aRequest, WOContext aContext)
263 	{
264 	    aContext.session().takeValuesFromRequest( aRequest, aContext );
265 	}
266 
267     /***
268     * Invoked to start the second phase of the request-response cycle,
269     * after all calls to takeValuesFromRequest have finished.
270     */
271     public WOActionResults invokeAction (WORequest aRequest, WOContext aContext)
272     {
273 	    return aContext.session().invokeAction( aRequest, aContext );
274     }
275 
276     /***
277     * Invoked to start the third phase of the request-response cycle,
278     * after invokeAction() has completed and returned a WOResponse.
279     */
280     public void appendToResponse (WOResponse aResponse, WOContext aContext)
281     {
282 	    aContext.session().appendToResponse( aResponse, aContext );
283     }
284     
285     /***
286 	* Invoked last in the request-response cycle.
287 	* Override to perform any kind of clean-up at the 
288 	* end of a request.  This implementation does nothing.
289 	*/
290     public void sleep ()
291     {
292 	
293     }
294 
295     /***
296     * Dispatches the request to the appropriate handler.
297     */
298     public WOResponse dispatchRequest (WORequest aRequest)
299     {
300 	    return handlerForRequest( aRequest ).handleRequest( aRequest );
301     }
302 
303     // request handling
304     
305     /***
306     * Returns the default request handler used if the requested
307     * handler isn't specified or cannot be found.  (This defaults
308     * to the WODirectActionRequestHandler.)
309     */ 
310     public WORequestHandler defaultRequestHandler ()
311     {
312         return defaultRequestHandler;
313     }
314     
315     /***
316     * Sets the default request handler used if the requested
317     * handler isn't specified or cannot be found.
318     */
319     public void setDefaultRequestHandler (WORequestHandler aRequestHandler)
320     {
321         defaultRequestHandler = aRequestHandler;
322     }
323     
324     /***
325     * Registers the specified request handler for the specified key.
326     */
327     public void registerRequestHandler (WORequestHandler aRequestHandler, String aKey)
328     {
329         requestHandlers.setObjectForKey( aRequestHandler, aKey );
330     }
331     
332     /***
333     * Unregisters any existing request handler for the specified key
334     * returning the existing request handler, if any.
335     */
336     public WORequestHandler removeRequestHandlerForKey (String aKey)
337     {
338         WORequestHandler result = requestHandlerForKey( aKey );
339         requestHandlers.removeObjectForKey( aKey );
340         return result;
341     }
342     
343     /***
344     * Returns the keys under which request handlers are registered.
345     */
346     public NSArray registeredRequestHandlerKeys ()
347     {
348         return requestHandlers.allKeys();
349     }
350     
351     /***
352     * Returns the request handler registered for the specified key,
353     * or null if no request handler is registered for that key.
354     */
355     public WORequestHandler requestHandlerForKey (String aKey)
356     {
357         return (WORequestHandler) requestHandlers.objectForKey( aKey );
358     }
359     
360     /***
361     * Returns the request handler that would best service the specified request.
362     */
363     public WORequestHandler handlerForRequest (WORequest aRequest)
364     {
365         WORequestHandler result = requestHandlerForKey( aRequest.requestHandlerKey() );
366         if ( aRequest == null ) result = defaultRequestHandler();
367         return result;
368     }
369 
370     // handling errors    
371 
372     public WOResponse handleSessionCreationErrorInContext( WOContext aContext )
373     {
374         WOResponse response = new WOResponse();
375         response.setStatus( 500 ); // internal server error
376         //TODO: add more useful information to the response
377         System.err.println( "Failed to create session: " + aContext );
378 new RuntimeException().printStackTrace(); // remove me
379         return response;
380     }
381     
382     public WOResponse handleSessionRestorationErrorInContext( WOContext aContext )
383     {
384         WOResponse response = new WOResponse();
385         response.setStatus( 500 ); // internal server error
386         //TODO: add more useful information to the response
387         System.err.println( "Failed to restore session: " + aContext );
388 new RuntimeException().printStackTrace(); // remove me
389         return response;
390     }
391     
392     public WOResponse handlePageRestorationErrorInContext( WOContext aContext )
393     {
394         WOResponse response = new WOResponse();
395         response.setStatus( 500 ); // internal server error
396         //TODO: add more useful information to the response
397         System.err.println( "Failed to restore page: " + aContext );
398 new RuntimeException().printStackTrace(); // remove me
399         return response;
400     }
401     
402     public WOResponse handleException( Throwable aThrowable, WOContext aContext )
403     {
404         WOResponse response = new WOResponse();
405         response.setStatus( 500 ); // internal server error
406         System.err.println( "Exception occurred: " + aContext );
407         if ( aThrowable.getMessage() != null )
408         {
409             response.appendContentString( aThrowable.getMessage() );
410             aThrowable.printStackTrace();
411         }
412         else
413         {
414             response.appendContentString( aThrowable.toString() );
415             aThrowable.printStackTrace();
416         }
417         aThrowable.printStackTrace();
418         return response;
419     }
420     
421     // managing pages 
422     
423     /***
424     * Sets the number of pages that will be retained
425     * in the user's session.  Set to zero to disable page caching.
426     */
427     public void setPageCacheSize (int aPositiveInt)
428     {
429         pageCacheSize = aPositiveInt;
430     }
431     
432     /***
433     * Returns the number of pages that will be retained
434     * in the user's session.  The default page cache size is 30.
435     */
436     public int pageCacheSize ()
437     {
438         return pageCacheSize;
439     }
440     
441     /***
442     * Returns the number of pages that will be retained in the
443     * longer-term "permanent" page cache in the user's session,
444     * which is typically used for navigation bars in frames, etc.
445     * The default permanent page cache size is 30.
446     */
447     public int permanentPageCacheSize ()
448     {
449         return permanentPageCacheSize;
450     }
451     
452     /***
453     * Returns the number of pages that will be retained in the
454     * longer-term "permanent" page cache in the user's session,
455     * which is typically used for navigation bars in frames, etc.
456     * Set to zero to disable permanent page caching.
457     */
458     public void setPermanentPageCacheSize (int aPositiveInt)
459     {
460         permanentPageCacheSize = aPositiveInt;
461     }
462     
463     /***
464     * Returns whether a "backtrack" for an existing page should
465     * simply call generateResponse() on the existing page instance.
466     * If false, a new page is created instead.  The default is true.
467     */
468     public void setPageRefreshOnBacktrackEnabled (boolean enabled)
469     {
470         pageRefreshOnBacktrack = enabled;
471     }
472     
473     /***
474     * Returns whether a "backtrack" for an existing page should
475     * simply call generateResponse() on the existing page instance.
476     * If false, a new page is created instead.  The default is true.
477     */
478     public boolean isPageRefreshOnBacktrackEnabled ()
479     {
480         return pageRefreshOnBacktrack;
481     }
482 
483     // managing sessions
484     
485     /***
486     * Sets the session store used by this application to persist
487     * sessions between request-response transactions.
488     */
489     public void setSessionStore(WOSessionStore aSessionStore)
490     {
491         sessionStore = aSessionStore;
492     }
493     
494     /***
495     * Returns the session store used by this application to persist
496     * sessions between request-response transactions.
497     */
498     public WOSessionStore sessionStore()
499     {
500         return sessionStore;
501     }
502     
503     /***
504     * Called at the end of the request-response cycle
505     * to persist the current session until the user's next request.
506     */
507     public void saveSessionForContext(WOContext aContext)
508     {
509         sessionStore.saveSessionForContext( aContext );
510     }
511     
512     /***
513     * Called at the beginning of the request-response cycle
514     * to obtain the current session from the user's last request.
515     * Returns null if no such session has been created.
516     * This method sets the context of the session to the specified context.
517     */
518     public WOSession restoreSessionWithID(String aSessionID, WOContext aContext)
519     {
520         WORequest request = aContext.request();
521         WOSession session = sessionStore.restoreSessionWithID( aSessionID, request );
522         if ( session != null )
523         {
524             session.setContext( aContext );
525             session.setServletSession( request.servletRequest().getSession() );
526         }
527         return session;
528     }
529     
530     /***
531     * Called to create a session for a new request.  This implementation
532     * looks for a class in the same package as the application class 
533     * called "Session" and failing that returns a WOSession.
534     */
535     public WOSession createSessionForRequest(WORequest aRequest)
536     {
537         WOSession result = null;
538         try
539         {   
540 			// using our class loader, which is hopefully dynamic.
541             result = (WOSession) getLocalClass( "Session" ).newInstance();
542         }
543         catch ( Throwable t )
544         {
545             // ignore: fall back to WOSession
546             //t.printStackTrace();
547         }
548         
549         if ( result == null )
550         {
551             result = new WOSession();
552         }
553         
554         result.setServletSession( aRequest.servletRequest().getSession( true ) );
555         return result;
556     }
557    
558 	/***
559 	* Returns the page component with the specified name.
560 	* A context is created with the specified request,
561     * along with a session if necessary.
562 	*/
563     public WOComponent pageWithName (String aName, WORequest aRequest)
564     {
565     	return pageWithName( aName, WOContext.contextWithRequest( aRequest ) );
566     }
567     
568     /***
569     * Called to retrieve a component for the specified context.
570     */
571     public WOComponent pageWithName (String aName, WOContext aContext)
572     {
573         if ( aName == null ) 
574         {
575             throw new IllegalArgumentException( 
576                 "WOApplication.pageWithName: name is null" );
577         }
578         
579         WOComponent result = null;
580         try
581         {   
582 			// using our class loader, which is hopefully dynamic.
583             Class c = getLocalClass( aName );
584             
585             if ( c != null )
586             {
587                 // get constructor
588                 Constructor ctor;
589                 try
590                 {
591                     ctor = c.getConstructor( new Class[] { WOContext.class } );
592                 }
593                 catch ( NoSuchMethodException nsme )
594                 {
595                     ctor = null;
596                 }
597                 
598                 // create instance of class
599                 if ( ctor != null )
600                 {
601                     result = (WOComponent) ctor.newInstance( new Object[] { aContext } );
602                 }
603                 else // call back on default constructor (deprecated)
604                 {
605                     result = (WOComponent) c.newInstance();
606                 }
607             }
608         }
609         catch ( Throwable t )
610         {
611             // ignore for now
612             //TODO: Throw appropriate exception here
613             //System.err.println( "Not found: pageWithName: " + aName );
614             t.printStackTrace();
615         }
616 
617         if ( result != null && aContext != null )
618         {
619             // this is where components get their context
620             result.ensureAwakeInContext( aContext );
621         }
622         else 
623         if ( result == null )
624         {
625             System.err.println( "Not found: pageWithName: " + aName );
626         }
627         
628         return result;
629     }
630     
631     /***
632     * Returns a class in the same package as the Application class,
633     * or, failing that, from the WOApplication package, or finally
634     * from the root of the class path.  Returns null if not found.
635     */
636     Class getLocalClass( String aName )
637     {
638         Class result = null;
639         if ( getClass() != WOApplication.class )
640         {
641             result = loadLocalClass( getClass(), aName );
642         }
643         if ( result == null )
644         {
645             result = loadLocalClass( WOApplication.class, aName );
646         }
647         if ( result == null )
648         {
649             result = loadLocalClass( null, aName );
650         }
651         return result;
652     }
653     
654     private static final Class loadLocalClass( Class aClass, String aName )
655     {
656         ClassLoader loader;
657         String packageName = "";
658         if ( aClass != null )
659         {
660             loader = aClass.getClassLoader();
661             packageName = aClass.getName();
662             int index = packageName.lastIndexOf( "." );
663             if ( index > -1 ) 
664             {
665                 packageName = packageName.substring( 0, index+1 );
666             }
667             else
668             {
669                 packageName = "";
670             }
671         }
672         else
673         {
674             loader = WOApplication.class.getClassLoader();
675         }
676         
677         try
678         {
679             return loader.loadClass( packageName + aName );
680         }
681         catch ( ClassNotFoundException e )
682         {
683             return null;
684         }
685     }
686     
687     // creating elements
688     
689     /***
690     * Returns either a dynamic element or a component
691     * for the specified name.
692     */
693     public WOElement dynamicElementWithName(
694         String anElementName, NSDictionary anAssociationMap, 
695         WOElement aBodyElement, List aLanguageList)
696     {
697         WOElement element = null;
698         Class c = null;
699         try
700         {
701             c = getLocalClass( anElementName );
702             if ( c == null )
703             {
704                 System.out.println( "Not found: dynamicElementWithName: " +
705                     "could not find WODynamicElement subclass: " + anElementName );
706                 c = WODynamicElement.class;
707             }
708 
709             // get constructor
710             Class[] params = new Class[]
711                 { String.class, NSDictionary.class, WOElement.class };
712             Constructor ctor = c.getConstructor( params );
713     
714             // create instance of class
715             if ( ctor != null )
716             {
717                 element = (WODynamicElement) ctor.newInstance( 
718                     new Object[] { anElementName, anAssociationMap, aBodyElement } );
719             }
720         }
721         catch ( Throwable t )
722         {
723             // ignore: not a dynamic element
724             //System.out.println( "Not a dynamic element: dynamicElementWithName: " + t );
725             //exc.printStackTrace();
726         }
727         
728         // no dynamic element found: look for a component
729         if ( element == null )
730         {
731             WOComponent component = (WOComponent) pageWithName( anElementName, (WOContext) null );
732 
733             // this seems hackish: 
734             // I don't see another way of setting the bindings.
735             component.associations = anAssociationMap;
736             component.rootElement = aBodyElement;
737             
738             element = component;
739         }
740         
741         return element;
742     }
743     
744     // resource handling
745     
746     /***
747     * Called to create the application's resource manager.
748     * Override to create a custom resource manager.
749     */
750     public WOResourceManager createResourceManager()
751     {
752         return new WOResourceManager();
753     }
754 
755     /***
756     * Returns the application's current resource manager.
757     */
758     public WOResourceManager resourceManager()
759     {
760         return resourceManager;
761     }
762     
763     /***
764     * Installs a custom resource manager into the current application.
765     * @deprecated Override createResourceManager() instead.
766     */
767     public void setResourceManager(WOResourceManager aResourceManager)
768     {
769         resourceManager = aResourceManager;
770     }
771 
772 /*
773     // request handling undocumented
774 
775     public WOComponent pageWithName (String);
776     public void savePage (WOComponent);
777     public WOComponent restorePageForContextID (String);
778     public WOContext context ();
779     public WOSession session ();
780     public WOSession createSession ();
781     public WOSession restoreSession ();
782     public void saveSession (WOSession);
783 
784     public WOResponse handleRequest (WORequest aRequest)
785     {
786     }
787 
788     // error handling undocumented
789     
790     WOResponse handleSessionCreationError ();
791     WOResponse handleSessionRestorationError ();
792     WOResponse handlePageRestorationError ();
793     WOResponse handleException (Throwable);
794 
795     // running
796     
797     public NSRunLoop runLoop ();
798     public void run ();
799     public void setTimeOut (double);
800     public double timeOut ();
801     public void terminate ();
802     public boolean isTerminating ();
803     
804     // script debugging
805     
806     public void traceScriptedMessages (boolean);
807     public void traceAssignments (boolean);
808     public void traceStatements (boolean);
809     public void traceObjectiveCMessages (boolean);
810     public void trace (boolean);
811     public void logTakeValueForDeclarationNamed (String, String, String, String, Object);
812     public void logSetValueForDeclarationNamed (String, String, String, String, Object);
813 	
814 	// script handling
815 	
816 	public String scriptedClassNameWithPath (String);
817     public String scriptedClassNameWithPathEncoding (String, int);
818 
819     // statistics report
820     
821     public void setStatisticsStore (WOStatisticsStore);
822     public WOStatisticsStore statisticsStore ();
823     public NSDictionary statistics ();
824     
825     // managing adaptors
826     
827     public WOAdaptor adaptorWithName (String, NSDictionary);
828     public NSArray adaptors ();
829     
830     // monitor support
831     
832     public boolean monitoringEnabled ();
833     public int activeSessionsCount ();
834     public void refuseNewSessions (boolean);
835     public boolean isRefusingNewSessions ();
836     public void setMinimumActiveSessionsCount (int);
837     public int minimumActiveSessionsCount ();
838     public void terminateAfterTimeInterval (double);
839 
840 	// garbage collection undocumented
841 
842     int garbageCollectionPeriod ();
843     void setGarbageCollectionPeriod (int);
844     
845 	// backwards compatibility
846 
847     public boolean requiresWOF35RequestHandling ();
848     public boolean requiresWOF35TemplateParser ();
849 
850     public void setPrintsHTMLParserDiagnostics (boolean);
851     public boolean printsHTMLParserDiagnostics ();
852     
853     // configuration and defaults
854 
855     public static NSArray loadFrameworks ();
856     public static void setLoadFrameworks (NSArray);
857 */
858     static boolean debuggingEnabled = false;
859     /***
860     * Returns whether the application is in "debug mode".
861     */
862     public static boolean isDebuggingEnabled()
863     {
864         return debuggingEnabled;
865     }
866 
867     /***
868     * Sets whether the application is in "debug mode".
869     */
870     public static void setDebuggingEnabled( boolean enabled )
871     {
872         debuggingEnabled = enabled;
873     }
874     
875     /***
876     * Sets whether templates are cached.  If true, templates will
877     * only be read once per application lifetime.  Otherwise, templates
878     * will be read each time this class is instantiated.  Defaults to false.
879     */
880     public static void setCachingEnabled (boolean enabled)
881 	{
882 		cachingEnabled = enabled;
883 	}
884 
885     /***
886     * Returns whether templates are cached.  If true, templates are
887     * read once per application lifetime.  Otherwise, templates are
888     * read each time this class is instantiated.
889     */
890     public static boolean isCachingEnabled ()
891     {
892     	return cachingEnabled;
893     }
894     
895     // configuration
896     
897     /***
898     * Returns the component request handler key,
899     * which is defined by the system property _ComponentRequestHandlerKey.
900     * The default is "wo".
901     */
902     public static String componentRequestHandlerKey()
903     {
904         return System.getProperty( _ComponentRequestHandlerKey, "wo" );
905     }
906     
907     /***
908     * Sets the component request handler key.
909     * @deprecated Set the system property _ComponentRequestHandlerKey.
910     */
911     public static void setComponentRequestHandlerKey(String aKey)
912     {
913         System.setProperty( _ComponentRequestHandlerKey, aKey );
914     }
915     
916     /***
917     * Returns the direct action request handler key,
918     * which is defined by the system property _DirectActionRequestHandlerKey.
919     * The default is "wa".
920     */
921     public static String directActionRequestHandlerKey()
922     {
923         return System.getProperty( _DirectActionRequestHandlerKey, "wa" );
924     }
925     
926     /***
927     * Sets the direct action request handler key.
928     * @deprecated Set the system property _DirectActionRequestHandlerKey.
929     */
930     public static void setDirectActionRequestHandlerKey(String aKey)
931     {
932         System.setProperty( _DirectActionRequestHandlerKey, aKey );
933     }
934     
935     /***
936     * Returns the resource request handler key,
937     * which is defined by the system property _ResourceRequestHandlerKey.
938     * The default is "wr".
939     */
940     public static String resourceRequestHandlerKey()
941     {
942         return System.getProperty( _ResourceRequestHandlerKey, "wr" );
943     }
944     
945     /***
946     * Sets the resource request handler key.
947     * @deprecated Set the system property _ResourceRequestHandlerKey.
948     */
949     public static void setResourceRequestHandlerKey(String aKey)
950     {
951         System.setProperty( _ResourceRequestHandlerKey, aKey );
952     }
953     
954     /***
955     * Returns whether this application should attempt to open
956     * a web browser on the host machine when launched standalone.
957     * The default is true.
958     */
959     public static boolean autoOpenInBrowser()
960     {
961         return autoOpenInBrowser;
962     }
963 
964     /***    
965     * Sets whether this application should attempt to open
966     * a web browser on the host machine when launched standalone.
967     */
968     public static void setAutoOpenInBrowser(boolean autoOpen)
969     {
970         autoOpenInBrowser = autoOpen;
971     }
972     
973     /***
974     * Gets the port used when run as a standalone server.  
975     * Returns the value of the system property WOPort. 
976     * By default, this is zero, which causes the application
977     * to automatically select an available port.
978     */
979     public static Number port ()
980     {
981         return Integer.getInteger( WOPort, 0 );
982     }
983     
984     /***
985     * Gets the smtp server that will be used to send email.
986     * Returns the system property WOSMTPHost.
987     */    
988     public static String SMTPHost()
989     {
990         return System.getProperty( WOSMTPHost );
991     }
992     
993     /***
994     * Sets the smtp server that will be used to send email.
995     * @deprecated Set the system property WOSMTPHost.
996     */
997     public static void setSMTPHost( String aHost )
998     {
999         System.setProperty( WOSMTPHost, aHost );
1000     }
1001 /*    
1002     public static boolean isDirectConnectEnabled ();
1003     public static void setDirectConnectEnabled (boolean);
1004     public static String cgiAdaptorURL ();
1005     public static void setCGIAdaptorURL (String);
1006     public static String applicationBaseURL ();
1007     public static void setApplicationBaseURL (String);
1008     public static String frameworksBaseURL ();
1009     public static void setFrameworksBaseURL (String);
1010     public static String recordingPath ();
1011     public static void setRecordingPath (String);
1012     public static NSArray projectSearchPath ();
1013     public static void setProjectSearchPath (NSArray);
1014     public static boolean isMonitorEnabled ();
1015     public static void setMonitorEnabled (boolean);
1016     public static String monitorHost ();
1017     public static String adaptor ();
1018     public String number (); // deprecated
1019     public static Number listenQueueSize ();
1020     public static void setListenQueueSize (Number);
1021     public static NSArray additionalAdaptors ();
1022     public static void setAdditionalAdaptors (NSArray);
1023     public static boolean includeCommentsInResponses ();
1024     public static void setIncludeCommentsInResponses (boolean);
1025     public static void setSessionTimeOut (Number);
1026     public static Number sessionTimeOut ();
1027     public static void logString (String);
1028     public static void debugString (String);
1029     public static void logToMonitorString (String);
1030 */
1031 
1032     /***
1033     * Main entry point for applications that do not subclass WOApplication.
1034     */
1035     public static void main( String[] argv )
1036     {
1037         main( argv, WOApplication.class );
1038     }
1039 
1040     /***
1041     * Subclasses may call this method to start a self-hosted 
1042     * web server to serve themselves directly (for testing).
1043     */
1044     public static void main( String[] argv, Class subclass )
1045     {
1046         try
1047         {
1048             int port = 0;
1049             boolean open = false;
1050             try
1051             {
1052                 port = ((Number)subclass.getMethod( "port",
1053                     new Class[0]).invoke(subclass,new Object[0])).intValue();
1054                 open = ((Boolean)subclass.getMethod( "autoOpenInBrowser",
1055                     new Class[0]).invoke(subclass,new Object[0])).booleanValue();
1056             }
1057             catch ( Throwable t )
1058             {
1059                 System.err.print("Error reading configuration:" );
1060                 t.printStackTrace();
1061             }
1062 
1063             HttpServer server = new HttpServer();
1064             HttpListener listener = server.addListener(new InetAddrPort(port));
1065             org.mortbay.http.HttpContext context = server.getContext("/");
1066             ServletHandler handler = new ServletHandler();
1067             handler.addServlet("/",subclass.getName());
1068             context.addHandler(handler);
1069             server.start();
1070             port = listener.getPort();
1071             System.out.println("Waiting for requests: http://127.0.0.1:" + port);
1072             if ( open )
1073             {
1074                 BrowserLauncher.openURL( "http://127.0.0.1:" + port );
1075             }
1076         }
1077         catch ( Throwable t )
1078         {
1079             t.printStackTrace();
1080         }
1081     }
1082 }
1083 
1084 /*
1085  * $Log$
1086  * Revision 1.2  2006/02/19 01:44:02  cgruber
1087  * Add xmlrpc files
1088  * Remove jclark and replace with dom4j and javax.xml.sax stuff
1089  * Re-work dependencies and imports so it all compiles.
1090  *
1091  * Revision 1.1  2006/02/16 13:22:22  cgruber
1092  * Check in all sources in eclipse-friendly maven-enabled packages.
1093  *
1094  * Revision 1.30  2003/03/28 18:01:19  mpowers
1095  * Now defaulting port to zero.
1096  *
1097  * Revision 1.29  2003/03/28 17:31:58  mpowers
1098  * Implemented support for autoselection of free port.  (thanks gmuth!)
1099  *
1100  * Revision 1.28  2003/03/28 17:26:17  mpowers
1101  * Implemented package support: Applications can now live in packages.
1102  * Better support for locating package local classes.
1103  *
1104  * Revision 1.27  2003/02/21 16:40:22  mpowers
1105  * Now reading port and smtp host from system properties.
1106  * Implemented WOApplication.main.
1107  *
1108  * Revision 1.26  2003/02/14 22:33:18  mpowers
1109  * Better handling for standalone mode.
1110  *
1111  * Revision 1.25  2003/02/14 15:18:27  mpowers
1112  * Now launching standalone app as a servlet, not a webapp.
1113  * Disabled jetty's event logging.
1114  *
1115  * Revision 1.24  2003/02/13 22:41:04  mpowers
1116  * WOApplications can now be self-serving.  Added configuration params too.
1117  *
1118  * Revision 1.23  2003/01/28 19:33:51  mpowers
1119  * Implemented the rest of WOResourceManager.
1120  * Implemented support for java-style i18n.
1121  * Components now use the resource manager to load templates.
1122  *
1123  * Revision 1.22  2003/01/27 15:08:00  mpowers
1124  * Implemented WOResourceManager, using java resources for now.
1125  *
1126  * Revision 1.21  2003/01/24 20:13:22  mpowers
1127  * Now accepting immutable NSDictionary in constructor, not Map.
1128  *
1129  * Revision 1.20  2003/01/20 17:50:11  mpowers
1130  * Caught a loop condition when same declaration was used twice.
1131  *
1132  * Revision 1.19  2003/01/19 22:33:25  mpowers
1133  * Fixed problems with classpath and dynamic class loading.
1134  * Dynamic elements now pass on ensureAwakeInContext.
1135  * Parser how handles <standalone/> tags.
1136  *
1137  * Revision 1.18  2003/01/18 23:54:50  mpowers
1138  * Implemented debugging enabled.
1139  *
1140  * Revision 1.17  2003/01/17 20:58:18  mpowers
1141  * Fixed up WOHyperlink.
1142  *
1143  * Revision 1.16  2003/01/17 20:34:17  mpowers
1144  * Rudimentary support for resource requests.
1145  *
1146  * Revision 1.15  2003/01/17 15:31:56  mpowers
1147  * Removed spurious error message.
1148  *
1149  * Revision 1.14  2003/01/17 14:39:00  mpowers
1150  * Now calling preferred constructor: WOComponent(WOContext)
1151  *
1152  * Revision 1.13  2003/01/16 20:10:46  mpowers
1153  *  - components now synchronize bindings
1154  *  - support for WOComponentContent
1155  *  - implemented performParentAction
1156  *
1157  * Revision 1.12  2003/01/16 15:50:43  mpowers
1158  * More robust declaration parsing.
1159  * Subcomponents are now supported.
1160  * dynamicElementWithName can now return subcomponents.
1161  *
1162  * Revision 1.11  2003/01/15 19:50:49  mpowers
1163  * Fixed issues with WOSession and Serializable.
1164  * Can now persist sessions between classloaders (hot swap of class impls).
1165  *
1166  * Revision 1.10  2003/01/13 22:24:18  mpowers
1167  * Request-response cycle is working with session and page persistence.
1168  *
1169  * Revision 1.9  2003/01/10 20:17:41  mpowers
1170  * Component action urls are now working.
1171  *
1172  * Revision 1.8  2003/01/10 19:16:40  mpowers
1173  * Implemented support for page caching.
1174  *
1175  * Revision 1.4  2002/12/19 17:58:52  mpowers
1176  * Rewrote the template parsing - no longer confused about "root element".
1177  *
1178  * Revision 1.3  2002/12/18 14:12:38  mpowers
1179  * Support for differentiated request handlers.
1180  * Support url generation for WOContext and WORequest.
1181  *
1182  * Revision 1.2  2002/12/17 14:57:41  mpowers
1183  * Minor corrections to WORequests's parsing, and updated javadocs.
1184  *
1185  * Revision 1.1.1.1  2000/12/21 15:52:50  mpowers
1186  * Contributing wotonomy.
1187  *
1188  * Revision 1.2  2000/12/20 16:25:49  michael
1189  * Added log to all files.
1190  *
1191  *
1192  */
1193