001: /*
002: * Timer: The timer class
003: * Copyright (C) 2006-2007 Rift IT Contracting
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
018: *
019: * TimerImpl.java
020: */
021:
022: package com.rift.coad.daemon.timer;
023:
024: // java imports
025: import java.io.Serializable;
026: import java.rmi.RemoteException;
027: import java.util.ArrayList;
028: import java.util.Calendar;
029: import java.util.Date;
030: import java.util.List;
031: import javax.naming.NamingException;
032: import javax.rmi.PortableRemoteObject;
033: import javax.transaction.HeuristicMixedException;
034: import javax.transaction.HeuristicRollbackException;
035: import javax.transaction.NotSupportedException;
036: import javax.transaction.RollbackException;
037: import javax.transaction.SystemException;
038: import javax.naming.Context;
039: import javax.naming.InitialContext;
040: import javax.transaction.UserTransaction;
041: import javax.transaction.Status;
042:
043: // logging import
044: import org.apache.log4j.Logger;
045:
046: // coadunation imports
047: import com.rift.coad.lib.configuration.Configuration;
048: import com.rift.coad.lib.configuration.ConfigurationFactory;
049: import com.rift.coad.lib.common.CommonException;
050: import com.rift.coad.lib.common.ObjectSerializer;
051: import com.rift.coad.lib.bean.BeanRunnable; //import com.rift.coad.daemon.timer.db.util.HibernateUtil;
052: import com.rift.coad.hibernate.util.HibernateUtil;
053: import com.rift.coad.daemon.timer.db.Schedule;
054: import com.rift.coad.lib.thread.ThreadStateMonitor;
055: import com.rift.coad.lib.thread.CoadunationThread;
056:
057: // hibernate imports
058: import org.hibernate.Session;
059: import org.hibernate.Transaction;
060:
061: /**
062: * The Timer implementation implements the Timer interface. All the logic
063: * involved in creating, processing, listing and deleting events is within the
064: * methods of this class.
065: *
066: * @author Glynn Chaldecott
067: */
068: public class TimerImpl implements Timer, BeanRunnable {
069:
070: /**
071: * The timer thread is responsible for peforming the processing on the
072: * target.
073: */
074: public class TimerThread extends CoadunationThread {
075:
076: // private member variables
077: private Object[] row = null;
078:
079: /**
080: * The constructor of the timer thread
081: */
082: public TimerThread(Object[] row) throws Exception {
083: this .row = row;
084: }
085:
086: /**
087: * This method is reponsible for performing the processing for this
088: * object.
089: */
090: public void process() {
091: String jndi = null;
092: UserTransaction ut = null;
093: try {
094: ut = (UserTransaction) ctx
095: .lookup("java:comp/UserTransaction");
096: ut.begin();
097:
098: Session session = HibernateUtil.getInstance(
099: com.rift.coad.daemon.timer.TimerImpl.class)
100: .getSession();
101:
102: jndi = (String) row[0];
103: byte[] b_event = (byte[]) row[1];
104: byte recure = ((Byte) row[2]).byteValue();
105: int id = Integer.parseInt(row[3].toString());
106: if (recure == 0) {
107: session.createQuery(
108: "DELETE FROM Schedule as sche WHERE "
109: + "sche.id = ?").setInteger(0, id)
110: .executeUpdate();
111: }
112: Object obj = ctx.lookup(jndi);
113: com.rift.coad.daemon.timer.TimerEventHandler beanInterface = (com.rift.coad.daemon.timer.TimerEventHandler) PortableRemoteObject
114: .narrow(
115: obj,
116: com.rift.coad.daemon.timer.TimerEventHandler.class);
117: Serializable event = (Serializable) ObjectSerializer
118: .deserialize(b_event);
119: beanInterface.processEvent(event);
120:
121: ut.commit();
122: } catch (Exception ex) {
123: log.warn("Failed to process timer event for [" + jndi
124: + "] because :" + ex.getMessage(), ex);
125: try {
126: ut.rollback();
127: } catch (Exception ex2) {
128: log.error("Failed to rollback the transaction "
129: + "event :" + ex2.getMessage(), ex2);
130: }
131: }
132: }
133:
134: /**
135: * The terminate method
136: */
137: public void terminate() {
138: // ignore
139: }
140: }
141:
142: // class constants
143: private final static String TIMER_USERNAME = "timer_user";
144:
145: // the class log variable
146: protected Logger log = Logger.getLogger(TimerImpl.class.getName());
147:
148: private Context ctx = null;
149: private ThreadStateMonitor state = null;
150: private String username = null;
151:
152: /** Creates a new instance of TimerImpl */
153: public TimerImpl() throws NamingException, TimerException {
154: ctx = new InitialContext();
155: // HibernateUtil.init();
156: state = new ThreadStateMonitor(60000);
157: try {
158: Configuration config = ConfigurationFactory.getInstance()
159: .getConfig(TimerImpl.class);
160: username = config.getString(TIMER_USERNAME);
161: } catch (Exception ex) {
162: log.error("Failed to instanciate the timer : "
163: + ex.getMessage(), ex);
164: throw new TimerException(
165: "Failed to instanciate the timer : "
166: + ex.getMessage());
167: }
168: }
169:
170: /**
171: * This method will register an event on the database.
172: *
173: * @param JNDI This is a string for the JNDI of the daemon that is to be
174: * called by the event.
175: * @param month The month of the event. -1 will occur monthly. (This
176: * parameter is zero indexed)
177: * @param day The day of the event. -1 will occur daily.
178: * @param hour The hour of the event. -1 will occur hourly.
179: * @param minute The minute of the event. -1 will occur every minute.
180: * @param event This is a serializable object used to identify an individual
181: * event.
182: * @param recure The value of this indicates whether an event will occur
183: * more then once of be deleted from the database after a single
184: * occurence. True will cause it to recure, false will result in it
185: * being deleted after a single occurence
186: */
187: public void register(String JNDI, int month, int day, int hour,
188: int minute, Serializable event, boolean recure)
189: throws RemoteException, TimerException {
190: try {
191:
192: Session session = HibernateUtil.getInstance(
193: com.rift.coad.daemon.timer.TimerImpl.class)
194: .getSession();
195:
196: byte[] b_event;
197: byte recure2 = 0;
198: if (recure == false) {
199: recure2 = 0;
200: } else {
201: recure2 = 1;
202: }
203: b_event = ObjectSerializer.serialize(event);
204: Schedule schedule = new Schedule(JNDI, month, day, hour,
205: minute, b_event, recure2);
206: session.save(schedule);
207:
208: log.info("Registered a new event for : " + JNDI);
209:
210: } catch (Exception ex) {
211: log.error("Failed to register timer events :"
212: + ex.getMessage(), ex);
213: throw new TimerException(
214: "Failed to register timer events :"
215: + ex.getMessage());
216: }
217: }
218:
219: /**
220: * This method terminates the thread.
221: */
222: public void terminate() {
223: state.terminate(true);
224: }
225:
226: /**
227: * This method is the thread. It loops once every minute checking the
228: * database for events assigned to that time. It then processes the event
229: * and deletes it from the database should recure be set to false.
230: */
231: public void process() {
232: while (!state.isTerminated()) {
233: // sleep so that other things can load
234: state.monitor();
235: if (state.isTerminated()) {
236: // break out as this is terminated
237: break;
238: }
239:
240: UserTransaction ut = null;
241: try {
242:
243: ut = (UserTransaction) ctx
244: .lookup("java:comp/UserTransaction");
245:
246: ut.begin();
247:
248: Session session = HibernateUtil.getInstance(
249: com.rift.coad.daemon.timer.TimerImpl.class)
250: .getSession();
251:
252: Calendar currentDate = Calendar.getInstance();
253:
254: List list = session
255: .createSQLQuery(
256: "SELECT Schedule.jndi, "
257: + "Schedule.event, Schedule.recure, Schedule.id FROM "
258: + "Schedule WHERE "
259: + "(month=? OR month=-1)"
260: + " AND " + "(day=? OR day=-1)"
261: + " AND "
262: + "(hour=? OR hour=-1)"
263: + " AND "
264: + "(minute=? OR minute=-1)")
265: .setInteger(0, currentDate.get(Calendar.MONTH))
266: .setInteger(1,
267: currentDate.get(Calendar.DAY_OF_MONTH))
268: .setInteger(2, currentDate.get(Calendar.HOUR))
269: .setInteger(3, currentDate.get(Calendar.MINUTE))
270: .list();
271:
272: java.util.ArrayList copy = new java.util.ArrayList();
273: copy.addAll(list);
274:
275: ut.commit();
276:
277: for (int index = 0; index < list.size()
278: && !state.isTerminated(); index++) {
279: TimerThread timerThread = new TimerThread(
280: (Object[]) list.get(index));
281: timerThread.start(username);
282: }
283:
284: } catch (Exception ex) {
285: log.error("Failed to process timer events :"
286: + ex.getMessage(), ex);
287: try {
288: ut.rollback();
289: } catch (Exception ex2) {
290: log.error("Rollback failed because :"
291: + ex2.getMessage(), ex2);
292: }
293: }
294: }
295: }
296:
297: /**
298: * This method returns a list of all the events currently stored in the
299: * database.
300: *
301: * @return The method returns a List of TimerEvent objects. The TimerEvent
302: * object contains all the properties of an event from the
303: * database.
304: */
305: public TimerEvent[] listEvents() throws RemoteException,
306: TimerException {
307: String returnString = "";
308: TimerEvent[] returnList = null;
309: try {
310:
311: Session session = HibernateUtil.getInstance(
312: com.rift.coad.daemon.timer.TimerImpl.class)
313: .getSession();
314:
315: List list = session
316: .createSQLQuery(
317: "SELECT id,jndi,month,day,"
318: + "hour,minute,event,recure FROM Schedule ORDER BY id")
319: .list();
320:
321: returnList = new TimerEvent[list.size()];
322: for (int i = 0; i < list.size(); i++) {
323: Object[] row = (Object[]) list.get(i);
324: TimerEvent tempEvent = new TimerEvent();
325: tempEvent.setId(((Integer) row[0]).intValue());
326: tempEvent.setJndi(row[1].toString());
327: tempEvent.setMonth(((Integer) row[2]).intValue());
328: tempEvent.setDay(((Integer) row[3]).intValue());
329: tempEvent.setHour(((Integer) row[4]).intValue());
330: tempEvent.setMinute(((Integer) row[5]).intValue());
331: byte[] b_event = (byte[]) row[6];
332: Serializable event = (Serializable) ObjectSerializer
333: .deserialize(b_event);
334: tempEvent.setEvent(event);
335: if (((Byte) row[7]).byteValue() == 0) {
336: tempEvent.setRecure(false);
337: } else {
338: tempEvent.setRecure(true);
339: }
340: returnList[i] = tempEvent;
341: }
342:
343: } catch (Exception ex) {
344: log.error(
345: "Failed to list timer events :" + ex.getMessage(),
346: ex);
347: throw new TimerException("Failed to list timer events :"
348: + ex.getMessage());
349: }
350: return returnList;
351: }
352:
353: /**
354: * This method deletes an event based on a supplied event ID which
355: * corresponds to the database ID.
356: *
357: * @param eventID The database ID of the event.
358: */
359: public void deleteEvent(int eventId) throws RemoteException,
360: TimerException {
361: try {
362: log.info("Remove event Event id : " + eventId);
363:
364: Session session = HibernateUtil.getInstance(
365: com.rift.coad.daemon.timer.TimerImpl.class)
366: .getSession();
367:
368: session.createQuery(
369: "DELETE FROM Schedule as sche WHERE sche.id = ?")
370: .setInteger(0, eventId).executeUpdate();
371:
372: } catch (Exception ex) {
373: log.error("Failed to delete timer event :"
374: + ex.getMessage(), ex);
375: throw new TimerException("Failed to delete timer event :"
376: + ex.getMessage());
377: }
378: }
379:
380: }
|