Various enhancements to our webmacro extensions.
git-svn-id: https://samskivert.googlecode.com/svn/trunk@60 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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:
|
||||
*
|
||||
* <ul>
|
||||
* <li> It defines the notion of a context populator which populates the
|
||||
* context with data to be used to satisfy a particular request. The
|
||||
* context populator is not a servlet and is therefore limited in what it
|
||||
* can do while populating data. Experience dictates that ultimate
|
||||
* flexibility leads to bad design decisions and that this is a place
|
||||
* where that sort of thing can be comfortably nipped in the bud. <br><br>
|
||||
* <li> It defines the notion of a logic object which populates the
|
||||
* context with data to be used to satisfy a particular request. The logic
|
||||
* is not a servlet and is therefore limited in what it can do while
|
||||
* populating data. Experience dictates that ultimate flexibility leads to
|
||||
* bad design decisions and that this is a place where that sort of thing
|
||||
* can be comfortably nipped in the bud. <br><br>
|
||||
*
|
||||
* <li> It allows .wm files to be referenced directly in the URL while
|
||||
* maintaining the ability to choose a cobranded template based. The URI
|
||||
@@ -60,7 +64,7 @@ import org.webmacro.servlet.*;
|
||||
* <pre>
|
||||
* applications=whowhere
|
||||
* whowhere.base_uri=/whowhere
|
||||
* whowhere.base_pkg=whowhere.servlets
|
||||
* whowhere.base_pkg=whowhere.logic
|
||||
* </pre>
|
||||
*
|
||||
* This defines an application identified as <code>whowhere</code>. An
|
||||
@@ -69,10 +73,10 @@ import org.webmacro.servlet.*;
|
||||
* <code>base_uri</code> 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 <code>base_pkg</code> is used to
|
||||
* construct the populator classname based on the URI and the
|
||||
* construct the logic classname based on the URI and the
|
||||
* <code>base_uri</code> parameter.
|
||||
*
|
||||
* <p> Now let's look at a sample request to determine how the populator
|
||||
* <p> Now let's look at a sample request to determine how the logic
|
||||
* classname is resolved. Consider the following request URI:
|
||||
*
|
||||
* <pre>
|
||||
@@ -102,16 +106,16 @@ import org.webmacro.servlet.*;
|
||||
*
|
||||
* <p><b>Error handling</b><br>
|
||||
* 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 <code>"error"</code> 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 <code>"error"</code> for easy display in the
|
||||
* resulting web page.
|
||||
*
|
||||
* <p> The process of mapping exceptions to friendly error messages is
|
||||
* done using the <code>ExceptionMap</code> class. 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";
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user