diff --git a/projects/samskivert/src/java/com/samskivert/velocity/Application.java b/projects/samskivert/src/java/com/samskivert/velocity/Application.java
index 75286cc2..aa21dd32 100644
--- a/projects/samskivert/src/java/com/samskivert/velocity/Application.java
+++ b/projects/samskivert/src/java/com/samskivert/velocity/Application.java
@@ -1,5 +1,5 @@
//
-// $Id: Application.java,v 1.4 2001/11/02 00:58:22 mdb Exp $
+// $Id: Application.java,v 1.5 2001/11/06 04:49:32 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
@@ -38,10 +38,63 @@ import com.samskivert.util.StringUtil;
public class Application
{
/**
- * This should be overridden by the application implementation to
- * perform any necessary initialization.
+ * Performs initializations common to all applications. Applications
+ * should override {@link #willInit} to perform initializations that
+ * need to take place before the common initialization (which includes
+ * the creation of the site identifier and message manager) and should
+ * override {@link #didInit} to perform initializations that need to
+ * take place after the common initialization (like passing the
+ * application to entities that might turn around and request a
+ * reference to our site identifier).
+ *
+ * @param context the servlet context in which this application is
+ * operating.
+ * @param logicPkg the base package for all of the logic
+ * implementations for this application.
*/
- public void init (ServletContext context)
+ public void init (ServletContext context, String logicPkg)
+ {
+ // keep this around for later
+ _context = context;
+
+ // let the derived application do pre-init stuff
+ willInit();
+
+ // remove any trailing dot
+ if (logicPkg.endsWith(".")) {
+ _logicPkg = logicPkg.substring(0, logicPkg.length()-1);
+ } else {
+ _logicPkg = logicPkg;
+ }
+
+ // instantiate our message manager if the application wants one
+ String bpath = getMessageBundlePath();
+ if (bpath != null) {
+ _msgmgr = new MessageManager(bpath);
+ }
+
+ // create our site identifier
+ _siteIdent = createSiteIdentifier(_context);
+
+ // let the derived application do post-init stuff
+ didInit();
+ }
+
+ /**
+ * This should be overridden by the application implementation to
+ * invoke any necessary pre-initialization code. They should be sure
+ * to call super.willInit().
+ */
+ protected void willInit ()
+ {
+ }
+
+ /**
+ * This should be overridden by the application implementation to
+ * invoke any necessary post-initialization code. They should be sure
+ * to call super.didInit().
+ */
+ protected void didInit ()
{
}
@@ -53,6 +106,15 @@ public class Application
{
}
+ /**
+ * Returns a reference to the servlet context in which this
+ * application is operating.
+ */
+ public ServletContext getServletContext ()
+ {
+ return _context;
+ }
+
/**
* Returns the message manager in effect for this application, if one
* is in effect.
@@ -144,34 +206,6 @@ public class Application
return _msgmgr.getMessage(ctx.getRequest(), msg, args);
}
- /**
- * Performs initializations common to all applications. We could put
- * this in {@link #init} and require application derived classes to
- * call super.init() but we want it to happen after the
- * application initializes itself.
- *
- * @param logicPkg the base package for all of the logic
- * implementations for this application.
- */
- protected void postInit (ServletContext ctx, String logicPkg)
- {
- // remove any trailing dot
- if (logicPkg.endsWith(".")) {
- _logicPkg = logicPkg.substring(0, logicPkg.length()-1);
- } else {
- _logicPkg = logicPkg;
- }
-
- // instantiate our message manager if the application wants one
- String bpath = getMessageBundlePath();
- if (bpath != null) {
- _msgmgr = new MessageManager(bpath);
- }
-
- // create our site identifier
- _siteIdent = createSiteIdentifier(ctx);
- }
-
/**
* Given the servlet path (the part of the URI after the context path)
* this generates the classname of the logic class that should handle
@@ -190,6 +224,10 @@ public class Application
return _logicPkg + path;
}
+ /** A reference to the servlet context in which this application is
+ * operating. */
+ protected ServletContext _context;
+
/** The prefix that we use to generate fully qualified logic class
* names. */
protected String _logicPkg;
diff --git a/projects/samskivert/src/java/com/samskivert/velocity/DispatcherServlet.java b/projects/samskivert/src/java/com/samskivert/velocity/DispatcherServlet.java
index 9332ecb3..26579f2b 100644
--- a/projects/samskivert/src/java/com/samskivert/velocity/DispatcherServlet.java
+++ b/projects/samskivert/src/java/com/samskivert/velocity/DispatcherServlet.java
@@ -1,5 +1,5 @@
//
-// $Id: DispatcherServlet.java,v 1.8 2001/11/02 18:46:48 mdb Exp $
+// $Id: DispatcherServlet.java,v 1.9 2001/11/06 04:49:32 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
@@ -35,12 +35,14 @@ import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.RuntimeSingleton;
import org.apache.velocity.servlet.VelocityServlet;
import com.samskivert.Log;
import com.samskivert.servlet.MessageManager;
import com.samskivert.servlet.RedirectException;
+import com.samskivert.servlet.SiteIdentifier;
import com.samskivert.servlet.util.ExceptionMap;
import com.samskivert.servlet.util.FriendlyException;
@@ -162,16 +164,13 @@ public class DispatcherServlet extends VelocityServlet
/**
* Initialize ourselves and our application.
*/
- public void init (ServletConfig config)
- throws ServletException
+ protected void initVelocity (ServletConfig config)
+ throws ServletException
{
- super.init(config);
-
- // Log.info("Initializing dispatcher servlet.");
-
// load up our application configuration
try {
String appcl = config.getInitParameter(APP_CLASS_PROPS_KEY);
+ Log.info("Creating application [class=" + appcl + "].");
if (appcl == null) {
_app = new Application();
} else {
@@ -184,21 +183,17 @@ public class DispatcherServlet extends VelocityServlet
if (StringUtil.blank(logicPkg)) {
logicPkg = "";
}
- _app.init(getServletContext());
- _app.postInit(getServletContext(), logicPkg);
+ _app.init(getServletContext(), logicPkg);
} catch (Throwable t) {
Log.warning("Error instantiating application.");
Log.logStackTrace(t);
}
- }
- // documentation inherited
- protected void initVelocity (ServletConfig config)
- throws ServletException
- {
- // stick the servlet context into the Velocity application context
- Velocity.setApplicationContext(getServletContext());
+ // stick the application into the Velocity application context
+ Velocity.setApplicationContext(_app);
+
+ // now let velocity initialize itself
super.initVelocity(config);
}
@@ -219,13 +214,20 @@ public class DispatcherServlet extends VelocityServlet
*/
public Template handleRequest (HttpServletRequest req,
HttpServletResponse rsp,
- Context ctx) throws Exception
+ Context ctx)
+ throws Exception
{
InvocationContext ictx = (InvocationContext)ctx;
String errmsg = null;
- // first we select the template
- Template tmpl = selectTemplate(ictx);
+ // obtain the siteid for this request and stuff that into the
+ // context
+ SiteIdentifier ident = _app.getSiteIdentifier();
+ int siteId = ident.identifySite(req);
+ ctx.put("__siteid__", new Integer(siteId));
+
+ // then select the template
+ Template tmpl = selectTemplate(siteId, ictx);
// assume an HTML response unless otherwise massaged by the logic
rsp.setContentType("text/html");
@@ -313,12 +315,16 @@ public class DispatcherServlet extends VelocityServlet
*
* @return The template to be used in generating the response.
*/
- protected Template selectTemplate (InvocationContext ctx)
+ protected Template selectTemplate (int siteId, InvocationContext ctx)
throws ResourceNotFoundException, ParseErrorException, Exception
{
+ // create a site resource key based on the template path and the
+ // id of the site through which this request was made
String path = ctx.getRequest().getServletPath();
- // Log.info("Loading template [path=" + path + "].");
- return getTemplate(path);
+ SiteResourceKey key = new SiteResourceKey(siteId, path);
+
+ // Log.info("Loading template [key=" + key + "].");
+ return RuntimeSingleton.getRuntimeServices().getTemplate(key);
}
/**
diff --git a/projects/samskivert/src/java/com/samskivert/velocity/ImportDirective.java b/projects/samskivert/src/java/com/samskivert/velocity/ImportDirective.java
new file mode 100644
index 00000000..e8f82b43
--- /dev/null
+++ b/projects/samskivert/src/java/com/samskivert/velocity/ImportDirective.java
@@ -0,0 +1,170 @@
+//
+// $Id: ImportDirective.java,v 1.1 2001/11/06 04:49:32 mdb Exp $
+//
+// samskivert library - useful routines for java programs
+// Copyright (C) 2001 Michael Bayne
+//
+// 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, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.samskivert.velocity;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.velocity.context.InternalContextAdapter;
+
+import org.apache.velocity.Template;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.directive.Directive;
+import org.apache.velocity.runtime.parser.node.Node;
+import org.apache.velocity.runtime.parser.node.SimpleNode;
+import org.apache.velocity.runtime.resource.Resource;
+
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+
+import com.samskivert.servlet.SiteIdentifier;
+import com.samskivert.util.StringUtil;
+
+/**
+ * Pluggable directive that handles the #import() statement in VTL. Import
+ * is like #parse() except that it creates a {@link SiteResourceKey} based
+ * on information from the current request when fetching the imported
+ * template.
+ */
+public class ImportDirective extends Directive
+{
+ /**
+ * Returns name of this directive.
+ */
+ public String getName ()
+ {
+ return "import";
+ }
+
+ /**
+ * Return type of this directive.
+ */
+ public int getType ()
+ {
+ return LINE;
+ }
+
+ /**
+ * Loads and renders the imported template.
+ */
+ public boolean render (
+ InternalContextAdapter context, Writer writer, Node node)
+ throws IOException, ResourceNotFoundException, ParseErrorException,
+ MethodInvocationException
+ {
+ // make sure an argument was supplied to the directive
+ if (node.jjtGetChild(0) == null) {
+ rsvc.error("#import() error : null argument");
+ return false;
+ }
+
+ // make sure that argument has a value
+ Object value = node.jjtGetChild(0).value(context);
+ if (value == null) {
+ rsvc.error("#import() error : null argument");
+ return false;
+ }
+
+ // obtain the path to the desired template
+ String path = value.toString();
+
+ // make sure we haven't exceeded the configured parse depth
+ Object[] templateStack = context.getTemplateNameStack();
+ int maxlen = rsvc.getInt(
+ RuntimeConstants.PARSE_DIRECTIVE_MAXDEPTH, 20);
+ if (templateStack.length >= maxlen) {
+ rsvc.error("Max recursion depth reached (" + maxlen + "). " +
+ "File stack: " +
+ StringUtil.toString(templateStack) + ".");
+ return false;
+ }
+
+ // inherit the current encoding if there is a current template,
+ // otherwise use the configured encoding
+ String encoding = null;
+ Resource current = context.getCurrentResource();
+ if (current != null) {
+ encoding = current.getEncoding();
+ } else {
+ encoding = (String)
+ rsvc.getProperty(RuntimeConstants.INPUT_ENCODING);
+ }
+
+ // construct the template key based on the desired path and the
+ // site information in the current context. the siteid was shoved
+ // into the context by the dispatcher servlet. this is a hack, i
+ // know, but i couldn't convince the velocity peeps that we should
+ // have access to a request context in places like this
+ Object siteIdVal = context.get("__siteid__");
+ int siteId = SiteIdentifier.DEFAULT_SITE_ID;
+ try {
+ siteId = ((Integer)siteIdVal).intValue();
+ } catch (Exception e) {
+ rsvc.error("#import() error: No siteId information in context.");
+ }
+ Object tkey = new SiteResourceKey(siteId, path);
+
+ // locate the requested template
+ Template t = null;
+ try {
+ t = rsvc.getTemplate(tkey, encoding);
+
+ } catch (ResourceNotFoundException rnfe) {
+ rsvc.error("#import(): cannot find template '" + tkey +
+ "', called from template " +
+ context.getCurrentTemplateName() +
+ " at (" + getLine() + ", " + getColumn() + ")");
+ throw rnfe;
+
+ } catch (ParseErrorException pee) {
+ rsvc.error("#import(): syntax error in #import()-ed template '" +
+ tkey + "', called from template " +
+ context.getCurrentTemplateName() +
+ " at (" + getLine() + ", " + getColumn() + ")");
+ throw pee;
+
+ } catch (Exception e) {
+ rsvc.error("#import(): Error [path=" + tkey +
+ ", error=" + e + "].");
+ return false;
+ }
+
+ // finally render the template
+ try {
+ context.pushCurrentTemplateName(path);
+ ((SimpleNode)t.getData()).render(context, writer);
+
+ } catch (Exception e) {
+ // we want to pass method invocation exceptions through
+ if (e instanceof MethodInvocationException) {
+ throw (MethodInvocationException)e;
+ }
+ rsvc.error("Exception rendering #import(" + path + "): " + e);
+ return false;
+
+ } finally {
+ context.popCurrentTemplateName();
+ }
+
+ return true;
+ }
+}
diff --git a/projects/samskivert/src/java/com/samskivert/velocity/ServletContextResourceLoader.java b/projects/samskivert/src/java/com/samskivert/velocity/ServletContextResourceLoader.java
index 200f294d..b72f1751 100644
--- a/projects/samskivert/src/java/com/samskivert/velocity/ServletContextResourceLoader.java
+++ b/projects/samskivert/src/java/com/samskivert/velocity/ServletContextResourceLoader.java
@@ -1,5 +1,5 @@
//
-// $Id: ServletContextResourceLoader.java,v 1.2 2001/11/02 02:30:24 mdb Exp $
+// $Id: ServletContextResourceLoader.java,v 1.3 2001/11/06 04:49:32 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
@@ -38,6 +38,24 @@ import org.apache.velocity.runtime.resource.loader.ResourceLoader;
*/
public class ServletContextResourceLoader extends ResourceLoader
{
+ /**
+ * When used with the default Velocity resource manager, we are
+ * constructed with our zero-argument constructor and later
+ * initialized via {@link #init}.
+ */
+ public ServletContextResourceLoader ()
+ {
+ }
+
+ /**
+ * When used with the {@link SiteResourceManager} we are constructed
+ * with our servlet context reference and not later initialized.
+ */
+ public ServletContextResourceLoader (ServletContext sctx)
+ {
+ _sctx = sctx;
+ }
+
/**
* Called by Velocity to initialize this resource loader.
*/
diff --git a/projects/samskivert/src/java/com/samskivert/velocity/SiteJarResourceLoader.java b/projects/samskivert/src/java/com/samskivert/velocity/SiteJarResourceLoader.java
new file mode 100644
index 00000000..d3465165
--- /dev/null
+++ b/projects/samskivert/src/java/com/samskivert/velocity/SiteJarResourceLoader.java
@@ -0,0 +1,182 @@
+//
+// $Id: SiteJarResourceLoader.java,v 1.1 2001/11/06 04:49:32 mdb Exp $
+//
+// samskivert library - useful routines for java programs
+// Copyright (C) 2001 Michael Bayne
+//
+// 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, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.samskivert.velocity;
+
+import java.io.InputStream;
+import java.io.IOException;
+import javax.servlet.ServletContext;
+
+import org.apache.commons.collections.ExtendedProperties;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.runtime.resource.Resource;
+import org.apache.velocity.runtime.resource.loader.ResourceLoader;
+
+import com.samskivert.Log;
+import com.samskivert.servlet.SiteIdentifier;
+
+/**
+ * A Velocity resource loader that loads resources from site-specific jar
+ * files. This cannot be used with the default Velocity resource manager,
+ * but is used by the {@link SiteResourceManager} which automatically
+ * handles the creation and use of this resource loader.
+ *
+ * @see com.samskivert.servlet.SiteResourceLoader
+ */
+public class SiteResourceLoader extends ResourceLoader
+{
+ /**
+ * Constructs a site resource loader that will use the specified site
+ * identifier to map site ids to site strings.
+ */
+ public SiteResourceLoader (SiteIdentifier ident, ServletContext context)
+ {
+ // naming conflict... alas.
+ _loader = new com.samskivert.servlet.SiteResourceLoader(
+ ident, context);
+ }
+
+ /**
+ * This is not called.
+ */
+ public void init (ExtendedProperties config)
+ {
+ }
+
+ /**
+ * Returns the input stream that can be used to load the named
+ * resource.
+ *
+ * @param resourceKey the locator key for the resource to be loaded.
+ *
+ * @return an input stream that can be used to read the resource.
+ *
+ * @exception ResourceNotFoundException if the resource was not found.
+ */
+ public InputStream getResourceStream (Object resourceKey)
+ throws ResourceNotFoundException
+ {
+ SiteResourceKey skey = castKey(resourceKey);
+ if (skey == null) {
+ String errmsg = "Cannot use SiteResourceLoader without " +
+ "loading resources with SiteResourceKey instances.";
+ throw new ResourceNotFoundException(errmsg);
+ }
+
+ // load it on up
+ try {
+ InputStream stream = _loader.getResourceAsStream(
+ skey.getSiteId(), skey.getPath(), false);
+ if (stream == null) {
+ String errmsg = "Unable to load resource via " +
+ "site-specific jar file [key=" + skey + "].";
+ throw new ResourceNotFoundException(errmsg);
+ }
+ return stream;
+
+ } catch (IOException ioe) {
+ throw new ResourceNotFoundException(ioe.getMessage());
+ }
+ }
+
+ /**
+ * Things won't ever be modified when loaded from the servlet context
+ * because they came from the webapp .war file and if that is
+ * reloaded, everything will be thrown away and started afresh.
+ */
+ public boolean isSourceModified (Resource resource)
+ {
+ SiteResourceKey skey = castKey(resource.getKey());
+ if (skey == null) {
+ // return false if we were supplied with a bogus key
+ return false;
+ }
+
+ // if the resource is for the default site, it is never considered
+ // to be modified
+ int siteId = skey.getSiteId();
+ if (siteId == SiteIdentifier.DEFAULT_SITE_ID) {
+ return false;
+
+ } else {
+ // otherwise compare the last modified time of the loaded
+ // resource with the last modified time of the associated
+ // site-specific jar file
+ try {
+ return (resource.getLastModified() <
+ _loader.getLastModified(siteId));
+ } catch (IOException ioe) {
+ Log.warning("Failure obtaining last modified time of " +
+ "site-specific jar file [siteId=" + siteId +
+ ", error=" + ioe + "].");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Things won't ever be modified when loaded from the servlet context
+ * because they came from the webapp .war file and if that is
+ * reloaded, everything will be thrown away and started afresh. So we
+ * can punt here and return zero.
+ */
+ public long getLastModified (Resource resource)
+ {
+ SiteResourceKey skey = castKey(resource.getKey());
+ if (skey == null) {
+ // return 0 if we were supplied with a bogus key
+ return 0;
+ }
+
+ // if the resource is for the default site, it is never considered
+ // to be modified
+ int siteId = skey.getSiteId();
+ if (siteId == SiteIdentifier.DEFAULT_SITE_ID) {
+ return 0;
+
+ } else {
+ // otherwise return the last modified time of the associated
+ // site-specific jar file
+ try {
+ return _loader.getLastModified(siteId);
+ } catch (IOException ioe) {
+ Log.warning("Failure obtaining last modified time of " +
+ "site-specific jar file [siteId=" + siteId +
+ ", error=" + ioe + "].");
+ return 0;
+ }
+ }
+ }
+
+ /**
+ * Casts the supplied resource key to a {@link SiteResourceKey} and
+ * returns it iff it is an instance of such. Returns null otherwise.
+ */
+ protected SiteResourceKey castKey (Object resourceKey)
+ {
+ return (resourceKey instanceof SiteResourceKey) ?
+ (SiteResourceKey)resourceKey : null;
+ }
+
+ /** A reference to the site resource loader through which we'll load
+ * things. */
+ protected com.samskivert.servlet.SiteResourceLoader _loader;
+}
diff --git a/projects/samskivert/src/java/com/samskivert/velocity/SiteResourceKey.java b/projects/samskivert/src/java/com/samskivert/velocity/SiteResourceKey.java
new file mode 100644
index 00000000..5d4d85a2
--- /dev/null
+++ b/projects/samskivert/src/java/com/samskivert/velocity/SiteResourceKey.java
@@ -0,0 +1,180 @@
+//
+// $Id: SiteResourceKey.java,v 1.1 2001/11/06 04:49:32 mdb Exp $
+//
+// samskivert library - useful routines for java programs
+// Copyright (C) 2001 Michael Bayne
+//
+// 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, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.samskivert.velocity;
+
+import com.samskivert.servlet.SiteIdentifier;
+import com.samskivert.util.IntListUtil;
+import com.samskivert.util.StringUtil;
+
+/**
+ * The site resource key is used as a key for resources that can be used
+ * for one or more sites. A resource always has the same path, but can be
+ * valid for a single site, or multiple sites and the set of sites for
+ * which a resource is valid can change without impacting the key's hash
+ * value.
+ */
+public class SiteResourceKey
+{
+ /**
+ * Constructs a new site resource key for the specified resource,
+ * loaded via the specified site.
+ */
+ public SiteResourceKey (int siteId, String path)
+ {
+ _path = path;
+ _sites = new int[] { siteId };
+ }
+
+ /**
+ * Resource keys that contain only a single site id will return that
+ * site id as their primary. Those that contain multiple sites are
+ * assumed to reference default resources (which may be applicable to
+ * multiple sites) in which case the default site id is returned.
+ */
+ public int getSiteId ()
+ {
+ return (_sites.length == 1 ? _sites[0] :
+ SiteIdentifier.DEFAULT_SITE_ID);
+ }
+
+ /**
+ * Returns the path to the resource referenced by this site resource
+ * key.
+ */
+ public String getPath ()
+ {
+ return _path;
+ }
+
+ /**
+ * Returns true if this resource key contains the specified site in
+ * its site set.
+ */
+ public boolean containsSite (int siteId)
+ {
+ return IntListUtil.contains(_sites, siteId);
+ }
+
+ /**
+ * Adds a site to this site resource key. The resource to which this
+ * key maps is assumed to be valid for the added site.
+ */
+ public void addSite (int siteId)
+ {
+ // sanity check
+ if (siteId == 0) {
+ throw new IllegalArgumentException(
+ "Site IDs cannot have the value 0.");
+ }
+
+ // add the site to the list only if it's not already there
+ int[] sites = IntListUtil.testAndAdd(_sites, siteId);
+ if (sites != null) {
+ _sites = sites;
+ }
+ }
+
+ /**
+ * Removes a site from this site resource key. The resource to which
+ * this key maps is no longer assumed to be valid for the removed
+ * site.
+ */
+ public boolean removeSite (int siteId)
+ {
+ // sanity check
+ if (siteId == 0) {
+ throw new IllegalArgumentException(
+ "Site IDs cannot have the value 0.");
+ }
+
+ // remove the site from the list
+ return (IntListUtil.remove(_sites, siteId) != 0);
+ }
+
+ /**
+ * We hash to the hash value of our path.
+ */
+ public int hashCode ()
+ {
+ return _path.hashCode();
+ }
+
+ /**
+ * Two site resource keys are equal if the paths are the same and at
+ * least one site id overlaps between the two keys. Most equality
+ * comparisons will involve one multi-site key and one key that
+ * contains only a single site.
+ */
+ public boolean equals (Object other)
+ {
+ if (other instanceof SiteResourceKey) {
+ SiteResourceKey okey = (SiteResourceKey)other;
+
+ // make sure the paths match
+ if (!_path.equals(okey._path)) {
+ return false;
+ }
+
+ // select the key with fewer sites in its sites array to be in
+ // the outer loop
+ int[] s1 = _sites;
+ int[] s2 = okey._sites;
+ if (s2.length < s1.length) {
+ s1 = s2;
+ s2 = _sites;
+ }
+
+ for (int i1 = 0; i1 < s1.length; i1++) {
+ for (int i2 = 0; i2 < s2.length; i2++) {
+ if (s1[i1] == s2[i2]) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * The string representation of a resource key is just the path so
+ * that it is backwards compatible with older resource loaders.
+ */
+ public String toString ()
+ {
+ return _path;
+ }
+
+ /**
+ * Returns a string description of this object.
+ */
+ public String description ()
+ {
+ return ("[path=" + _path +
+ ", sites=" + StringUtil.toString(_sites) + "]");
+ }
+
+ /** The path to the resource. */
+ protected String _path;
+
+ /** The sites for which this resource is valid. */
+ protected int[] _sites;
+}
diff --git a/projects/samskivert/src/java/com/samskivert/velocity/SiteResourceManager.java b/projects/samskivert/src/java/com/samskivert/velocity/SiteResourceManager.java
new file mode 100644
index 00000000..3e40dc8d
--- /dev/null
+++ b/projects/samskivert/src/java/com/samskivert/velocity/SiteResourceManager.java
@@ -0,0 +1,137 @@
+//
+// $Id: SiteResourceManager.java,v 1.1 2001/11/06 04:49:32 mdb Exp $
+//
+// samskivert library - useful routines for java programs
+// Copyright (C) 2001 Michael Bayne
+//
+// 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, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.samskivert.velocity;
+
+import javax.servlet.ServletContext;
+
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.runtime.resource.Resource;
+import org.apache.velocity.runtime.resource.ResourceFactory;
+import org.apache.velocity.runtime.resource.ResourceManagerImpl;
+import org.apache.velocity.runtime.resource.loader.ResourceLoader;
+
+import com.samskivert.Log;
+import com.samskivert.servlet.SiteIdentifier;
+
+/**
+ * A resource manager implementation for Velocity that first loads site
+ * specific resources (via the {@link SiteResourceLoader}), but falls back
+ * to default resources if no site-specific resource loader is available.
+ *
+ *
If this resource manager is to be used, resources must be fetched + * using {@likn SiteResourceKey} objects as keys rather than simple + * strings. + */ +public class SiteResourceManager extends ResourceManagerImpl +{ + public void initialize (RuntimeServices rsvc) + throws Exception + { + super.initialize(rsvc); + + // the web framework was kind enough to slip this into the runtime + // instance when it started up + Application app = (Application)rsvc.getApplicationContext(); + if (app == null) { + rsvc.warn("SiteResourceManager: Application reference " + + "was not supplied as application context. A " + + "user of the site resource manager must supply " + + "a reference to the Application instance via " + + "Velocity.setApplicationContext()."); + } + + // get handles on the good stuff + _sctx = app.getServletContext(); + _ident = app.getSiteIdentifier(); + + // create our resource loaders + _siteLoader = new SiteResourceLoader(_ident, _sctx); + _contextLoader = new ServletContextResourceLoader(_sctx); + } + + protected Resource loadResource( + Object resourceKey, int resourceType, String encoding) + throws ResourceNotFoundException, ParseErrorException, Exception + { + // create a blank new resource + Resource resource = + ResourceFactory.getResource(resourceKey, resourceType); + resource.setRuntimeServices(rsvc); + resource.setKey(resourceKey); + resource.setEncoding(encoding); + + // if the resource was requested using a site resource key, we can + // attempt to load a site-specific version + if (resourceKey instanceof SiteResourceKey) { + SiteResourceKey rkey = (SiteResourceKey)resourceKey; + Log.info("Looking for site-specific version of " + + rkey.description() + "."); + + // make sure the site we're loading for is not the default + // site, in which case we want to skip to the second resource + // loader directly + if (rkey.getSiteId() != SiteIdentifier.DEFAULT_SITE_ID) { + // try loading it via the site-specific resource loader + try { + resolveResource(resource, _siteLoader); + Log.info("Loaded site-specific [key=" + resourceKey + "]."); + } catch (ResourceNotFoundException rnfe) { + // nothing to worry about here + } + } + } + + // try the servlet context loader if we didn't find a + // site-specific resource + if (resource.getData() == null) { + resolveResource(resource, _contextLoader); + Log.info("Loaded default [key=" + resourceKey + "]."); + } + + return resource; + } + + protected void resolveResource (Resource resource, ResourceLoader loader) + throws ResourceNotFoundException, ParseErrorException, Exception + { + resource.setResourceLoader(loader); + resource.process(); + resource.setLastModified(loader.getLastModified(resource)); + resource.setModificationCheckInterval( + loader.getModificationCheckInterval()); + resource.touch(); + } + + /** A reference to the servlet context through which we'll load + * default resources. */ + protected ServletContext _sctx; + + /** A reference to the site identifier in use by the application. */ + protected SiteIdentifier _ident; + + /** We use this to load site-specific resources. */ + protected SiteResourceLoader _siteLoader; + + /** We use this to load default resources. */ + protected ServletContextResourceLoader _contextLoader; +}