001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb3.timerservice.quartz;
023:
024: import java.sql.Connection;
025: import java.sql.PreparedStatement;
026: import java.sql.SQLException;
027: import java.util.Properties;
028:
029: import javax.ejb.TimerService;
030: import javax.management.ObjectName;
031: import javax.naming.InitialContext;
032: import javax.transaction.HeuristicMixedException;
033: import javax.transaction.HeuristicRollbackException;
034: import javax.transaction.NotSupportedException;
035: import javax.transaction.RollbackException;
036: import javax.transaction.Status;
037: import javax.transaction.SystemException;
038: import javax.transaction.TransactionManager;
039:
040: import org.jboss.ejb3.timerservice.TimedObjectInvoker;
041: import org.jboss.ejb3.timerservice.TimerServiceFactory;
042: import org.jboss.logging.Logger;
043: import org.jboss.tm.TransactionManagerService;
044: import org.quartz.Scheduler;
045: import org.quartz.SchedulerFactory;
046: import org.quartz.impl.StdSchedulerFactory;
047: import org.quartz.utils.DBConnectionManager;
048: import org.quartz.utils.JNDIConnectionProvider;
049:
050: /**
051: * Creates timer service objects for use in EJB3 containers. For this
052: * two methods are provided: createTimerService and removeTimerService.
053: *
054: * The factory can be started and stopped within both an embedded and full stack.
055: *
056: * For now only one scheduler is supported. Each bean container has its own
057: * job and trigger group within Quartz.
058: *
059: * @author <a href="mailto:carlo@nerdnet.nl">Carlo de Wolf</a>
060: * @version $Revision: 56116 $
061: */
062: public class QuartzTimerServiceFactory extends TimerServiceFactory {
063: @SuppressWarnings("unused")
064: private static final Logger log = Logger
065: .getLogger(QuartzTimerServiceFactory.class);
066:
067: private TransactionManager tm;
068:
069: private static Scheduler scheduler;
070:
071: private Properties properties;
072:
073: /**
074: * Contains the sql statements to create the database schema.
075: */
076: private Properties sqlProperties;
077:
078: private void createSchema() {
079: try {
080: tm.begin();
081: try {
082: Connection conn = getConnection();
083: try {
084: boolean success = execute(conn,
085: "CREATE_TABLE_JOB_DETAILS");
086: if (success) {
087: execute(conn, "CREATE_TABLE_JOB_LISTENERS");
088: execute(conn, "CREATE_TABLE_TRIGGERS");
089: execute(conn, "CREATE_TABLE_SIMPLE_TRIGGERS");
090: execute(conn, "CREATE_TABLE_CRON_TRIGGERS");
091: execute(conn, "CREATE_TABLE_BLOB_TRIGGERS");
092: execute(conn, "CREATE_TABLE_TRIGGER_LISTENERS");
093: execute(conn, "CREATE_TABLE_CALENDARS");
094: execute(conn,
095: "CREATE_TABLE_PAUSED_TRIGGER_GRPS");
096: execute(conn, "CREATE_TABLE_FIRED_TRIGGERS");
097: execute(conn, "CREATE_TABLE_SCHEDULER_STATE");
098: execute(conn, "CREATE_TABLE_LOCKS");
099:
100: execute(conn, "INSERT_TRIGGER_ACCESS");
101: execute(conn, "INSERT_JOB_ACCESS");
102: execute(conn, "INSERT_CALENDAR_ACCESS");
103: execute(conn, "INSERT_STATE_ACCESS");
104: execute(conn, "INSERT_MISFIRE_ACCESS");
105: }
106: } finally {
107: conn.close();
108: }
109: tm.commit();
110: } catch (SQLException e) {
111: throw new RuntimeException(e);
112: } catch (RollbackException e) {
113: throw new RuntimeException(e);
114: } catch (HeuristicMixedException e) {
115: throw new RuntimeException(e);
116: } catch (HeuristicRollbackException e) {
117: throw new RuntimeException(e);
118: } finally {
119: if (tm.getStatus() == Status.STATUS_ACTIVE)
120: tm.rollback();
121: }
122: } catch (SystemException e) {
123: throw new RuntimeException(e);
124: } catch (NotSupportedException e) {
125: throw new RuntimeException(e);
126: }
127: }
128:
129: /**
130: * Create a TimerService for use in a bean container.
131: *
132: * @param objectName the name of the bean container
133: * @param invoker the invoker to call on timeouts
134: * @return an EJB TimerService
135: */
136: public TimerService createTimerService(ObjectName objectName,
137: TimedObjectInvoker invoker) {
138: Scheduler scheduler = getScheduler();
139: if (scheduler == null)
140: return null;
141:
142: return new TimerServiceImpl(scheduler, objectName, invoker);
143: }
144:
145: private boolean execute(Connection conn, String stmtName)
146: throws SQLException {
147: String sql = sqlProperties.getProperty(stmtName);
148: if (sql == null)
149: throw new IllegalStateException("No sql set for '"
150: + stmtName + "'");
151:
152: PreparedStatement stmt = conn.prepareStatement(sql);
153: try {
154: stmt.execute();
155: return true;
156: } catch (SQLException e) {
157: log.warn("sql failed: " + sql);
158: if (log.isDebugEnabled())
159: log.debug("sql failed: " + sql, e);
160: return false;
161: } finally {
162: stmt.close();
163: }
164: }
165:
166: private Connection getConnection() throws SQLException {
167: return DBConnectionManager.getInstance().getConnection("myDS");
168: }
169:
170: /**
171: * @return the scheduler for package use
172: */
173: protected static Scheduler getScheduler() {
174: if (scheduler == null) {
175: return null;
176: //throw new IllegalStateException("TimerServiceFactory hasn't been started yet");
177: }
178:
179: return scheduler;
180: }
181:
182: public void removeTimerService(TimerService aTimerService) {
183: TimerServiceImpl timerService = (TimerServiceImpl) aTimerService;
184: timerService.shutdown();
185: }
186:
187: public void restoreTimerService(TimerService aTimerService) {
188: // TODO: implement Quartz restore timer service
189: }
190:
191: public void setDataSource(String jndiName) {
192: JNDIConnectionProvider connectionProvider = new JNDIConnectionProvider(
193: jndiName, false);
194: // FIXME: remove hardcoding
195: DBConnectionManager.getInstance().addConnectionProvider("myDS",
196: connectionProvider);
197: }
198:
199: public void setProperties(final Properties props) {
200: // if(scheduler != null)
201: // throw new IllegalStateException("already started");
202:
203: // TODO: precondition the prop
204: properties = props;
205: }
206:
207: public void setSqlProperties(Properties props) {
208: this .sqlProperties = props;
209: }
210:
211: public synchronized void start() throws Exception {
212: if (scheduler != null)
213: throw new IllegalStateException("already started");
214:
215: log.debug("properties = " + properties);
216:
217: InitialContext ctx = new InitialContext();
218: tm = (TransactionManager) ctx
219: .lookup(TransactionManagerService.JNDI_NAME);
220:
221: createSchema();
222:
223: // TODO: bind in JNDI, or is this done by the JMX bean?
224: SchedulerFactory factory;
225: if (properties == null)
226: factory = new StdSchedulerFactory();
227: else
228: factory = new StdSchedulerFactory(properties);
229: scheduler = factory.getScheduler();
230: // TODO: really start right away?
231: scheduler.start();
232: }
233:
234: public synchronized void stop() throws Exception {
235: if (scheduler == null)
236: throw new IllegalStateException("already stopped");
237:
238: // TODO: unbind from JNDI
239:
240: // TODO: standby or shutdown?
241: scheduler.shutdown();
242:
243: scheduler = null;
244: }
245: }
|