001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. The ASF licenses this file to You
004: * under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License. For additional information regarding
015: * copyright in this work, please see the NOTICE file in the top level
016: * directory of this distribution.
017: */
018:
019: package org.apache.roller.business.runnable;
020:
021: import java.util.Date;
022: import java.util.Enumeration;
023: import java.util.Properties;
024: import java.util.TimerTask;
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027: import org.apache.roller.RollerException;
028: import org.apache.roller.config.RollerConfig;
029: import org.apache.roller.business.RollerFactory;
030: import org.apache.roller.business.runnable.ThreadManager;
031: import org.apache.roller.util.DateUtil;
032:
033: /**
034: * An abstract class representing a scheduled task in Roller.
035: *
036: * This class extends the java.util.TimerTask class and builds in some Roller
037: * specifics, such as handling of locks for synchronization in clustered
038: * environments.
039: */
040: public abstract class RollerTask extends TimerTask {
041:
042: private static Log log = LogFactory.getLog(RollerTask.class);
043:
044: /**
045: * Initialization. Run once before the task is started.
046: */
047: public void init() throws RollerException {
048: // no-op by default
049: }
050:
051: /**
052: * Get the unique name for this task.
053: *
054: * @return The unique name for this task.
055: */
056: public abstract String getName();
057:
058: /**
059: * When should this task be started? The task is given the current time
060: * so that it may determine a start time relative to the current time,
061: * such as the end of the day or hour.
062: *
063: * It is acceptable to return the currentTime object passed in or any other
064: * time after it. If the return value is before currentTime then it will
065: * be ignored and the task will be started at currentTime.
066: *
067: * @param currentTime The current time.
068: * @return The Date when this task should be started.
069: */
070: public abstract Date getStartTime(Date currentTime);
071:
072: /**
073: * How often should the task run, in seconds.
074: *
075: * example: 3600 means this task runs once every hour.
076: *
077: * @return The interval the task should be run at, in minutes.
078: */
079: public abstract int getInterval();
080:
081: /**
082: * Get the time, in seconds, this task wants to be leased for.
083: *
084: * example: 300 means the task is allowed 5 minutes to run.
085: *
086: * @return The time this task should lease its lock for, in minutes.
087: */
088: public abstract int getLeaseTime();
089:
090: /**
091: * Run the task.
092: */
093: public abstract void runTask() throws RollerException;
094:
095: /**
096: * The run() method as called by our thread manager.
097: *
098: * This method is purposely defined as "final" so that any tasks that are
099: * defined may not override it and remove any of its functionality. It is
100: * setup to provide some basic functionality to the running of all tasks,
101: * such as lock acquisition and releasing.
102: *
103: * Roller tasks should put their logic in the runTask() method.
104: */
105: public final void run() {
106:
107: ThreadManager mgr = null;
108: try {
109: mgr = RollerFactory.getRoller().getThreadManager();
110: } catch (Exception ex) {
111: log.fatal("Unable to obtain ThreadManager", ex);
112: return;
113: }
114:
115: boolean lockAcquired = false;
116: try {
117:
118: if (!mgr.isLocked(this )) {
119:
120: log.debug("Attempting to acquire lock");
121:
122: lockAcquired = mgr.acquireLock(this );
123:
124: // now if we have a lock then run the task
125: if (lockAcquired) {
126: log.debug("Lock acquired, running task");
127: this .runTask();
128: } else {
129: log.debug("Lock NOT acquired, cannot continue");
130:
131: // when we don't have a lock we can't continue, so bail
132: return;
133: }
134:
135: } else {
136: log.debug("Task already locked, nothing to do");
137: }
138:
139: } catch (Exception ex) {
140: log.error("Unexpected exception running task", ex);
141: } finally {
142:
143: if (lockAcquired) {
144:
145: log.debug("Attempting to release lock");
146:
147: boolean lockReleased = mgr.releaseLock(this );
148:
149: if (lockReleased) {
150: log.debug("Lock released, time to sleep");
151: } else {
152: log
153: .debug("Lock NOT released, some kind of problem");
154: }
155: }
156:
157: // always release Roller session
158: RollerFactory.getRoller().release();
159: }
160:
161: }
162:
163: /**
164: * Get the properties from RollerConfig which pertain to this task.
165: *
166: * This extracts all properties from the RollerConfig of the type
167: * task.<taskname>.<prop>=value and returns them in a properties object
168: * where each item is keyed by <prop>.
169: */
170: protected Properties getTaskProperties() {
171:
172: String prefix = "tasks." + this .getName() + ".";
173:
174: Properties taskProps = new Properties();
175:
176: String key = null;
177: Enumeration keys = RollerConfig.keys();
178: while (keys.hasMoreElements()) {
179: key = (String) keys.nextElement();
180:
181: if (key.startsWith(prefix)) {
182: taskProps.setProperty(key.substring(prefix.length()),
183: RollerConfig.getProperty(key));
184: }
185: }
186:
187: return taskProps;
188: }
189:
190: /**
191: * A convenience method for calculating an adjusted time given an initial
192: * Date to work from and a "changeFactor" which describes how the time
193: * should be adjusted.
194: *
195: * Allowed change factors are ...
196: * 'immediate' - no change
197: * 'startOfHour' - top of the hour, beginning with next hour
198: * 'startOfDay' - midnight, beginning on the next day
199: */
200: protected Date getAdjustedTime(Date startTime, String changeFactor) {
201:
202: if (startTime == null || changeFactor == null) {
203: return startTime;
204: }
205:
206: Date adjustedTime = startTime;
207:
208: if ("startOfDay".equals(changeFactor)) {
209: adjustedTime = DateUtil.getEndOfDay(startTime);
210: } else if ("startOfHour".equals(changeFactor)) {
211: adjustedTime = DateUtil.getEndOfHour(startTime);
212: }
213:
214: return adjustedTime;
215: }
216:
217: }
|