diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/ContextPopulator.java b/projects/samskivert/src/java/com/samskivert/webmacro/ContextPopulator.java deleted file mode 100644 index 9e4e7845..00000000 --- a/projects/samskivert/src/java/com/samskivert/webmacro/ContextPopulator.java +++ /dev/null @@ -1,30 +0,0 @@ -// -// $Id: ContextPopulator.java,v 1.1 2001/02/15 01:44:34 mdb Exp $ - -package com.samskivert.webmacro; - -import org.webmacro.servlet.WebContext; - -/** - * The context populator is called upon to populate the WebMacro web - * context, prior to invoking a particular WebMacro template upon it to - * generate a response page. The populator takes the place of the servlet - * in the standard WebMacro architecture and should perform all of the - * logic involved in handling a particular request. - * - * @see DispatcherServlet - */ -public interface ContextPopulator -{ - /** - * Perform any necessary computation and populate the context with - * data for this request. Any exceptions that are thrown will be - * converted into friendly error messages using the exception mapping - * services. - * - * @param context The WebMacro context in scope for this request. - * - * @see ExceptionMap - */ - public void populate (WebContext context) throws Exception; -} diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/DispatcherServlet.java b/projects/samskivert/src/java/com/samskivert/webmacro/DispatcherServlet.java index 8b10de7d..9264cf29 100644 --- a/projects/samskivert/src/java/com/samskivert/webmacro/DispatcherServlet.java +++ b/projects/samskivert/src/java/com/samskivert/webmacro/DispatcherServlet.java @@ -1,5 +1,5 @@ // -// $Id: DispatcherServlet.java,v 1.2 2001/02/16 03:27:54 mdb Exp $ +// $Id: DispatcherServlet.java,v 1.3 2001/03/01 21:06:22 mdb Exp $ package com.samskivert.webmacro; @@ -9,7 +9,11 @@ import java.util.HashMap; import java.util.Properties; import java.util.StringTokenizer; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; import javax.servlet.ServletException; import com.samskivert.util.ConfigUtil; @@ -22,12 +26,12 @@ import org.webmacro.servlet.*; * in the following ways: * *
* applications=whowhere * whowhere.base_uri=/whowhere - * whowhere.base_pkg=whowhere.servlets + * whowhere.base_pkg=whowhere.logic ** * This defines an application identified as
whowhere. An
@@ -69,10 +73,10 @@ import org.webmacro.servlet.*;
* base_uri defines the prefix shared by all pages served by
* the application and which serves to identify which application to
* invoke when processing a request. The base_pkg is used to
- * construct the populator classname based on the URI and the
+ * construct the logic classname based on the URI and the
* base_uri parameter.
*
- * Now let's look at a sample request to determine how the populator + *
Now let's look at a sample request to determine how the logic * classname is resolved. Consider the following request URI: * *
@@ -102,16 +106,16 @@ import org.webmacro.servlet.*; * *Error handling
* The dispatcher servlet provides a common error handling mechanism. The - * design is to catch any exceptions thrown by the populator and to - * convert them into friendly error messages that are inserted into the - * web context with the key"error"for easy display in the + * design is to catch any exceptions thrown by the logic and to convert + * them into friendly error messages that are inserted into the web + * context with the key"error"for easy display in the * resulting web page. * *The process of mapping exceptions to friendly error messages is * done using the
ExceptionMapclass. Consult its * documentation for an explanation of how it works. * - * @see ContextPopulator + * @see Logic * @see ExceptionMap */ public class DispatcherServlet extends WMServlet @@ -148,6 +152,7 @@ public class DispatcherServlet extends WMServlet String appid = tok.nextToken(); String baseURI = props.getProperty(appid + ".base_uri"); String basePkg = props.getProperty(appid + ".base_pkg"); + String appcl = props.getProperty(appid + ".application"); // make sure we're not missing anything if (baseURI == null) { @@ -161,8 +166,14 @@ public class DispatcherServlet extends WMServlet continue; } + Application app = new Application(baseURI, basePkg); + // if an application class was specified, initialize it + if (appcl != null) { + app.init(appcl, getServletContext()); + } + // construct an application object and add it to our list - _apps.add(new Application(baseURI, basePkg)); + _apps.add(app); } } } @@ -177,19 +188,18 @@ public class DispatcherServlet extends WMServlet throw new HandlerException("Unable to load template: " + e); } - // assume an HTML response unless otherwise massaged by the data - // populator + // assume an HTML response unless otherwise massaged by the logic ctx.getResponse().setContentType("text/html"); // then we populate the context with data try { - ContextPopulator pop = selectPopulator(ctx); - // if no populator is matched, we simply execute the template + Logic logic = selectLogic(ctx); + // if no logic is matched, we simply execute the template // directly with the default information in the context (tools // and other WebMacro services can be used by the template to // do their WebMacro thing) - if (pop != null) { - pop.populate(ctx); + if (logic != null) { + logic.invoke(ctx); } } catch (DataValidationException dve) { @@ -197,6 +207,7 @@ public class DispatcherServlet extends WMServlet } catch (Exception e) { ctx.put(ERROR_KEY, ExceptionMap.getMessage(e)); + Log.logStackTrace(e); } return tmpl; @@ -222,57 +233,57 @@ public class DispatcherServlet extends WMServlet } /** - * This method is called to select the appropriate context populator - * for this request. The dispatcher configuration described in this - * class's documentation is consulted to map the URI to a populator - * class which is then instantiated and a single instance used to - * process all matching requests. + * This method is called to select the appropriate logic for this + * request. The dispatcher configuration described in this class's + * documentation is consulted to map the URI to a logic class which is + * then instantiated and a single instance used to process all + * matching requests. * * @param ctx The context of this request. * - * @return The populator to be used in generating the response or null - * if no populator could be matched. + * @return The logic to be used in generating the response or null if + * no logic could be matched. */ - protected ContextPopulator selectPopulator (WebContext ctx) + protected Logic selectLogic (WebContext ctx) { String path = cleanupURI(ctx.getRequest().getRequestURI()); - String pclass = null; - // Log.info("Loading populator [path=" + path + "]."); + String lclass = null; + // Log.info("Loading logic [path=" + path + "]."); // try to locate an application that matches this URI for (int i = 0; i < _apps.size(); i++) { Application app = (Application)_apps.get(i); if (app.matches(path)) { - pclass = app.generateClass(path); + lclass = app.generateClass(path); break; } } // if we didn't find a matching application, we can stop now - if (pclass == null) { + if (lclass == null) { return null; } - // otherwise look for a cached populator instance - ContextPopulator pop = (ContextPopulator)_pops.get(pclass); - if (pop == null) { + // otherwise look for a cached logic instance + Logic logic = (Logic)_logic.get(lclass); + if (logic == null) { try { - Class pcl = Class.forName(pclass); - pop = (ContextPopulator)pcl.newInstance(); + Class pcl = Class.forName(lclass); + logic = (Logic)pcl.newInstance(); } catch (Throwable t) { - Log.warning("Unable to instantiate populator for " + + Log.warning("Unable to instantiate logic for " + "matching application [path=" + path + - ", pclass=" + pclass + ", error=" + t + "]."); + ", lclass=" + lclass + ", error=" + t + "]."); // use a dummy in it's place so that we don't sit around // all day freaking out about our inability to instantiate - // the proper populator class - pop = new DummyPopulator(); + // the proper logic class + logic = new DummyLogic(); } - _pops.put(pclass, pop); + _logic.put(lclass, logic); } - return pop; + return logic; } /** @@ -343,12 +354,73 @@ public class DispatcherServlet extends WMServlet return _basePkg + uri; } + public void init (String appclass, ServletContext context) + { + // keep track of this for later + _appclass = appclass; + + try { + // get the application class + Class appcl = Class.forName(appclass); + + // look up the init method + Class[] ptypes = new Class[] { ServletContext.class }; + Method initmeth = appcl.getDeclaredMethod("init", ptypes); + + // make sure it's static + if ((initmeth.getModifiers() & Modifier.STATIC) == 0) { + Log.warning("Application init() method not static " + + "[app=" + appclass + "]."); + return; + } + + // look up the shutdown method + _shutdownMethod = + appcl.getDeclaredMethod("shutdown", new Class[0]); + + // make sure it's static + if ((_shutdownMethod.getModifiers() & Modifier.STATIC) == 0) { + Log.warning("Application shutdown() method not static " + + "[app=" + appclass + "]."); + return; + } + + // invoke the init method + Object[] args = new Object[] { context }; + initmeth.invoke(null, args); + + } catch (NoSuchMethodError nsme) { + Log.warning("Application class missing init() method " + + "[app=" + appclass + "]."); + + } catch (Throwable t) { + Log.warning("Error initializing application [app=" + appclass + + ", error=" + t + "]."); + } + } + + public void shutdown () + { + if (_shutdownMethod != null) { + try { + _shutdownMethod.invoke(null, null); + } catch (Throwable t) { + Log.warning("Error shutting down application " + + "[app=" + _appclass + ", error=" + t + "]."); + Log.logStackTrace(t); + } + } + } + protected String _baseURI; protected String _basePkg; + + protected String _appclass; + protected Method _shutdownMethod; } protected ArrayList _apps = new ArrayList(); - protected HashMap _pops = new HashMap(); + protected HashMap _logic = new HashMap(); /** This is the key used in the context for error messages. */ protected static final String ERROR_KEY = "error"; diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/DummyLogic.java b/projects/samskivert/src/java/com/samskivert/webmacro/DummyLogic.java new file mode 100644 index 00000000..9389b6ec --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/webmacro/DummyLogic.java @@ -0,0 +1,19 @@ +// +// $Id: DummyLogic.java,v 1.1 2001/03/01 21:06:22 mdb Exp $ + +package com.samskivert.webmacro; + +import org.webmacro.servlet.WebContext; + +/** + * The dummy logic is used as a placeholder for URIs that match no normal + * logic or for which the matching logic could not be instantiated. It + * does nothing. + */ +public class DummyLogic implements Logic +{ + public void invoke (WebContext context) throws Exception + { + // we're such a dummy that we do absolutely nothing. + } +} diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/DummyPopulator.java b/projects/samskivert/src/java/com/samskivert/webmacro/DummyPopulator.java deleted file mode 100644 index 8fcf555a..00000000 --- a/projects/samskivert/src/java/com/samskivert/webmacro/DummyPopulator.java +++ /dev/null @@ -1,19 +0,0 @@ -// -// $Id: DummyPopulator.java,v 1.1 2001/02/15 01:44:34 mdb Exp $ - -package com.samskivert.webmacro; - -import org.webmacro.servlet.WebContext; - -/** - * The dummy populator is used as a placeholder for URIs that match no - * normal populator or for which the matching populator could not be - * instantiated. It does nothing. - */ -public class DummyPopulator implements ContextPopulator -{ - public void populate (WebContext context) throws Exception - { - // we're such a dummy that we do absolutely nothing. - } -} diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/FormUtil.java b/projects/samskivert/src/java/com/samskivert/webmacro/FormUtil.java index d9ef799f..93884fef 100644 --- a/projects/samskivert/src/java/com/samskivert/webmacro/FormUtil.java +++ b/projects/samskivert/src/java/com/samskivert/webmacro/FormUtil.java @@ -1,8 +1,13 @@ // -// $Id: FormUtil.java,v 1.1 2001/02/15 01:44:34 mdb Exp $ +// $Id: FormUtil.java,v 1.2 2001/03/01 21:06:22 mdb Exp $ package com.samskivert.webmacro; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.samskivert.util.StringUtil; import org.webmacro.servlet.WebContext; /** @@ -28,4 +33,87 @@ public class FormUtil throw new DataValidationException(invalidDataMessage); } } + + /** + * Fetches the supplied parameter from the request. If the parameter + * does not exist, a data validation exception is thrown with the + * supplied message. + */ + public static String requireParameter (WebContext context, String name, + String missingDataMessage) + throws DataValidationException + { + String value = context.getForm(name); + if (StringUtil.blank(value)) { + throw new DataValidationException(missingDataMessage); + } + return value; + } + + /** + * Fetches the supplied parameter from the request and converts it to + * a date. The value of the parameter should be a date formatted like + * so: 2001-12-25. If the parameter does not exist or is not a + * well-formed date, a data validation exception is thrown with the + * supplied message. + */ + public static Date requireDateParameter (WebContext context, String name, + String invalidDataMessage) + throws DataValidationException + { + String value = context.getForm(name); + Date date = null; + + try { + if (value != null) { + date = _dparser.parse(value); + } + } catch (ParseException pe) { + // fall through with date == null + Log.info("Date parsing failed: " + pe); + } + + // freak out if we failed to parse the date for some reason + if (date == null) { + throw new DataValidationException(invalidDataMessage); + } + + return date; + } + + /** + * Returns true if the specified parameter is set in the request + * context. + * + * @return true if the specified parameter is set in the request + * context, false otherwise. + */ + public static boolean isSet (WebContext context, String name) + { + return !StringUtil.blank(context.getForm(name)); + } + + /** + * Returns true if the specified parameter is equal to the supplied + * value. If the parameter is not set in the request context, false is + * returned. + * + * @param context The request context. + * @param name The parameter whose value should be compared with the + * supplied value. + * @param value The value to which the parameter may be equal. This + * should not be null. + * + * @return true if the specified parameter is equal to the supplied + * parameter, false otherwise. + */ + public static boolean equals (WebContext context, String name, + String value) + { + return value.equals(context.getForm(name)); + } + + /** We use this to parse dates in requireDateParameter(). */ + protected static SimpleDateFormat _dparser = + new SimpleDateFormat("yyyy-MM-dd"); } diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/Log.java b/projects/samskivert/src/java/com/samskivert/webmacro/Log.java index 6aca94b3..eed018a1 100644 --- a/projects/samskivert/src/java/com/samskivert/webmacro/Log.java +++ b/projects/samskivert/src/java/com/samskivert/webmacro/Log.java @@ -1,5 +1,5 @@ // -// $Id: Log.java,v 1.2 2001/02/16 03:27:54 mdb Exp $ +// $Id: Log.java,v 1.3 2001/03/01 21:06:22 mdb Exp $ package com.samskivert.webmacro; @@ -29,4 +29,10 @@ public class Log { log.warning(message); } + + /** Convenience function. */ + public static void logStackTrace (Throwable t) + { + log.logStackTrace(com.samskivert.util.Log.WARNING, t); + } } diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/Logic.java b/projects/samskivert/src/java/com/samskivert/webmacro/Logic.java new file mode 100644 index 00000000..28a0c74f --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/webmacro/Logic.java @@ -0,0 +1,30 @@ +// +// $Id: Logic.java,v 1.1 2001/03/01 21:06:22 mdb Exp $ + +package com.samskivert.webmacro; + +import org.webmacro.servlet.WebContext; + +/** + * The logic class is called upon to populate the WebMacro web context, + * prior to invoking a particular WebMacro template upon it to generate a + * response page. The logic takes the place of the servlet in the standard + * WebMacro architecture and should perform all of the logic involved in + * handling a particular request. + * + * @see DispatcherServlet + */ +public interface Logic +{ + /** + * Perform any necessary computation and populate the context with + * data for this request. Any exceptions that are thrown will be + * converted into friendly error messages using the exception mapping + * services. + * + * @param context The WebMacro context in scope for this request. + * + * @see ExceptionMap + */ + public void invoke (WebContext context) throws Exception; +} diff --git a/projects/samskivert/src/java/com/samskivert/webmacro/Makefile b/projects/samskivert/src/java/com/samskivert/webmacro/Makefile index 7a6bc4a6..5b81698d 100644 --- a/projects/samskivert/src/java/com/samskivert/webmacro/Makefile +++ b/projects/samskivert/src/java/com/samskivert/webmacro/Makefile @@ -1,16 +1,16 @@ # -# $Id: Makefile,v 1.3 2001/02/15 01:44:34 mdb Exp $ +# $Id: Makefile,v 1.4 2001/03/01 21:06:22 mdb Exp $ ROOT = ../../.. SRCS = \ - ContextPopulator.java \ DataValidationException.java \ DispatcherServlet.java \ - DummyPopulator.java \ + DummyLogic.java \ ExceptionMap.java \ FormUtil.java \ FriendlyException.java \ Log.java \ + Logic.java \ include $(ROOT)/build/Makefile.java