From c25ffcf69d4da9b2f8e3a65321c600570d9e9b4b Mon Sep 17 00:00:00 2001 From: hns Date: Fri, 7 Mar 2003 16:49:18 +0000 Subject: [PATCH] Merging changes from helma_1_2_3 --- build/build.xml | 6 +- src/helma/doc/DocPrototype.java | 33 +- src/helma/framework/ResponseTrans.java | 3 +- src/helma/framework/core/Application.java | 6 +- src/helma/framework/core/ApplicationBean.java | 8 +- src/helma/framework/core/Prototype.java | 22 +- .../framework/core/RequestEvaluator.java | 6 +- src/helma/framework/core/Skin.java | 5 +- src/helma/main/Server.java | 2 +- src/helma/objectmodel/db/DbMapping.java | 196 ++--- src/helma/objectmodel/db/Node.java | 51 +- src/helma/objectmodel/db/NodeManager.java | 19 +- src/helma/objectmodel/db/Property.java | 2 +- src/helma/objectmodel/db/Relation.java | 37 +- src/helma/scripting/fesi/HopExtension.java | 39 +- src/helma/servlet/AbstractServletClient.java | 12 +- src/helma/util/CronJob.java | 675 ++++++++++++++++++ 17 files changed, 940 insertions(+), 182 deletions(-) create mode 100644 src/helma/util/CronJob.java diff --git a/build/build.xml b/build/build.xml index c9451c10..cd0dd648 100644 --- a/build/build.xml +++ b/build/build.xml @@ -8,7 +8,7 @@ - + @@ -246,7 +246,7 @@ - + @@ -258,7 +258,7 @@ base base.mountpoint = / bloggerapi himp -hopblog +gong lillebror manage diff --git a/src/helma/doc/DocPrototype.java b/src/helma/doc/DocPrototype.java index 336f83d9..45dc9e86 100644 --- a/src/helma/doc/DocPrototype.java +++ b/src/helma/doc/DocPrototype.java @@ -84,20 +84,25 @@ public class DocPrototype extends DocDirElement { File f = new File (location.getAbsolutePath (), arr[i]); if (f.isDirectory ()) continue; - if (arr[i].endsWith (".skin")) { - addChild (DocSkin.newInstance (f, this)); - } else if (arr[i].endsWith (".properties")) { - continue; - } else if (arr[i].endsWith (".hac")) { - addChild (DocFunction.newAction (f, this)); - } else if (arr[i].endsWith (".hsp")) { - addChild (DocFunction.newTemplate (f, this)); - } else if (arr[i].endsWith (".js")) { - DocElement[] elements = DocFunction.newFunctions (f, this); - for (int j=0; j 0) { setName (propvalue); @@ -664,9 +664,9 @@ public final class Node implements INode, Serializable { if (parentmap != null) { // first try to retrieve name via generic property relation of parent Relation prel = parentmap.getPropertyRelation (); - if (prel != null && prel.otherType == dbmap && prel.accessor != null) { + if (prel != null && prel.otherType == dbmap && prel.accessName != null) { // reverse look up property used to access this via parent - Relation proprel = dbmap.columnNameToRelation (prel.accessor); + Relation proprel = dbmap.columnNameToRelation (prel.accessName); if (proprel != null && proprel.propName != null) newname = getString (proprel.propName); } @@ -773,17 +773,18 @@ public final class Node implements INode, Serializable { checkWriteLock (); node.checkWriteLock (); } - + // if subnodes are defined via realation, make sure its constraints are enforced. - if (dbmap != null && dbmap.getSubnodeRelation () != null) + if (dbmap != null && dbmap.getSubnodeRelation () != null) { dbmap.getSubnodeRelation ().setConstraints (this, node); - + } + // if the new node is marked as TRANSIENT and this node is not, mark new node as NEW if (state != TRANSIENT && node.state == TRANSIENT) node.makePersistentCapable (); String n = node.getName(); - + // if (n.indexOf('/') > -1) // throw new RuntimeException ("\"/\" found in Node name."); @@ -809,7 +810,7 @@ public final class Node implements INode, Serializable { if (groupbyNode == null) groupbyNode = getGroupbySubnode (groupbyValue, true); groupbyNode.addNode (node); - + return node; } catch (Exception x) { System.err.println ("Error adding groupby: "+x); @@ -836,10 +837,10 @@ public final class Node implements INode, Serializable { // check if properties are subnodes (_properties.aresubnodes=true) if (dbmap != null && node.dbmap != null) { Relation prel = dbmap.getPropertyRelation(); - if (prel != null && prel.accessor != null) { - Relation localrel = node.dbmap.columnNameToRelation (prel.accessor); + if (prel != null && prel.accessName != null) { + Relation localrel = node.dbmap.columnNameToRelation (prel.accessName); // if no relation from db column to prop name is found, assume that both are equal - String propname = localrel == null ? prel.accessor : localrel.propName; + String propname = localrel == null ? prel.accessName : localrel.propName; String prop = node.getString (propname); if (prop != null && prop.length() > 0) { INode old = getNode (prop); @@ -932,7 +933,7 @@ public final class Node implements INode, Serializable { if (rel != null) return (IPathElement) getNode (name); rel = dbmap.getSubnodeRelation (); - if (rel != null && rel.groupby == null && rel.accessor != null) { + if (rel != null && rel.groupby == null && rel.accessName != null) { if (rel.otherType != null && rel.otherType.isRelational ()) return (IPathElement) nmgr.getNode (this, name, rel); else @@ -1112,10 +1113,10 @@ public final class Node implements INode, Serializable { // check if subnodes are also accessed as properties. If so, also unset the property if (dbmap != null && node.dbmap != null) { Relation prel = dbmap.getPropertyRelation(); - if (prel != null && prel.accessor != null) { - Relation localrel = node.dbmap.columnNameToRelation (prel.accessor); + if (prel != null && prel.accessName != null) { + Relation localrel = node.dbmap.columnNameToRelation (prel.accessName); // if no relation from db column to prop name is found, assume that both are equal - String propname = localrel == null ? prel.accessor : localrel.propName; + String propname = localrel == null ? prel.accessName : localrel.propName; String prop = node.getString (propname); if (prop != null && getNode (prop) == node) unset (prop); @@ -1309,17 +1310,13 @@ public final class Node implements INode, Serializable { */ public Enumeration properties () { - if (dbmap != null && dbmap.isRelational() && dbmap.getProp2DB ().size() > 0) + if (dbmap != null && dbmap.isRelational()) // return the properties defined in type.properties, if there are any - return new Enumeration () { - Iterator i = dbmap.getProp2DB().keySet().iterator(); - public boolean hasMoreElements() {return i.hasNext();} - public Object nextElement () {return i.next();} - }; + return dbmap.getPropertyEnumeration(); Relation prel = dbmap == null ? null : dbmap.getPropertyRelation (); - if (prel != null && prel.accessor != null && !prel.subnodesAreProperties - && prel.otherType != null && prel.otherType.isRelational ()) + if (prel != null && prel.hasAccessName() + && prel.otherType != null && prel.otherType.isRelational ()) // return names of objects from a relational db table return nmgr.getPropertyNames (this, prel).elements (); else if (propMap != null) @@ -1493,7 +1490,7 @@ public final class Node implements INode, Serializable { propMap.put (p2, prop); } - // check if this may have an effect on the node's URL when using subnodesAreProperties + // check if this may have an effect on the node's URL when using accessname // but only do this if we already have a parent set, i.e. if we are already stored in the db Node parent = parentHandle == null ? null : (Node) getParent (); @@ -1504,7 +1501,7 @@ public final class Node implements INode, Serializable { Relation propRel = parentmap.getPropertyRelation (); String dbcolumn = dbmap.propertyToColumnName (propname); - if (propRel != null && propRel.accessor != null && propRel.accessor.equals (dbcolumn)) { + if (propRel != null && propRel.accessName != null && propRel.accessName.equals (dbcolumn)) { INode n = parent.getNode (value); if (n != null && n != this) { parent.unset (value); @@ -1748,7 +1745,7 @@ public final class Node implements INode, Serializable { // UPDATE: using n.getKey() instead of manually constructing key. HW 2002/09/13 tx.visitCleanNode (n.getKey(), n); // if the field is not the primary key of the property, also register it - if (rel != null && rel.accessor != null && state != TRANSIENT) { + if (rel != null && rel.accessName != null && state != TRANSIENT) { Key secKey = new SyntheticKey (getKey (), propname); nmgr.evictKey (secKey); tx.visitCleanNode (secKey, n); diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java index c2740d5d..87134589 100644 --- a/src/helma/objectmodel/db/NodeManager.java +++ b/src/helma/objectmodel/db/NodeManager.java @@ -1027,8 +1027,8 @@ public final class NodeManager { } String accessProp = null; - if (rel.accessor != null && !rel.usesPrimaryKey ()) - accessProp = dbm.columnNameToProperty (rel.accessor); + if (rel.accessName != null && !rel.usesPrimaryKey ()) + accessProp = dbm.columnNameToProperty (rel.accessName); while (rs.next ()) { // create new Nodes. @@ -1048,7 +1048,7 @@ public final class NodeManager { sn.add (new NodeHandle (primKey)); } - // if relation doesn't use primary key as accessor, get accessor value + // if relation doesn't use primary key as accessName, get accessName value String accessName = null; if (accessProp != null) { accessName = node.getString (accessProp); @@ -1166,7 +1166,7 @@ public final class NodeManager { Vector retval = new Vector (); // if we do a groupby query (creating an intermediate layer of groupby nodes), // retrieve the value of that field instead of the primary key - String namefield = rel.accessor; + String namefield = rel.accessName; Connection con = rel.otherType.getConnection (); String table = rel.otherType.getTableName (); @@ -1267,12 +1267,9 @@ public final class NodeManager { node = (Node) home.createNode (kstr); else node = new Node (home, kstr, safe, rel.prototype); - if (rel.prototype != null) { - node.setPrototype (rel.prototype); - node.setDbMapping (app.getDbMapping (rel.prototype)); - } else { - node.setDbMapping (rel.getVirtualMapping ()); - } + // set prototype and dbmapping on the newly created virtual/collection node + node.setPrototype (rel.prototype); + node.setDbMapping (rel.getVirtualMapping ()); } else if (rel != null && rel.groupby != null) { node = home.getGroupbySubnode (kstr, false); @@ -1299,7 +1296,7 @@ public final class NodeManager { if (home.getSubnodeRelation () != null) { // combine our key with the constraints in the manually set subnode relation q.append ("WHERE "); - q.append (rel.accessor); + q.append (rel.accessName); q.append (" = '"); q.append (escape(kstr)); q.append ("'"); diff --git a/src/helma/objectmodel/db/Property.java b/src/helma/objectmodel/db/Property.java index d933c5b2..beabe7fa 100644 --- a/src/helma/objectmodel/db/Property.java +++ b/src/helma/objectmodel/db/Property.java @@ -238,7 +238,7 @@ public final class Property implements IProperty, Serializable, Cloneable { // check if the property node is also a subnode // BUG: this doesn't work because properties for subnode/properties are never stored and therefore // never reused. - if (nvrel != null && nvrel.subnodesAreProperties) { + if (nvrel != null && nvrel.hasAccessName()) { node.removeNode (nvalue); } // only need to call unregisterPropLink if the value node is not stored in a relational db diff --git a/src/helma/objectmodel/db/Relation.java b/src/helma/objectmodel/db/Relation.java index 7554356a..b744e588 100644 --- a/src/helma/objectmodel/db/Relation.java +++ b/src/helma/objectmodel/db/Relation.java @@ -52,10 +52,9 @@ public final class Relation { boolean readonly; boolean aggressiveLoading; boolean aggressiveCaching; - boolean subnodesAreProperties; boolean isPrivate; - String accessor; // db column used to access objects through this relation + String accessName; // db column used to access objects through this relation String order; String groupbyOrder; String groupby; @@ -76,9 +75,8 @@ public final class Relation { this.columnName = rel.columnName; this.reftype = rel.reftype; this.constraints = rel.constraints; - this.accessor = rel.accessor; + this.accessName = rel.accessName; this.maxSize = rel.maxSize; - this.subnodesAreProperties = rel.subnodesAreProperties; } /** @@ -199,10 +197,8 @@ public final class Relation { aggressiveLoading = aggressiveCaching = false; } // check if subnode condition should be applied for property relations - accessor = props.getProperty (propName+".accessname"); - if (accessor != null) - subnodesAreProperties = true; - // parse contstraints + accessName = props.getProperty (propName+".accessname"); + // parse contstraints String local = props.getProperty (propName+".local"); String foreign = props.getProperty (propName+".foreign"); if (local != null && foreign != null) { @@ -257,7 +253,7 @@ public final class Relation { * and never stored to a persistent storage. */ public boolean createPropertyOnDemand () { - return virtual || accessor != null || groupby != null; + return virtual || accessName != null || groupby != null; } /** @@ -321,13 +317,17 @@ public final class Relation { if (reftype == REFERENCE) return constraints.length == 1 && constraints[0].foreignKeyIsPrimary (); if (reftype == COLLECTION) - return accessor == null || accessor.equalsIgnoreCase (otherType.getIDField ()); + return accessName == null || accessName.equalsIgnoreCase (otherType.getIDField ()); } return false; } - public String getAccessor () { - return accessor; + public boolean hasAccessName () { + return accessName != null; + } + + public String getAccessName () { + return accessName; } public Relation getSubnodeRelation () { @@ -344,9 +344,20 @@ public final class Relation { } + /** + * get a DbMapping to use for virtual aka collection nodes. + */ public DbMapping getVirtualMapping () { + // return null unless this relation describes a virtual/collection node. if (!virtual) return null; + // if the collection node is prototyped, return the app's DbMapping + // for that prototype + if (prototype != null) { + return otherType; + } + // create a synthetic DbMapping that describes how to fetch the + // collection's child objects. if (virtualMapping == null) { virtualMapping = new DbMapping (ownType.app); virtualMapping.subRelation = getVirtualSubnodeRelation (); @@ -434,7 +445,7 @@ public final class Relation { String prefix = pre; if (kstr != null) { q.append (prefix); - String accessColumn = accessor == null ? otherType.getIDField () : accessor; + String accessColumn = accessName == null ? otherType.getIDField () : accessName; q.append (accessColumn); q.append (" = "); // check if column is string type and value needs to be quoted diff --git a/src/helma/scripting/fesi/HopExtension.java b/src/helma/scripting/fesi/HopExtension.java index 1b0bd16c..5e241596 100644 --- a/src/helma/scripting/fesi/HopExtension.java +++ b/src/helma/scripting/fesi/HopExtension.java @@ -8,6 +8,7 @@ import helma.framework.core.*; import helma.objectmodel.*; import helma.util.*; import helma.framework.IPathElement; +import helma.scripting.ScriptingException; import FESI.Interpreter.*; import FESI.Exceptions.*; import FESI.Extensions.*; @@ -759,10 +760,29 @@ public final class HopExtension { Object elem = thisObject.toJavaObject (); String tmpname = arguments.length == 0 ? "" : arguments[0].toString (); String basicHref =app.getNodeHref (elem, tmpname); + // check if the app.properties specify a href-function to post-process the + // basic href. + String hrefFunction = app.getProperty ("hrefFunction", null); + if (hrefFunction != null) { + Object funcElem = elem; + while (funcElem != null) { + if (engine.hasFunction (funcElem, hrefFunction)) { + Object obj; + try { + obj = engine.invoke (funcElem, hrefFunction, new Object[] {basicHref}, false); + } catch (ScriptingException x) { + throw new RuntimeException ("Error in hrefFunction: "+x); + } + if (obj == null) + throw new RuntimeException ("hrefFunction "+hrefFunction+" returned null"); + basicHref = obj.toString (); + } + funcElem = app.getParentElement (funcElem); + } + } + // check if the app.properties specify a href-skin to post-process the + // basic href. String hrefSkin = app.getProperty ("hrefSkin", null); - - // FIXME: we should actually walk down the path from the object we called href() on - // instead we move down the URL path. if (hrefSkin != null) { // we need to post-process the href with a skin for this application // first, look in the object href was called on. @@ -774,29 +794,24 @@ public final class HopExtension { Prototype proto = app.getPrototype (skinElem); if (proto != null) { skin = proto.getSkin (hrefSkin); - /* String skinid = proto.getName()+"/"+hrefSkin; - skin = res.getCachedSkin (skinid); - if (skin == null) { - skin = app.getSkin (skinElem, hrefSkin, skinpath); - res.cacheSkin (skinid, skin); - } */ } if (skin == null) skinElem = app.getParentElement (skinElem); } if (skin != null) { - return renderSkin (skin, basicHref, skinElem); + basicHref = renderSkin (skin, basicHref, skinElem); } } + return new ESString (basicHref); } - private ESString renderSkin (Skin skin, String path, Object skinElem) throws EcmaScriptException { + private String renderSkin (Skin skin, String path, Object skinElem) throws EcmaScriptException { engine.getResponse().pushStringBuffer (); HashMap param = new HashMap (); param.put ("path", path); skin.render (engine.getRequestEvaluator(), skinElem, param); - return new ESString (engine.getResponse().popStringBuffer ().trim ()); + return engine.getResponse().popStringBuffer ().trim (); } } diff --git a/src/helma/servlet/AbstractServletClient.java b/src/helma/servlet/AbstractServletClient.java index 5ec01caf..8d14aeb6 100644 --- a/src/helma/servlet/AbstractServletClient.java +++ b/src/helma/servlet/AbstractServletClient.java @@ -241,13 +241,11 @@ public abstract class AbstractServletClient extends HttpServlet { if (!hopres.cache || ! caching) { // Disable caching of response. - if (isOneDotOne (req.getProtocol ())) { - // for HTTP 1.0 - res.setHeader ("Pragma", "no-cache"); - } else { - // for HTTP 1.1 - res.setHeader ("Cache-Control", "no-cache"); - } + // for HTTP 1.0 + res.setDateHeader ("Expires", System.currentTimeMillis ()-10000); + res.setHeader ("Pragma", "no-cache"); + // for HTTP 1.1 + res.setHeader ("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0"); } if ( hopres.realm!=null ) res.setHeader( "WWW-Authenticate", "Basic realm=\"" + hopres.realm + "\"" ); diff --git a/src/helma/util/CronJob.java b/src/helma/util/CronJob.java new file mode 100644 index 00000000..05ce44ac --- /dev/null +++ b/src/helma/util/CronJob.java @@ -0,0 +1,675 @@ +package helma.util; + +/** + * Modifications by Stefan Pollach, 2002-08, 2003-03 + * First, to compile without protomatter-library (we're only interested in the + * parsing functions for helma), second to encapsulate a function call and not + * a PASEvent + */ + +/** + * The Protomatter Software License, Version 1.0 + * derived from The Apache Software License, Version 1.1 + * + * Copyright (c) 1998-2002 Nate Sammons. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed for the + * Protomatter Software Project + * (http://protomatter.sourceforge.net/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Protomatter" and "Protomatter Software Project" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact support@protomatter.com. + * + * 5. Products derived from this software may not be called "Protomatter", + * nor may "Protomatter" appear in their name, without prior written + * permission of the Protomatter Software Project + * (support@protomatter.com). + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE PROTOMATTER SOFTWARE PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +import java.util.*; + + +/** + * A cron entry, derived from Protomatter's CronEntry class. + * This class encapsulates a function call, a timeout value + * and a specification for when the given event should be + * delivered to the given topics. The specification of when + * the event should be delivered is based on the UNIX cron + * facility. + */ + + +public class CronJob { + + // used as the value in hashtables + private static Object value = new Object(); + private static Hashtable all = new Hashtable (); + private static String ALL_VALUE = "*"; + + private Hashtable year; + private Hashtable month; + private Hashtable day; + private Hashtable weekday; + private Hashtable hour; + private Hashtable minute; + + private String name = null; + private String function = null; + private long timeout = 30000; + + /** A method for parsing properties. It looks through the properties + * file for entries that look like this: + * + *
+     *  cron.name1.function = functionname
+     *
+     *  cron.name1.year     = year-list
+     *  cron.name1.month    = month-list
+     *  cron.name1.day      = day-list
+     *  cron.name1.weekday  = weekday-list
+     *  cron.name1.hour     = hour-list
+     *  cron.name1.minute   = minute-list
+     *
+     *  cron.name1.timeout  = timeout-value
+     *  
+     *  

+ * + * And delivers corresponding CronJob objects in a collection. + * The specified lists from above are:

+ * + *

    + *
    year-list
    + **
    + ** This is a comma (,) separated list of individual + * years or of year ranges. Examples: "1999,2000" or + * "1999-2004,2005-2143,2650" + *

    + * + *

    month-list
    + *
    + * This is a comma (,) separated list of month + * names. Example: "january,march,may" + *

    + * + *

    day-list
    + *
    + * This is a comma (,) separated list of individual + * day-of-month numbers or of day-of-month ranges. + * Examples: "1,15" or "1-5,7,10-24" + *

    + * + *

    weekday-list
    + *
    + * This is a comma (,) separated list of weekday names + * names. Example: "monday,tuesday" + *

    + * + *

    hour-list
    + *
    + * This is a comma (,) separated list of individual + * hours-of-day (24-hour time) or of hour-of-day ranges. + * Examples: "12,15" or "8-17,19,20-22" + *

    + * + *

    minute-list
    + *
    + * This is a comma (,) separated list of individual + * minutes (during an hour) or of minute ranges. + * Examples: "0,15,30,45" or "0-5,8-14,23,28-32" + *

    + * + *

+ * + * The value of each of those lists can also be an asterisk (*), + * which tells the cron system to disregard the given list when + * determining if a cron entry applies to a specific time -- for instance + * setting the year-list to * would cause the system to + * not take the current year into consideration. If a given list is + * not specified at all, it's the same as specifying it and giving it + * a value of *.

+ */ + + + public static Collection parse(Properties props) { + Hashtable jobs = new Hashtable (); + Enumeration e = props.keys (); + while (e.hasMoreElements ()) { + String key = (String) e.nextElement (); + if (!key.startsWith ("cron.")) + continue; + try { + StringTokenizer st = new StringTokenizer (key.trim(), "."); + st.nextElement (); + String jobName = st.nextToken (); + String jobSpec = st.nextToken (); + if (jobSpec==null || jobSpec.equals("")) // might happen with cron.testname. = XXX + continue; + CronJob job = (CronJob) jobs.get (jobName); + if (job==null) { + job = new CronJob (jobName); + jobs.put (jobName, job); + } + String value = props.getProperty (key); + if (jobSpec.equalsIgnoreCase("function")) { + job.setFunction(value); + } else if (jobSpec.equalsIgnoreCase("year")) { + parseYear (job, value); + } else if (jobSpec.equalsIgnoreCase("month")) { + parseMonth (job, value); + } else if (jobSpec.equalsIgnoreCase("day")) { + parseDay (job, value); + } else if (jobSpec.equalsIgnoreCase("weekday")) { + parseWeekDay (job, value); + } else if (jobSpec.equalsIgnoreCase("hour")) { + parseHour (job, value); + } else if (jobSpec.equalsIgnoreCase("minute")) { + parseMinute (job, value); + } + } catch (NoSuchElementException nsee) { + } + } + return jobs.values (); + } + + + public static void parseYear (CronJob job, String value) { + if (value.equals("*")) { + job.setAllYears(true); + } else { + StringTokenizer st = new StringTokenizer(value.trim(), ","); + while (st.hasMoreTokens()) { + String s = st.nextToken(); + if (s.indexOf("-") != -1) { + int start = Integer.parseInt(s.substring(0, s.indexOf("-"))); + int finish = Integer.parseInt(s.substring(s.indexOf("-") +1)); + for (int i=start; i<=finish; i++) { + job.addYear(i); + } + } else { + int y = Integer.parseInt(s); + job.addYear(y); + } + } + } + } + + public static void parseMonth (CronJob job, String value) { + if (value.equals("*")) { + job.setAllMonths(true); + } else { + StringTokenizer st = new StringTokenizer(value.trim(), ","); + while (st.hasMoreTokens()) { + String m = st.nextToken(); + if (m.equalsIgnoreCase("january")) + job.addMonth(Calendar.JANUARY); + if (m.equalsIgnoreCase("february")) + job.addMonth(Calendar.FEBRUARY); + if (m.equalsIgnoreCase("march")) + job.addMonth(Calendar.MARCH); + if (m.equalsIgnoreCase("april")) + job.addMonth(Calendar.APRIL); + if (m.equalsIgnoreCase("may")) + job.addMonth(Calendar.MAY); + if (m.equalsIgnoreCase("june")) + job.addMonth(Calendar.JUNE); + if (m.equalsIgnoreCase("july")) + job.addMonth(Calendar.JULY); + if (m.equalsIgnoreCase("august")) + job.addMonth(Calendar.AUGUST); + if (m.equalsIgnoreCase("september")) + job.addMonth(Calendar.SEPTEMBER); + if (m.equalsIgnoreCase("october")) + job.addMonth(Calendar.OCTOBER); + if (m.equalsIgnoreCase("november")) + job.addMonth(Calendar.NOVEMBER); + if (m.equalsIgnoreCase("december")) + job.addMonth(Calendar.DECEMBER); + } + } + } + + public static void parseDay (CronJob job, String day) { + if (day.equals("*")) { + job.setAllDays(true); + } else { + StringTokenizer st = new StringTokenizer(day.trim(), ","); + while (st.hasMoreTokens()) { + String s = st.nextToken(); + if (s.indexOf("-") != -1) { + int start = Integer.parseInt(s.substring(0, s.indexOf("-"))); + int finish = Integer.parseInt(s.substring(s.indexOf("-") +1)); + for (int i=start; i<=finish; i++) { + job.addDay(i); + } + } else { + int d = Integer.parseInt(s); + job.addDay(d); + } + } + } + } + + + public static void parseWeekDay (CronJob job, String weekday) { + if (weekday.equals("*")) { + job.setAllWeekdays(true); + } else { + StringTokenizer st = new StringTokenizer(weekday.trim(), ","); + while (st.hasMoreTokens()) { + String d = st.nextToken(); + if (d.equalsIgnoreCase("monday")) + job.addWeekday(Calendar.MONDAY); + if (d.equalsIgnoreCase("tuesday")) + job.addWeekday(Calendar.TUESDAY); + if (d.equalsIgnoreCase("wednesday")) + job.addWeekday(Calendar.WEDNESDAY); + if (d.equalsIgnoreCase("thursday")) + job.addWeekday(Calendar.THURSDAY); + if (d.equalsIgnoreCase("friday")) + job.addWeekday(Calendar.FRIDAY); + if (d.equalsIgnoreCase("saturday")) + job.addWeekday(Calendar.SATURDAY); + if (d.equalsIgnoreCase("sunday")) + job.addWeekday(Calendar.SUNDAY); + } + } + } + + + public static void parseHour (CronJob job, String hour) { + if (hour.equals("*")) { + job.setAllHours(true); + } else { + StringTokenizer st = new StringTokenizer(hour.trim (), ","); + while (st.hasMoreTokens()) { + String s = st.nextToken(); + if (s.indexOf("-") != -1) { + int start = Integer.parseInt(s.substring(0, s.indexOf("-"))); + int finish = Integer.parseInt(s.substring(s.indexOf("-") +1)); + for (int i=start; i<=finish; i++) { + job.addHour(i); + } + } else { + int h = Integer.parseInt(s); + job.addHour(h); + } + } + } + } + + + public static void parseMinute (CronJob job, String minute) { + if (minute.equals("*")) { + job.setAllMinutes(true); + } else { + StringTokenizer st = new StringTokenizer(minute.trim (), ","); + while (st.hasMoreTokens()) { + String s = st.nextToken(); + if (s.indexOf("-") != -1) { + int start = Integer.parseInt(s.substring(0, s.indexOf("-"))); + int finish = Integer.parseInt(s.substring(s.indexOf("-") +1)); + for (int i=start; i<=finish; i++) { + job.addMinute(i); + } + } else { + int m = Integer.parseInt(s); + job.addMinute(m); + } + } + } + } + + + + /** + * Create an empty CronJob. + */ + public CronJob (String name) { + this.name = name; + all.put (ALL_VALUE, value); + year = new Hashtable (all); + month = new Hashtable (all); + day = new Hashtable (all); + weekday = new Hashtable (all); + hour = new Hashtable (all); + minute = new Hashtable (all); + } + + /** + * Determines if this CronJob applies to the given date. + * Seconds and milliseconds in the date are ignored. + */ + public boolean appliesToDate(Date date) + { + GregorianCalendar cal = new GregorianCalendar(); + cal.setTime(date); + + // try and short-circuit as fast as possible. + Integer theYear = new Integer(cal.get(Calendar.YEAR)); + if (!year.containsKey(ALL_VALUE) && !year.containsKey(theYear)) + return false; + + Integer theMonth = new Integer(cal.get(Calendar.MONTH)); + if (!month.containsKey(ALL_VALUE) && !month.containsKey(theMonth)) + return false; + + Integer theDay = new Integer(cal.get(Calendar.DAY_OF_MONTH)); + if (!day.containsKey(ALL_VALUE) && !day.containsKey(theDay)) + return false; + + Integer theWeekDay = new Integer(cal.get(Calendar.DAY_OF_WEEK)); + if (!weekday.containsKey(ALL_VALUE) && !weekday.containsKey(theWeekDay)) + return false; + + Integer theHour = new Integer(cal.get(Calendar.HOUR_OF_DAY)); + if (!hour.containsKey(ALL_VALUE) && !hour.containsKey(theHour)) + return false; + + Integer theMinute = new Integer(cal.get(Calendar.MINUTE)); + if (!minute.containsKey(ALL_VALUE) && !minute.containsKey(theMinute)) + return false; + + return true; + } + + + /** + * Add a year to the list of years this entry applies to. + */ + public void addYear(int year) + { + this.year.remove(ALL_VALUE); + this.year.put(new Integer(year), value); + } + + /** + * Remove a year from the list of years this entry applies to. + */ + public void removeYear(int year) + { + this.year.remove(new Integer(year)); + } + + /** + * Should the current year be taken into consideration when + * deciding if this entry is applicable? + * If this is set to false (the default) then the values set with + * the addYear() and removeYear() are taken + * into consideration. If this is set to true then the current + * year is not taken into consideration. + */ + public void setAllYears(boolean set) + { + if (set) + this.year.put(ALL_VALUE, value); + else + this.year.remove(ALL_VALUE); + } + + + /** + * Add a month to the list of years this entry applies to. + * Month numbers are taken from the constants on the + * java.util.Calendar class. + */ + public void addMonth(int month) + { + this.month.remove(ALL_VALUE); + this.month.put(new Integer(month), value); + } + + /** + * Remove a month from the list of years this entry applies to. + * Month numbers are taken from the constants on the + * java.util.Calendar class. + */ + public void removeMonth(int month) + { + this.month.remove(new Integer(month)); + } + + /** + * Should the current month be taken into consideration when + * deciding if this entry is applicable? + * If this is set to false (the default) then the values set with + * the addMonth() and removeMonth() are taken + * into consideration. If this is set to true then the current + * month is not taken into consideration. + */ + public void setAllMonths(boolean set) + { + if (set) + this.month.put(ALL_VALUE, value); + else + this.month.remove(ALL_VALUE); + } + + + /** + * Add a day of the month to the list of years this entry applies to. + */ + public void addDay(int day) + { + this.day.remove(ALL_VALUE); + this.day.put(new Integer(day), value); + } + + /** + * Remove a day of the month from the list of years this entry applies to. + */ + public void removeDay(int day) + { + this.day.remove(new Integer(day)); + } + + /** + * Should the current day of the month be taken into consideration when + * deciding if this entry is applicable? + * If this is set to false (the default) then the values set with + * the addDay() and removeDay() are taken + * into consideration. If this is set to true then the current + * year is not taken into consideration. + */ + public void setAllDays(boolean set) + { + if (set) + this.day.put(ALL_VALUE, value); + else + this.day.remove(ALL_VALUE); + } + + + /** + * Add a weekday to the list of years this entry applies to. + * Weekday numbers are taken from the constants on the + * java.util.Calendar class. + */ + public void addWeekday(int weekday) + { + this.weekday.remove(ALL_VALUE); + this.weekday.put(new Integer(weekday), value); + } + + /** + * Remove a weekday from the list of years this entry applies to. + * Weekday numbers are taken from the constants on the + * java.util.Calendar class. + */ + public void removeWeekday(int weekday) + { + this.weekday.remove(new Integer(weekday)); + } + + /** + * Should the current weekday be taken into consideration when + * deciding if this entry is applicable? + * If this is set to false (the default) then the values set with + * the addWeekday() and removeWeekday() are taken + * into consideration. If this is set to true then the current + * weekday is not taken into consideration. + */ + public void setAllWeekdays(boolean set) + { + if (set) + this.weekday.put(ALL_VALUE, value); + else + this.weekday.remove(ALL_VALUE); + } + + + /** + * Add an hour to the list of years this entry applies to. + */ + public void addHour(int hour) + { + this.hour.remove(ALL_VALUE); + this.hour.put(new Integer(hour), value); + } + + /** + * Remove an hour from the list of years this entry applies to. + */ + public void removeHour(int hour) + { + this.hour.remove(new Integer(hour)); + } + + /** + * Should the current hour be taken into consideration when + * deciding if this entry is applicable? + * If this is set to false (the default) then the values set with + * the addHour() and removeHour() are taken + * into consideration. If this is set to true then the current + * hour is not taken into consideration. + */ + public void setAllHours(boolean set) + { + if (set) + this.hour.put(ALL_VALUE, value); + else + this.hour.remove(ALL_VALUE); + } + + + /** + * Add a minute to the list of years this entry applies to. + */ + public void addMinute(int minute) + { + this.minute.remove(ALL_VALUE); + this.minute.put(new Integer(minute), value); + } + + /** + * Remove a minute from the list of years this entry applies to. + */ + public void removeMinute(int minute) + { + this.minute.remove(new Integer(minute)); + } + + /** + * Should the current minute be taken into consideration when + * deciding if this entry is applicable? + * If this is set to false (the default) then the values set with + * the addMinute() and removeMinute() are taken + * into consideration. If this is set to true then the current + * minute is not taken into consideration. + */ + public void setAllMinutes(boolean set) + { + if (set) + this.minute.put(ALL_VALUE, value); + else + this.minute.remove(ALL_VALUE); + } + + + /** + * Set this entry's name + */ + public void setName(String name) + { + this.name = name; + } + + /** + * Get this entry's name + */ + public String getName() + { + return this.name; + } + + /** + * Set this entry's function + */ + public void setFunction(String function) + { + this.function = function; + } + + /** + * Get this entry's function + */ + public String getFunction() + { + return this.function; + } + + + /** + * Set this entry's timeout + */ + public void setTimeout(long timeout) + { + this.timeout = timeout; + } + + /** + * Set this entry's timeout + */ + public void setTimeout(String timeout) + { + this.timeout = Long.valueOf(timeout).longValue (); + } + + /** + * Get this entry's timeout + */ + public long getTimeout() + { + return this.timeout; + } +}