/*
Wotonomy: OpenStep design patterns for pure Java applications.
Copyright (C) 2000 Blacksmith, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see http://www.gnu.org
*/
package net.wotonomy.web;
import java.lang.reflect.Constructor;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.wotonomy.foundation.NSArray;
import net.wotonomy.foundation.NSDictionary;
import net.wotonomy.foundation.NSMutableDictionary;
import net.wotonomy.web.util.BrowserLauncher;
import org.mortbay.http.HttpListener;
import org.mortbay.http.HttpServer;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.util.InetAddrPort;
/**
* A pure java implementation of WOApplication.
*
* The application is responsible for creating and managing sessions
* and dispatching requests to the appropriate handlers.
*
* This implementation extends HttpServlet, so the application itself
* is a servlet and can be configured and managed as such by the servlet
* container.
*
* @author michael@mpowers.net
* @author $Author: cgruber $
* @version $Revision: 905 $
*/
public class WOApplication
extends HttpServlet
{
/**
* A tricky way to allow multiple WOApplications
* in the same servlet container.
*/
static private ThreadLocal threadLocal;
//static private WOApplication application;
/**
* Determines application-wide page caching.
* Pages may individually prevent caching.
*/
static private boolean cachingEnabled = false;
private static boolean autoOpenInBrowser = true;
private String name;
private WORequestHandler defaultRequestHandler;
private NSMutableDictionary requestHandlers;
private WOSessionStore sessionStore;
private WOResourceManager resourceManager;
private boolean pageRefreshOnBacktrack;
private int pageCacheSize;
private int permanentPageCacheSize;
public static final String WOApplicationWillFinishLaunchingNotification
= "WOApplicationWillFinishLaunchingNotification";
public static final String WOApplicationDidFinishLaunchingNotification
= "WOApplicationDidFinishLaunchingNotification";
public static final String WOGarbageCollectionPeriodKey
= "WOGarbageCollectionPeriodKey";
static String _DirectActionRequestHandlerKey = "_DirectActionRequestHandlerKey";
static String _ComponentRequestHandlerKey = "_ComponentRequestHandlerKey";
static String _ResourceRequestHandlerKey = "_ResourceRequestHandlerKey";
static String WOPort = "WOPort";
static String WOSMTPHost = "WOSMTPHost";
static final String ELEMENT_CLASS = "elementClass";
public WOApplication ()
{
if ( threadLocal == null )
{
threadLocal = new ThreadLocal();
}
threadLocal.set( this );
//application = this;
resourceManager = createResourceManager();
requestHandlers = new NSMutableDictionary();
defaultRequestHandler = new WODirectActionRequestHandler();
registerRequestHandler( defaultRequestHandler, directActionRequestHandlerKey() );
registerRequestHandler( new WOComponentRequestHandler(), componentRequestHandlerKey() );
registerRequestHandler( new WOResourceRequestHandler(), resourceRequestHandlerKey() );
sessionStore = WOSessionStore.serverSessionStore();
pageRefreshOnBacktrack = true;
pageCacheSize = 30;
permanentPageCacheSize = 30;
threadLocal.set( null );
}
/**
* Dispatches the request and updates the specified response
* as appropriate. This implementation creates a new WORequest
* and WOContext from the request, sends the response to the
* appropriate WORequestHandler, and then updates the servlet
* response from the resulting WOResponse.
*/
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException
{
threadLocal.set( this );
WORequest request = new WORequest( req, this );
WOResponse response = dispatchRequest( request );
response.generateServletResponse( resp );
}
/**
* Handles post requests by calling doGet(), since the framework
* handles both gets and posts similarly. Override to handle
* post requests in a different manner.
*/
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException
{
doGet( req, resp );
}
// obtaining attributes
/**
* Returns the singleton instance of this application.
*/
public static WOApplication application()
{
return (WOApplication) threadLocal.get();
//return application;
}
/**
* Returns the name of the application. This implementation returns
* the name the jar file or directory from which the class was loaded,
* with no extensions.
*/
public String name()
{
if ( name == null )
{
name = path();
int i;
if ( name.endsWith( "/" ) )
{ // path
name = name.substring( 0, name.length() - 1 );
}
else
{ // jar file
i = name.lastIndexOf( '.' );
if ( i != -1 ) name = name.substring( 0, i );
}
i = name.lastIndexOf( '/' );
if ( i != -1 ) name = name.substring( i+1 );
}
return name;
}
/**
* Returns the absolute path to the application on the local file system.
* Note that the application might be embedded inside of a jar file.
*/
public String path ()
{
return getClass().getProtectionDomain().getCodeSource().getLocation().toString();
}
/**
* Returns the path to the application on the local file system
* relative to the server's document root.
*/
public String baseURL ()
{
String root = getServletContext().getRealPath( "/" );
String result = path();
if ( result.endsWith("/") )
{ // path
if ( result.startsWith( root ) )
{ // relative to root
result = result.substring( root.length() );
}
// else leave as absolute reference
}
// jar or war file: leave as absolute reference
return result;
}
// concurrent request handling
/**
* Returns whether this application allows request
* to be handled concurrently.
* This implementation returns true.
* Subclasses may override to return false to force
* single-threaded request handling, although this
* is not implemented.
*/
public boolean allowsConcurrentRequestHandling ()
{
return true;
}
/**
* Returns whether this application allows request
* to be handled concurrently.
* This implementation returns true.
*/
public boolean adaptorsDispatchRequestsConcurrently ()
{
return true;
}
/**
* Returns whether this application allows request
* to be handled concurrently.
* This implementation returns true.
*/
public boolean isConcurrentRequestHandlingEnabled ()
{
return true;
}
// handling requests
/**
* Invoked first in the request-response cycle.
* Override to perform any kind of initialization at the
* start of a request. This implementation does nothing.
*/
public void awake ()
{
}
/**
* Invoked to start the first phase of the request-response cycle,
* after all calls to awake() have been completed.
*/
public void takeValuesFromRequest (WORequest aRequest, WOContext aContext)
{
aContext.session().takeValuesFromRequest( aRequest, aContext );
}
/**
* Invoked to start the second phase of the request-response cycle,
* after all calls to takeValuesFromRequest have finished.
*/
public WOActionResults invokeAction (WORequest aRequest, WOContext aContext)
{
return aContext.session().invokeAction( aRequest, aContext );
}
/**
* Invoked to start the third phase of the request-response cycle,
* after invokeAction() has completed and returned a WOResponse.
*/
public void appendToResponse (WOResponse aResponse, WOContext aContext)
{
aContext.session().appendToResponse( aResponse, aContext );
}
/**
* Invoked last in the request-response cycle.
* Override to perform any kind of clean-up at the
* end of a request. This implementation does nothing.
*/
public void sleep ()
{
}
/**
* Dispatches the request to the appropriate handler.
*/
public WOResponse dispatchRequest (WORequest aRequest)
{
return handlerForRequest( aRequest ).handleRequest( aRequest );
}
// request handling
/**
* Returns the default request handler used if the requested
* handler isn't specified or cannot be found. (This defaults
* to the WODirectActionRequestHandler.)
*/
public WORequestHandler defaultRequestHandler ()
{
return defaultRequestHandler;
}
/**
* Sets the default request handler used if the requested
* handler isn't specified or cannot be found.
*/
public void setDefaultRequestHandler (WORequestHandler aRequestHandler)
{
defaultRequestHandler = aRequestHandler;
}
/**
* Registers the specified request handler for the specified key.
*/
public void registerRequestHandler (WORequestHandler aRequestHandler, String aKey)
{
requestHandlers.setObjectForKey( aRequestHandler, aKey );
}
/**
* Unregisters any existing request handler for the specified key
* returning the existing request handler, if any.
*/
public WORequestHandler removeRequestHandlerForKey (String aKey)
{
WORequestHandler result = requestHandlerForKey( aKey );
requestHandlers.removeObjectForKey( aKey );
return result;
}
/**
* Returns the keys under which request handlers are registered.
*/
public NSArray registeredRequestHandlerKeys ()
{
return requestHandlers.allKeys();
}
/**
* Returns the request handler registered for the specified key,
* or null if no request handler is registered for that key.
*/
public WORequestHandler requestHandlerForKey (String aKey)
{
return (WORequestHandler) requestHandlers.objectForKey( aKey );
}
/**
* Returns the request handler that would best service the specified request.
*/
public WORequestHandler handlerForRequest (WORequest aRequest)
{
WORequestHandler result = requestHandlerForKey( aRequest.requestHandlerKey() );
if ( aRequest == null ) result = defaultRequestHandler();
return result;
}
// handling errors
public WOResponse handleSessionCreationErrorInContext( WOContext aContext )
{
WOResponse response = new WOResponse();
response.setStatus( 500 ); // internal server error
//TODO: add more useful information to the response
System.err.println( "Failed to create session: " + aContext );
new RuntimeException().printStackTrace(); // remove me
return response;
}
public WOResponse handleSessionRestorationErrorInContext( WOContext aContext )
{
WOResponse response = new WOResponse();
response.setStatus( 500 ); // internal server error
//TODO: add more useful information to the response
System.err.println( "Failed to restore session: " + aContext );
new RuntimeException().printStackTrace(); // remove me
return response;
}
public WOResponse handlePageRestorationErrorInContext( WOContext aContext )
{
WOResponse response = new WOResponse();
response.setStatus( 500 ); // internal server error
//TODO: add more useful information to the response
System.err.println( "Failed to restore page: " + aContext );
new RuntimeException().printStackTrace(); // remove me
return response;
}
public WOResponse handleException( Throwable aThrowable, WOContext aContext )
{
WOResponse response = new WOResponse();
response.setStatus( 500 ); // internal server error
System.err.println( "Exception occurred: " + aContext );
if ( aThrowable.getMessage() != null )
{
response.appendContentString( aThrowable.getMessage() );
aThrowable.printStackTrace();
}
else
{
response.appendContentString( aThrowable.toString() );
aThrowable.printStackTrace();
}
aThrowable.printStackTrace();
return response;
}
// managing pages
/**
* Sets the number of pages that will be retained
* in the user's session. Set to zero to disable page caching.
*/
public void setPageCacheSize (int aPositiveInt)
{
pageCacheSize = aPositiveInt;
}
/**
* Returns the number of pages that will be retained
* in the user's session. The default page cache size is 30.
*/
public int pageCacheSize ()
{
return pageCacheSize;
}
/**
* Returns the number of pages that will be retained in the
* longer-term "permanent" page cache in the user's session,
* which is typically used for navigation bars in frames, etc.
* The default permanent page cache size is 30.
*/
public int permanentPageCacheSize ()
{
return permanentPageCacheSize;
}
/**
* Returns the number of pages that will be retained in the
* longer-term "permanent" page cache in the user's session,
* which is typically used for navigation bars in frames, etc.
* Set to zero to disable permanent page caching.
*/
public void setPermanentPageCacheSize (int aPositiveInt)
{
permanentPageCacheSize = aPositiveInt;
}
/**
* Returns whether a "backtrack" for an existing page should
* simply call generateResponse() on the existing page instance.
* If false, a new page is created instead. The default is true.
*/
public void setPageRefreshOnBacktrackEnabled (boolean enabled)
{
pageRefreshOnBacktrack = enabled;
}
/**
* Returns whether a "backtrack" for an existing page should
* simply call generateResponse() on the existing page instance.
* If false, a new page is created instead. The default is true.
*/
public boolean isPageRefreshOnBacktrackEnabled ()
{
return pageRefreshOnBacktrack;
}
// managing sessions
/**
* Sets the session store used by this application to persist
* sessions between request-response transactions.
*/
public void setSessionStore(WOSessionStore aSessionStore)
{
sessionStore = aSessionStore;
}
/**
* Returns the session store used by this application to persist
* sessions between request-response transactions.
*/
public WOSessionStore sessionStore()
{
return sessionStore;
}
/**
* Called at the end of the request-response cycle
* to persist the current session until the user's next request.
*/
public void saveSessionForContext(WOContext aContext)
{
sessionStore.saveSessionForContext( aContext );
}
/**
* Called at the beginning of the request-response cycle
* to obtain the current session from the user's last request.
* Returns null if no such session has been created.
* This method sets the context of the session to the specified context.
*/
public WOSession restoreSessionWithID(String aSessionID, WOContext aContext)
{
WORequest request = aContext.request();
WOSession session = sessionStore.restoreSessionWithID( aSessionID, request );
if ( session != null )
{
session.setContext( aContext );
session.setServletSession( request.servletRequest().getSession() );
}
return session;
}
/**
* Called to create a session for a new request. This implementation
* looks for a class in the same package as the application class
* called "Session" and failing that returns a WOSession.
*/
public WOSession createSessionForRequest(WORequest aRequest)
{
WOSession result = null;
try
{
// using our class loader, which is hopefully dynamic.
result = (WOSession) getLocalClass( "Session" ).newInstance();
}
catch ( Throwable t )
{
// ignore: fall back to WOSession
//t.printStackTrace();
}
if ( result == null )
{
result = new WOSession();
}
result.setServletSession( aRequest.servletRequest().getSession( true ) );
return result;
}
/**
* Returns the page component with the specified name.
* A context is created with the specified request,
* along with a session if necessary.
*/
public WOComponent pageWithName (String aName, WORequest aRequest)
{
return pageWithName( aName, WOContext.contextWithRequest( aRequest ) );
}
/**
* Called to retrieve a component for the specified context.
*/
public WOComponent pageWithName (String aName, WOContext aContext)
{
if ( aName == null )
{
throw new IllegalArgumentException(
"WOApplication.pageWithName: name is null" );
}
WOComponent result = null;
try
{
// using our class loader, which is hopefully dynamic.
Class c = getLocalClass( aName );
if ( c != null )
{
// get constructor
Constructor ctor;
try
{
ctor = c.getConstructor( new Class[] { WOContext.class } );
}
catch ( NoSuchMethodException nsme )
{
ctor = null;
}
// create instance of class
if ( ctor != null )
{
result = (WOComponent) ctor.newInstance( new Object[] { aContext } );
}
else // call back on default constructor (deprecated)
{
result = (WOComponent) c.newInstance();
}
}
}
catch ( Throwable t )
{
// ignore for now
//TODO: Throw appropriate exception here
//System.err.println( "Not found: pageWithName: " + aName );
t.printStackTrace();
}
if ( result != null && aContext != null )
{
// this is where components get their context
result.ensureAwakeInContext( aContext );
}
else
if ( result == null )
{
System.err.println( "Not found: pageWithName: " + aName );
}
return result;
}
/**
* Returns a class in the same package as the Application class,
* or, failing that, from the WOApplication package, or finally
* from the root of the class path. Returns null if not found.
*/
Class getLocalClass( String aName )
{
Class result = null;
if ( getClass() != WOApplication.class )
{
result = loadLocalClass( getClass(), aName );
}
if ( result == null )
{
result = loadLocalClass( WOApplication.class, aName );
}
if ( result == null )
{
result = loadLocalClass( null, aName );
}
return result;
}
private static final Class loadLocalClass( Class aClass, String aName )
{
ClassLoader loader;
String packageName = "";
if ( aClass != null )
{
loader = aClass.getClassLoader();
packageName = aClass.getName();
int index = packageName.lastIndexOf( "." );
if ( index > -1 )
{
packageName = packageName.substring( 0, index+1 );
}
else
{
packageName = "";
}
}
else
{
loader = WOApplication.class.getClassLoader();
}
try
{
return loader.loadClass( packageName + aName );
}
catch ( ClassNotFoundException e )
{
return null;
}
}
// creating elements
/**
* Returns either a dynamic element or a component
* for the specified name.
*/
public WOElement dynamicElementWithName(
String anElementName, NSDictionary anAssociationMap,
WOElement aBodyElement, List aLanguageList)
{
WOElement element = null;
Class c = null;
try
{
c = getLocalClass( anElementName );
if ( c == null )
{
System.out.println( "Not found: dynamicElementWithName: " +
"could not find WODynamicElement subclass: " + anElementName );
c = WODynamicElement.class;
}
// get constructor
Class[] params = new Class[]
{ String.class, NSDictionary.class, WOElement.class };
Constructor ctor = c.getConstructor( params );
// create instance of class
if ( ctor != null )
{
element = (WODynamicElement) ctor.newInstance(
new Object[] { anElementName, anAssociationMap, aBodyElement } );
}
}
catch ( Throwable t )
{
// ignore: not a dynamic element
//System.out.println( "Not a dynamic element: dynamicElementWithName: " + t );
//exc.printStackTrace();
}
// no dynamic element found: look for a component
if ( element == null )
{
WOComponent component = (WOComponent) pageWithName( anElementName, (WOContext) null );
// this seems hackish:
// I don't see another way of setting the bindings.
component.associations = anAssociationMap;
component.rootElement = aBodyElement;
element = component;
}
return element;
}
// resource handling
/**
* Called to create the application's resource manager.
* Override to create a custom resource manager.
*/
public WOResourceManager createResourceManager()
{
return new WOResourceManager();
}
/**
* Returns the application's current resource manager.
*/
public WOResourceManager resourceManager()
{
return resourceManager;
}
/**
* Installs a custom resource manager into the current application.
* @deprecated Override createResourceManager() instead.
*/
public void setResourceManager(WOResourceManager aResourceManager)
{
resourceManager = aResourceManager;
}
/*
// request handling undocumented
public WOComponent pageWithName (String);
public void savePage (WOComponent);
public WOComponent restorePageForContextID (String);
public WOContext context ();
public WOSession session ();
public WOSession createSession ();
public WOSession restoreSession ();
public void saveSession (WOSession);
public WOResponse handleRequest (WORequest aRequest)
{
}
// error handling undocumented
WOResponse handleSessionCreationError ();
WOResponse handleSessionRestorationError ();
WOResponse handlePageRestorationError ();
WOResponse handleException (Throwable);
// running
public NSRunLoop runLoop ();
public void run ();
public void setTimeOut (double);
public double timeOut ();
public void terminate ();
public boolean isTerminating ();
// script debugging
public void traceScriptedMessages (boolean);
public void traceAssignments (boolean);
public void traceStatements (boolean);
public void traceObjectiveCMessages (boolean);
public void trace (boolean);
public void logTakeValueForDeclarationNamed (String, String, String, String, Object);
public void logSetValueForDeclarationNamed (String, String, String, String, Object);
// script handling
public String scriptedClassNameWithPath (String);
public String scriptedClassNameWithPathEncoding (String, int);
// statistics report
public void setStatisticsStore (WOStatisticsStore);
public WOStatisticsStore statisticsStore ();
public NSDictionary statistics ();
// managing adaptors
public WOAdaptor adaptorWithName (String, NSDictionary);
public NSArray adaptors ();
// monitor support
public boolean monitoringEnabled ();
public int activeSessionsCount ();
public void refuseNewSessions (boolean);
public boolean isRefusingNewSessions ();
public void setMinimumActiveSessionsCount (int);
public int minimumActiveSessionsCount ();
public void terminateAfterTimeInterval (double);
// garbage collection undocumented
int garbageCollectionPeriod ();
void setGarbageCollectionPeriod (int);
// backwards compatibility
public boolean requiresWOF35RequestHandling ();
public boolean requiresWOF35TemplateParser ();
public void setPrintsHTMLParserDiagnostics (boolean);
public boolean printsHTMLParserDiagnostics ();
// configuration and defaults
public static NSArray loadFrameworks ();
public static void setLoadFrameworks (NSArray);
*/
static boolean debuggingEnabled = false;
/**
* Returns whether the application is in "debug mode".
*/
public static boolean isDebuggingEnabled()
{
return debuggingEnabled;
}
/**
* Sets whether the application is in "debug mode".
*/
public static void setDebuggingEnabled( boolean enabled )
{
debuggingEnabled = enabled;
}
/**
* Sets whether templates are cached. If true, templates will
* only be read once per application lifetime. Otherwise, templates
* will be read each time this class is instantiated. Defaults to false.
*/
public static void setCachingEnabled (boolean enabled)
{
cachingEnabled = enabled;
}
/**
* Returns whether templates are cached. If true, templates are
* read once per application lifetime. Otherwise, templates are
* read each time this class is instantiated.
*/
public static boolean isCachingEnabled ()
{
return cachingEnabled;
}
// configuration
/**
* Returns the component request handler key,
* which is defined by the system property _ComponentRequestHandlerKey.
* The default is "wo".
*/
public static String componentRequestHandlerKey()
{
return System.getProperty( _ComponentRequestHandlerKey, "wo" );
}
/**
* Sets the component request handler key.
* @deprecated Set the system property _ComponentRequestHandlerKey.
*/
public static void setComponentRequestHandlerKey(String aKey)
{
System.setProperty( _ComponentRequestHandlerKey, aKey );
}
/**
* Returns the direct action request handler key,
* which is defined by the system property _DirectActionRequestHandlerKey.
* The default is "wa".
*/
public static String directActionRequestHandlerKey()
{
return System.getProperty( _DirectActionRequestHandlerKey, "wa" );
}
/**
* Sets the direct action request handler key.
* @deprecated Set the system property _DirectActionRequestHandlerKey.
*/
public static void setDirectActionRequestHandlerKey(String aKey)
{
System.setProperty( _DirectActionRequestHandlerKey, aKey );
}
/**
* Returns the resource request handler key,
* which is defined by the system property _ResourceRequestHandlerKey.
* The default is "wr".
*/
public static String resourceRequestHandlerKey()
{
return System.getProperty( _ResourceRequestHandlerKey, "wr" );
}
/**
* Sets the resource request handler key.
* @deprecated Set the system property _ResourceRequestHandlerKey.
*/
public static void setResourceRequestHandlerKey(String aKey)
{
System.setProperty( _ResourceRequestHandlerKey, aKey );
}
/**
* Returns whether this application should attempt to open
* a web browser on the host machine when launched standalone.
* The default is true.
*/
public static boolean autoOpenInBrowser()
{
return autoOpenInBrowser;
}
/**
* Sets whether this application should attempt to open
* a web browser on the host machine when launched standalone.
*/
public static void setAutoOpenInBrowser(boolean autoOpen)
{
autoOpenInBrowser = autoOpen;
}
/**
* Gets the port used when run as a standalone server.
* Returns the value of the system property WOPort.
* By default, this is zero, which causes the application
* to automatically select an available port.
*/
public static Number port ()
{
return Integer.getInteger( WOPort, 0 );
}
/**
* Gets the smtp server that will be used to send email.
* Returns the system property WOSMTPHost.
*/
public static String SMTPHost()
{
return System.getProperty( WOSMTPHost );
}
/**
* Sets the smtp server that will be used to send email.
* @deprecated Set the system property WOSMTPHost.
*/
public static void setSMTPHost( String aHost )
{
System.setProperty( WOSMTPHost, aHost );
}
/*
public static boolean isDirectConnectEnabled ();
public static void setDirectConnectEnabled (boolean);
public static String cgiAdaptorURL ();
public static void setCGIAdaptorURL (String);
public static String applicationBaseURL ();
public static void setApplicationBaseURL (String);
public static String frameworksBaseURL ();
public static void setFrameworksBaseURL (String);
public static String recordingPath ();
public static void setRecordingPath (String);
public static NSArray projectSearchPath ();
public static void setProjectSearchPath (NSArray);
public static boolean isMonitorEnabled ();
public static void setMonitorEnabled (boolean);
public static String monitorHost ();
public static String adaptor ();
public String number (); // deprecated
public static Number listenQueueSize ();
public static void setListenQueueSize (Number);
public static NSArray additionalAdaptors ();
public static void setAdditionalAdaptors (NSArray);
public static boolean includeCommentsInResponses ();
public static void setIncludeCommentsInResponses (boolean);
public static void setSessionTimeOut (Number);
public static Number sessionTimeOut ();
public static void logString (String);
public static void debugString (String);
public static void logToMonitorString (String);
*/
/**
* Main entry point for applications that do not subclass WOApplication.
*/
public static void main( String[] argv )
{
main( argv, WOApplication.class );
}
/**
* Subclasses may call this method to start a self-hosted
* web server to serve themselves directly (for testing).
*/
public static void main( String[] argv, Class subclass )
{
try
{
int port = 0;
boolean open = false;
try
{
port = ((Number)subclass.getMethod( "port",
new Class[0]).invoke(subclass,new Object[0])).intValue();
open = ((Boolean)subclass.getMethod( "autoOpenInBrowser",
new Class[0]).invoke(subclass,new Object[0])).booleanValue();
}
catch ( Throwable t )
{
System.err.print("Error reading configuration:" );
t.printStackTrace();
}
HttpServer server = new HttpServer();
HttpListener listener = server.addListener(new InetAddrPort(port));
org.mortbay.http.HttpContext context = server.getContext("/");
ServletHandler handler = new ServletHandler();
handler.addServlet("/",subclass.getName());
context.addHandler(handler);
server.start();
port = listener.getPort();
System.out.println("Waiting for requests: http://127.0.0.1:" + port);
if ( open )
{
BrowserLauncher.openURL( "http://127.0.0.1:" + port );
}
}
catch ( Throwable t )
{
t.printStackTrace();
}
}
}
/*
* $Log$
* Revision 1.2 2006/02/19 01:44:02 cgruber
* Add xmlrpc files
* Remove jclark and replace with dom4j and javax.xml.sax stuff
* Re-work dependencies and imports so it all compiles.
*
* Revision 1.1 2006/02/16 13:22:22 cgruber
* Check in all sources in eclipse-friendly maven-enabled packages.
*
* Revision 1.30 2003/03/28 18:01:19 mpowers
* Now defaulting port to zero.
*
* Revision 1.29 2003/03/28 17:31:58 mpowers
* Implemented support for autoselection of free port. (thanks gmuth!)
*
* Revision 1.28 2003/03/28 17:26:17 mpowers
* Implemented package support: Applications can now live in packages.
* Better support for locating package local classes.
*
* Revision 1.27 2003/02/21 16:40:22 mpowers
* Now reading port and smtp host from system properties.
* Implemented WOApplication.main.
*
* Revision 1.26 2003/02/14 22:33:18 mpowers
* Better handling for standalone mode.
*
* Revision 1.25 2003/02/14 15:18:27 mpowers
* Now launching standalone app as a servlet, not a webapp.
* Disabled jetty's event logging.
*
* Revision 1.24 2003/02/13 22:41:04 mpowers
* WOApplications can now be self-serving. Added configuration params too.
*
* Revision 1.23 2003/01/28 19:33:51 mpowers
* Implemented the rest of WOResourceManager.
* Implemented support for java-style i18n.
* Components now use the resource manager to load templates.
*
* Revision 1.22 2003/01/27 15:08:00 mpowers
* Implemented WOResourceManager, using java resources for now.
*
* Revision 1.21 2003/01/24 20:13:22 mpowers
* Now accepting immutable NSDictionary in constructor, not Map.
*
* Revision 1.20 2003/01/20 17:50:11 mpowers
* Caught a loop condition when same declaration was used twice.
*
* Revision 1.19 2003/01/19 22:33:25 mpowers
* Fixed problems with classpath and dynamic class loading.
* Dynamic elements now pass on ensureAwakeInContext.
* Parser how handles tags.
*
* Revision 1.18 2003/01/18 23:54:50 mpowers
* Implemented debugging enabled.
*
* Revision 1.17 2003/01/17 20:58:18 mpowers
* Fixed up WOHyperlink.
*
* Revision 1.16 2003/01/17 20:34:17 mpowers
* Rudimentary support for resource requests.
*
* Revision 1.15 2003/01/17 15:31:56 mpowers
* Removed spurious error message.
*
* Revision 1.14 2003/01/17 14:39:00 mpowers
* Now calling preferred constructor: WOComponent(WOContext)
*
* Revision 1.13 2003/01/16 20:10:46 mpowers
* - components now synchronize bindings
* - support for WOComponentContent
* - implemented performParentAction
*
* Revision 1.12 2003/01/16 15:50:43 mpowers
* More robust declaration parsing.
* Subcomponents are now supported.
* dynamicElementWithName can now return subcomponents.
*
* Revision 1.11 2003/01/15 19:50:49 mpowers
* Fixed issues with WOSession and Serializable.
* Can now persist sessions between classloaders (hot swap of class impls).
*
* Revision 1.10 2003/01/13 22:24:18 mpowers
* Request-response cycle is working with session and page persistence.
*
* Revision 1.9 2003/01/10 20:17:41 mpowers
* Component action urls are now working.
*
* Revision 1.8 2003/01/10 19:16:40 mpowers
* Implemented support for page caching.
*
* Revision 1.4 2002/12/19 17:58:52 mpowers
* Rewrote the template parsing - no longer confused about "root element".
*
* Revision 1.3 2002/12/18 14:12:38 mpowers
* Support for differentiated request handlers.
* Support url generation for WOContext and WORequest.
*
* Revision 1.2 2002/12/17 14:57:41 mpowers
* Minor corrections to WORequests's parsing, and updated javadocs.
*
* Revision 1.1.1.1 2000/12/21 15:52:50 mpowers
* Contributing wotonomy.
*
* Revision 1.2 2000/12/20 16:25:49 michael
* Added log to all files.
*
*
*/