Coverage Report - net.wotonomy.web.WOApplication
 
Classes in this File Line Coverage Branch Coverage Complexity
WOApplication
0% 
0% 
1.54
 
 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  0
 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  0
     static private boolean cachingEnabled = false;    
 68  0
     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  0
     static String _DirectActionRequestHandlerKey = "_DirectActionRequestHandlerKey";
 87  0
     static String _ComponentRequestHandlerKey = "_ComponentRequestHandlerKey";
 88  0
     static String _ResourceRequestHandlerKey = "_ResourceRequestHandlerKey";
 89  0
     static String WOPort = "WOPort";
 90  0
     static String WOSMTPHost = "WOSMTPHost";
 91  
     static final String ELEMENT_CLASS = "elementClass";
 92  
 
 93  0
     public WOApplication ()
 94  0
     {
 95  0
         if ( threadLocal == null )
 96  
         {
 97  0
             threadLocal = new ThreadLocal();
 98  
         }
 99  0
         threadLocal.set( this );
 100  
         
 101  
             //application = this;
 102  0
         resourceManager = createResourceManager();
 103  0
         requestHandlers = new NSMutableDictionary();
 104  0
         defaultRequestHandler = new WODirectActionRequestHandler();
 105  0
         registerRequestHandler( defaultRequestHandler, directActionRequestHandlerKey() );
 106  0
         registerRequestHandler( new WOComponentRequestHandler(), componentRequestHandlerKey() );
 107  0
         registerRequestHandler( new WOResourceRequestHandler(), resourceRequestHandlerKey() );
 108  0
         sessionStore = WOSessionStore.serverSessionStore();
 109  
         
 110  0
         pageRefreshOnBacktrack = true;
 111  0
         pageCacheSize = 30;
 112  0
         permanentPageCacheSize = 30;
 113  
         
 114  0
         threadLocal.set( null );
 115  0
     }
 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  0
         threadLocal.set( this );
 128  
 
 129  0
             WORequest request = new WORequest( req, this );
 130  0
         WOResponse response = dispatchRequest( request );
 131  0
         response.generateServletResponse( resp );
 132  0
     }
 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  0
             doGet( req, resp );
 143  0
         }
 144  
         
 145  
         // obtaining attributes
 146  
         
 147  
         /**
 148  
         * Returns the singleton instance of this application.
 149  
         */
 150  
     public static WOApplication application()
 151  
     {
 152  0
         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  0
         if ( name == null )
 164  
         {
 165  0
             name = path();
 166  
             int i;
 167  0
             if ( name.endsWith( "/" ) )
 168  
             { // path
 169  0
                 name = name.substring( 0, name.length() - 1 );
 170  0
             }
 171  
             else
 172  
             { // jar file
 173  0
                 i = name.lastIndexOf( '.' );
 174  0
                 if ( i != -1 ) name = name.substring( 0, i );
 175  
             }
 176  0
             i = name.lastIndexOf( '/' );
 177  0
             if ( i != -1 ) name = name.substring( i+1 );
 178  
         }
 179  0
         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  0
         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  0
         String root = getServletContext().getRealPath( "/" );
 198  0
         String result = path();
 199  0
         if ( result.endsWith("/") )
 200  
         { // path
 201  0
             if ( result.startsWith( root ) )
 202  
             { // relative to root
 203  0
                 result = result.substring( root.length() );
 204  
             }
 205  
             // else leave as absolute reference
 206  
         }
 207  
         // jar or war file: leave as absolute reference
 208  0
         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  0
         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  0
         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  0
         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  0
     }
 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  0
             aContext.session().takeValuesFromRequest( aRequest, aContext );
 265  0
         }
 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  0
             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  0
             aContext.session().appendToResponse( aResponse, aContext );
 283  0
     }
 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  0
     }
 294  
 
 295  
     /**
 296  
     * Dispatches the request to the appropriate handler.
 297  
     */
 298  
     public WOResponse dispatchRequest (WORequest aRequest)
 299  
     {
 300  0
             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  0
         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  0
         defaultRequestHandler = aRequestHandler;
 322  0
     }
 323  
     
 324  
     /**
 325  
     * Registers the specified request handler for the specified key.
 326  
     */
 327  
     public void registerRequestHandler (WORequestHandler aRequestHandler, String aKey)
 328  
     {
 329  0
         requestHandlers.setObjectForKey( aRequestHandler, aKey );
 330  0
     }
 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  0
         WORequestHandler result = requestHandlerForKey( aKey );
 339  0
         requestHandlers.removeObjectForKey( aKey );
 340  0
         return result;
 341  
     }
 342  
     
 343  
     /**
 344  
     * Returns the keys under which request handlers are registered.
 345  
     */
 346  
     public NSArray registeredRequestHandlerKeys ()
 347  
     {
 348  0
         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  0
         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  0
         WORequestHandler result = requestHandlerForKey( aRequest.requestHandlerKey() );
 366  0
         if ( aRequest == null ) result = defaultRequestHandler();
 367  0
         return result;
 368  
     }
 369  
 
 370  
     // handling errors    
 371  
 
 372  
     public WOResponse handleSessionCreationErrorInContext( WOContext aContext )
 373  
     {
 374  0
         WOResponse response = new WOResponse();
 375  0
         response.setStatus( 500 ); // internal server error
 376  
         //TODO: add more useful information to the response
 377  0
         System.err.println( "Failed to create session: " + aContext );
 378  0
 new RuntimeException().printStackTrace(); // remove me
 379  0
         return response;
 380  
     }
 381  
     
 382  
     public WOResponse handleSessionRestorationErrorInContext( WOContext aContext )
 383  
     {
 384  0
         WOResponse response = new WOResponse();
 385  0
         response.setStatus( 500 ); // internal server error
 386  
         //TODO: add more useful information to the response
 387  0
         System.err.println( "Failed to restore session: " + aContext );
 388  0
 new RuntimeException().printStackTrace(); // remove me
 389  0
         return response;
 390  
     }
 391  
     
 392  
     public WOResponse handlePageRestorationErrorInContext( WOContext aContext )
 393  
     {
 394  0
         WOResponse response = new WOResponse();
 395  0
         response.setStatus( 500 ); // internal server error
 396  
         //TODO: add more useful information to the response
 397  0
         System.err.println( "Failed to restore page: " + aContext );
 398  0
 new RuntimeException().printStackTrace(); // remove me
 399  0
         return response;
 400  
     }
 401  
     
 402  
     public WOResponse handleException( Throwable aThrowable, WOContext aContext )
 403  
     {
 404  0
         WOResponse response = new WOResponse();
 405  0
         response.setStatus( 500 ); // internal server error
 406  0
         System.err.println( "Exception occurred: " + aContext );
 407  0
         if ( aThrowable.getMessage() != null )
 408  
         {
 409  0
             response.appendContentString( aThrowable.getMessage() );
 410  0
             aThrowable.printStackTrace();
 411  0
         }
 412  
         else
 413  
         {
 414  0
             response.appendContentString( aThrowable.toString() );
 415  0
             aThrowable.printStackTrace();
 416  
         }
 417  0
         aThrowable.printStackTrace();
 418  0
         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  0
         pageCacheSize = aPositiveInt;
 430  0
     }
 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  0
         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  0
         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  0
         permanentPageCacheSize = aPositiveInt;
 461  0
     }
 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  0
         pageRefreshOnBacktrack = enabled;
 471  0
     }
 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  0
         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  0
         sessionStore = aSessionStore;
 492  0
     }
 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  0
         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  0
         sessionStore.saveSessionForContext( aContext );
 510  0
     }
 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  0
         WORequest request = aContext.request();
 521  0
         WOSession session = sessionStore.restoreSessionWithID( aSessionID, request );
 522  0
         if ( session != null )
 523  
         {
 524  0
             session.setContext( aContext );
 525  0
             session.setServletSession( request.servletRequest().getSession() );
 526  
         }
 527  0
         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  0
         WOSession result = null;
 538  
         try
 539  
         {   
 540  
                         // using our class loader, which is hopefully dynamic.
 541  0
             result = (WOSession) getLocalClass( "Session" ).newInstance();
 542  
         }
 543  0
         catch ( Throwable t )
 544  
         {
 545  
             // ignore: fall back to WOSession
 546  
             //t.printStackTrace();
 547  0
         }
 548  
         
 549  0
         if ( result == null )
 550  
         {
 551  0
             result = new WOSession();
 552  
         }
 553  
         
 554  0
         result.setServletSession( aRequest.servletRequest().getSession( true ) );
 555  0
         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  0
             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  0
         if ( aName == null ) 
 574  
         {
 575  0
             throw new IllegalArgumentException( 
 576  0
                 "WOApplication.pageWithName: name is null" );
 577  
         }
 578  
         
 579  0
         WOComponent result = null;
 580  
         try
 581  
         {   
 582  
                         // using our class loader, which is hopefully dynamic.
 583  0
             Class c = getLocalClass( aName );
 584  
             
 585  0
             if ( c != null )
 586  
             {
 587  
                 // get constructor
 588  
                 Constructor ctor;
 589  
                 try
 590  
                 {
 591  0
                     ctor = c.getConstructor( new Class[] { WOContext.class } );
 592  
                 }
 593  0
                 catch ( NoSuchMethodException nsme )
 594  
                 {
 595  0
                     ctor = null;
 596  0
                 }
 597  
                 
 598  
                 // create instance of class
 599  0
                 if ( ctor != null )
 600  
                 {
 601  0
                     result = (WOComponent) ctor.newInstance( new Object[] { aContext } );
 602  0
                 }
 603  
                 else // call back on default constructor (deprecated)
 604  
                 {
 605  0
                     result = (WOComponent) c.newInstance();
 606  
                 }
 607  
             }
 608  
         }
 609  0
         catch ( Throwable t )
 610  
         {
 611  
             // ignore for now
 612  
             //TODO: Throw appropriate exception here
 613  
             //System.err.println( "Not found: pageWithName: " + aName );
 614  0
             t.printStackTrace();
 615  0
         }
 616  
 
 617  0
         if ( result != null && aContext != null )
 618  
         {
 619  
             // this is where components get their context
 620  0
             result.ensureAwakeInContext( aContext );
 621  0
         }
 622  
         else 
 623  0
         if ( result == null )
 624  
         {
 625  0
             System.err.println( "Not found: pageWithName: " + aName );
 626  
         }
 627  
         
 628  0
         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  0
         Class result = null;
 639  0
         if ( getClass() != WOApplication.class )
 640  
         {
 641  0
             result = loadLocalClass( getClass(), aName );
 642  
         }
 643  0
         if ( result == null )
 644  
         {
 645  0
             result = loadLocalClass( WOApplication.class, aName );
 646  
         }
 647  0
         if ( result == null )
 648  
         {
 649  0
             result = loadLocalClass( null, aName );
 650  
         }
 651  0
         return result;
 652  
     }
 653  
     
 654  
     private static final Class loadLocalClass( Class aClass, String aName )
 655  
     {
 656  
         ClassLoader loader;
 657  0
         String packageName = "";
 658  0
         if ( aClass != null )
 659  
         {
 660  0
             loader = aClass.getClassLoader();
 661  0
             packageName = aClass.getName();
 662  0
             int index = packageName.lastIndexOf( "." );
 663  0
             if ( index > -1 ) 
 664  
             {
 665  0
                 packageName = packageName.substring( 0, index+1 );
 666  0
             }
 667  
             else
 668  
             {
 669  0
                 packageName = "";
 670  
             }
 671  0
         }
 672  
         else
 673  
         {
 674  0
             loader = WOApplication.class.getClassLoader();
 675  
         }
 676  
         
 677  
         try
 678  
         {
 679  0
             return loader.loadClass( packageName + aName );
 680  
         }
 681  0
         catch ( ClassNotFoundException e )
 682  
         {
 683  0
             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  0
         WOElement element = null;
 698  0
         Class c = null;
 699  
         try
 700  
         {
 701  0
             c = getLocalClass( anElementName );
 702  0
             if ( c == null )
 703  
             {
 704  0
                 System.out.println( "Not found: dynamicElementWithName: " +
 705  0
                     "could not find WODynamicElement subclass: " + anElementName );
 706  0
                 c = WODynamicElement.class;
 707  
             }
 708  
 
 709  
             // get constructor
 710  0
             Class[] params = new Class[]
 711  0
                 { String.class, NSDictionary.class, WOElement.class };
 712  0
             Constructor ctor = c.getConstructor( params );
 713  
     
 714  
             // create instance of class
 715  0
             if ( ctor != null )
 716  
             {
 717  0
                 element = (WODynamicElement) ctor.newInstance( 
 718  0
                     new Object[] { anElementName, anAssociationMap, aBodyElement } );
 719  
             }
 720  
         }
 721  0
         catch ( Throwable t )
 722  
         {
 723  
             // ignore: not a dynamic element
 724  
             //System.out.println( "Not a dynamic element: dynamicElementWithName: " + t );
 725  
             //exc.printStackTrace();
 726  0
         }
 727  
         
 728  
         // no dynamic element found: look for a component
 729  0
         if ( element == null )
 730  
         {
 731  0
             WOComponent component = (WOComponent) pageWithName( anElementName, (WOContext) null );
 732  
 
 733  
             // this seems hackish: 
 734  
             // I don't see another way of setting the bindings.
 735  0
             component.associations = anAssociationMap;
 736  0
             component.rootElement = aBodyElement;
 737  
             
 738  0
             element = component;
 739  
         }
 740  
         
 741  0
         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  0
         return new WOResourceManager();
 753  
     }
 754  
 
 755  
     /**
 756  
     * Returns the application's current resource manager.
 757  
     */
 758  
     public WOResourceManager resourceManager()
 759  
     {
 760  0
         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  0
         resourceManager = aResourceManager;
 770  0
     }
 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  0
     static boolean debuggingEnabled = false;
 859  
     /**
 860  
     * Returns whether the application is in "debug mode".
 861  
     */
 862  
     public static boolean isDebuggingEnabled()
 863  
     {
 864  0
         return debuggingEnabled;
 865  
     }
 866  
 
 867  
     /**
 868  
     * Sets whether the application is in "debug mode".
 869  
     */
 870  
     public static void setDebuggingEnabled( boolean enabled )
 871  
     {
 872  0
         debuggingEnabled = enabled;
 873  0
     }
 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  0
                 cachingEnabled = enabled;
 883  0
         }
 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  0
             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  0
         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  0
         System.setProperty( _ComponentRequestHandlerKey, aKey );
 914  0
     }
 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  0
         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  0
         System.setProperty( _DirectActionRequestHandlerKey, aKey );
 933  0
     }
 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  0
         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  0
         System.setProperty( _ResourceRequestHandlerKey, aKey );
 952  0
     }
 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  0
         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  0
         autoOpenInBrowser = autoOpen;
 971  0
     }
 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  0
         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  0
         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  0
         System.setProperty( WOSMTPHost, aHost );
 1000  0
     }
 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  0
         main( argv, WOApplication.class );
 1038  0
     }
 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  0
             int port = 0;
 1049  0
             boolean open = false;
 1050  
             try
 1051  
             {
 1052  0
                 port = ((Number)subclass.getMethod( "port",
 1053  0
                     new Class[0]).invoke(subclass,new Object[0])).intValue();
 1054  0
                 open = ((Boolean)subclass.getMethod( "autoOpenInBrowser",
 1055  0
                     new Class[0]).invoke(subclass,new Object[0])).booleanValue();
 1056  
             }
 1057  0
             catch ( Throwable t )
 1058  
             {
 1059  0
                 System.err.print("Error reading configuration:" );
 1060  0
                 t.printStackTrace();
 1061  0
             }
 1062  
 
 1063  0
             HttpServer server = new HttpServer();
 1064  0
             HttpListener listener = server.addListener(new InetAddrPort(port));
 1065  0
             org.mortbay.http.HttpContext context = server.getContext("/");
 1066  0
             ServletHandler handler = new ServletHandler();
 1067  0
             handler.addServlet("/",subclass.getName());
 1068  0
             context.addHandler(handler);
 1069  0
             server.start();
 1070  0
             port = listener.getPort();
 1071  0
             System.out.println("Waiting for requests: http://127.0.0.1:" + port);
 1072  0
             if ( open )
 1073  
             {
 1074  0
                 BrowserLauncher.openURL( "http://127.0.0.1:" + port );
 1075  
             }
 1076  
         }
 1077  0
         catch ( Throwable t )
 1078  
         {
 1079  0
             t.printStackTrace();
 1080  0
         }
 1081  0
     }
 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