001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2004 Danet GmbH (www.danet.de), GS-AN.
004: * All rights reserved.
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * $Id: TimerHandlerEJB.java,v 1.14 2007/06/10 05:55:30 drmlipp Exp $
021: *
022: * $Log: TimerHandlerEJB.java,v $
023: * Revision 1.14 2007/06/10 05:55:30 drmlipp
024: * Using local interface for optimization.
025: *
026: * Revision 1.13 2006/12/18 13:07:31 drmlipp
027: * Fixed typo.
028: *
029: * Revision 1.12 2006/12/18 12:45:55 drmlipp
030: * Handle exception to avoid loop.
031: *
032: * Revision 1.10 2006/10/11 09:43:11 drmlipp
033: * Removed not used non-local name.
034: *
035: * Revision 1.9 2006/10/11 09:05:54 drmlipp
036: * Fixed EJB naming.
037: *
038: * Revision 1.8 2006/10/10 15:37:17 drmlipp
039: * Made TimerHandlerEJB local.
040: *
041: * Revision 1.7 2006/10/07 21:35:30 mlipp
042: * Continuing migration to J2EE 1.4.
043: *
044: * Revision 1.6 2006/10/07 20:41:34 mlipp
045: * Merged J2EE 1.4 adaptions from test branch.
046: *
047: * Revision 1.5 2006/09/29 12:32:10 drmlipp
048: * Consistently using WfMOpen as projct name now.
049: *
050: * Revision 1.4 2005/04/08 11:28:05 drmlipp
051: * Merged changes from 1.3 branch up to 1.3p6.
052: *
053: * Revision 1.3.4.1 2005/04/04 20:09:21 drmlipp
054: * Changed WLS transaction isolation.
055: *
056: * Revision 1.3 2004/10/20 20:40:32 drmlipp
057: * Made simple WaitTool terminatable.
058: *
059: * Revision 1.2 2004/09/10 12:44:30 drmlipp
060: * Enabled call by reference for weblogic by default.
061: *
062: * Revision 1.1.1.1 2004/08/18 15:17:39 drmlipp
063: * Update to 1.2
064: *
065: * Revision 1.4 2004/02/27 12:01:48 lipp
066: * Fixed permissions.
067: *
068: * Revision 1.3 2004/02/23 13:59:31 lipp
069: * Added simple waittool invocation.
070: *
071: * Revision 1.2 2004/02/21 21:31:01 lipp
072: * Some more refactoring to resolve cyclic dependencies.
073: *
074: * Revision 1.1 2004/02/20 18:56:35 lipp
075: * Renamed package waittool to timing (much better ;-)).
076: *
077: * Revision 1.3 2004/02/20 15:58:22 lipp
078: * Several WaitTool fixes.
079: *
080: * Revision 1.2 2004/02/19 21:14:48 lipp
081: * Several WaitTool fixes.
082: *
083: * Revision 1.1 2004/02/19 17:55:55 lipp
084: * Initial version of waittool.
085: *
086: */
087: package de.danet.an.workflow.tools.timing;
088:
089: import java.util.Date;
090:
091: import java.rmi.RemoteException;
092:
093: import javax.ejb.CreateException;
094: import javax.ejb.EJBException;
095: import javax.ejb.FinderException;
096: import javax.ejb.NoSuchObjectLocalException;
097: import javax.ejb.SessionBean;
098: import javax.ejb.SessionContext;
099: import javax.ejb.TimedObject;
100: import javax.ejb.Timer;
101: import javax.ejb.TimerHandle;
102:
103: import de.danet.an.util.EJBUtil;
104: import de.danet.an.util.ResourceNotAvailableException;
105:
106: import de.danet.an.workflow.localapi.ActivityLocal;
107: import de.danet.an.workflow.omgcore.CannotCompleteException;
108: import de.danet.an.workflow.omgcore.CannotStopException;
109: import de.danet.an.workflow.omgcore.InvalidDataException;
110: import de.danet.an.workflow.omgcore.NotRunningException;
111: import de.danet.an.workflow.omgcore.ProcessData;
112: import de.danet.an.workflow.omgcore.WfExecutionObject.State;
113:
114: import de.danet.an.workflow.api.ActivityUniqueKey;
115: import de.danet.an.workflow.api.DefaultProcessData;
116: import de.danet.an.workflow.api.InvalidKeyException;
117:
118: import de.danet.an.workflow.ejbs.core.WfActivityLocalHome;
119: import de.danet.an.workflow.tools.util.SimpleApplicationDirectoryLocal;
120: import de.danet.an.workflow.tools.util.SimpleApplicationDirectoryLocalHome;
121: import de.danet.an.workflow.tools.util.SimpleApplicationInfo;
122:
123: /**
124: * This EJB provides a directory for simple applications. Applications
125: * are considered simple in this context if their associated
126: * persistent state information can efficiently be represented (and
127: * stored) using a single serializable object.<P>
128: *
129: * This directory maps a unique id to the activity unique key
130: * information of the executing activity and an associated state (and
131: * vice versa). Optionally, the assignment time and the assigned
132: * resource may be saved in this directory as well.
133: *
134: * @author <a href="mailto:lipp@danet.de">Michael Lipp</a>
135: * @version $Revision: 1.14 $
136: * @ejb.bean name="TimerHandler" display-name="Timer Handler"
137: * local-jndi-name="ejb/@@@_JNDI_Name_Prefix_@@@TimerHandlerLocal"
138: * type="Stateless" transaction-type="Container" view-type="local"
139: * @ejb.transaction type="Required"
140: * @ejb.permission role-name="WfMOpenAdmin"
141: * @ejb.security-identity run-as="WfMOpenAdmin"
142: * sunone-principal="WfMOpenPrincipalForAdmin"
143: * @weblogic.run-as-identity-principal WfMOpenPrincipalForAdmin
144: * @ejb.ejb-ref ejb-name="SimpleApplicationDirectory" view-type="local"
145: * @ejb.ejb-ref ejb-name="ActivityBean" view-type="local"
146: * @ejb.ejb-external-ref ref-name="ejb/JdbcKeyGenLocal" link="KeyGen"
147: * type="Session" view-type="local" home="de.danet.an.util.KeyGenLocalHome"
148: * business="de.danet.an.util.KeyGenLocal"
149: * @weblogic.enable-call-by-reference True
150: */
151:
152: public class TimerHandlerEJB implements SessionBean, TimedObject {
153:
154: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
155: .getLog(TimerHandlerEJB.class);
156:
157: /**
158: * The SessionContext interface of the instance.
159: */
160: private SessionContext ctx;
161:
162: /** The cached home interface of the activity. */
163: private WfActivityLocalHome activityLocalHomeCache = null;
164:
165: /** Application directory. */
166: private SimpleApplicationDirectoryLocal applDirCache = null;
167:
168: /**
169: * Creates an instance of <code>TimerHandlerEJB</code>
170: * with all attributes initialized to default values.
171: */
172: public TimerHandlerEJB() {
173: }
174:
175: /**
176: * The application directory.
177: * @return the application directory
178: */
179: private SimpleApplicationDirectoryLocal applicationDirectory()
180: throws ResourceNotAvailableException {
181: if (applDirCache == null) {
182: applDirCache = (SimpleApplicationDirectoryLocal) EJBUtil
183: .createSession(
184: SimpleApplicationDirectoryLocalHome.class,
185: "java:comp/env/ejb/SimpleApplicationDirectoryLocal");
186: }
187: return applDirCache;
188: }
189:
190: /**
191: * The home interface of the WfActivityBean.
192: * @return home interface of the WfActivityBean
193: */
194: private WfActivityLocalHome activityLocalHome()
195: throws ResourceNotAvailableException {
196: if (activityLocalHomeCache == null) {
197: activityLocalHomeCache = (WfActivityLocalHome) EJBUtil
198: .retrieveEJBLocalHome(WfActivityLocalHome.class,
199: "java:comp/env/ejb/ActivityBeanLocal");
200: }
201: return activityLocalHomeCache;
202: }
203:
204: /**
205: * Save the session context asigned by the container.
206: * @param context the context
207: */
208: public void setSessionContext(SessionContext context) {
209: ctx = context;
210: }
211:
212: /**
213: * Create a new EJB.
214: * @throws CreateException if creation fails
215: */
216: public void ejbCreate() throws CreateException {
217: }
218:
219: /**
220: * Remove this EJB.
221: */
222: public void ejbRemove() {
223: ctx = null;
224: }
225:
226: /**
227: * Not called for stateless session beans.
228: * @see javax.ejb.SessionBean
229: */
230: public void ejbActivate() throws EJBException {
231: }
232:
233: /**
234: * Not called for stateless session beans.
235: * @see javax.ejb.SessionBean
236: */
237: public void ejbPassivate() throws EJBException {
238: }
239:
240: /**
241: * Create the timer for a waiting application.
242: *
243: * @param applId the application id
244: * @param waitUntil the time to wait for
245: * @return the timer
246: * @ejb.interface-method view-type="local"
247: */
248: public Object createTimer(long applId, Date waitUntil) {
249: return ctx.getTimerService().createTimer(waitUntil,
250: new Long(applId)).getHandle();
251: }
252:
253: /**
254: * Create a timer for a an activity
255: *
256: * @param auk the activity's unique key
257: * the result
258: * @param waitUntil the time to wait for
259: * @ejb.interface-method view-type="local"
260: */
261: public void createTimer(ActivityUniqueKey auk, Date waitUntil) {
262: ctx.getTimerService().createTimer(waitUntil, auk);
263: }
264:
265: /**
266: * Remove a previously created timer.
267: *
268: * @param timer the timer
269: * @ejb.interface-method view-type="local"
270: */
271: public void removeTimer(Object timer) {
272: try {
273: ((TimerHandle) timer).getTimer().cancel();
274: } catch (NoSuchObjectLocalException e) {
275: logger.warn("Trying to cancel no longer existing timer "
276: + "(may be due to a race condition): "
277: + e.getMessage(), e);
278: }
279: }
280:
281: /**
282: * Handle the timeout of a timer.
283: * @param timer the timer that has expired.
284: */
285: public void ejbTimeout(Timer timer) {
286: // Note that a RemoteException will lead to a rollback and an
287: // automatic re-invocation of this method.
288: Object info = timer.getInfo();
289: ActivityUniqueKey auk = null;
290: String resParam = null;
291: long applId = ((Long) info).longValue();
292: SimpleApplicationInfo applInfo = null;
293: try {
294: try {
295: applInfo = applicationDirectory().instanceInfo(applId);
296: } catch (InvalidKeyException e) {
297: logger.warn("Application " + applId
298: + " removed without removing "
299: + "associated timer (may be race condition).");
300: return;
301: }
302: auk = applInfo.activityUniqueKey();
303: if (logger.isDebugEnabled()) {
304: logger.debug("Handling timeout for application "
305: + applId + (auk == null ? "" : (", " + auk)));
306: }
307: applicationDirectory().removeInstance(applId);
308: if (auk == null) {
309: if (logger.isDebugEnabled()) {
310: logger.debug("Nothing to do for application "
311: + applId + " (no associated activity)");
312: }
313: return;
314: }
315: resParam = (String) ((Object[]) applInfo.state())[1];
316: ActivityLocal act = null;
317: try {
318: Long pk = Long.valueOf(auk.activityKey());
319: act = activityLocalHome().findByPrimaryKey(pk);
320: } catch (FinderException nex) {
321: logger.warn(auk + " has disappeared (doing nothing)");
322: return;
323: }
324: if (logger.isDebugEnabled()) {
325: logger.debug("Setting result for " + auk);
326: }
327: try {
328: if (resParam != null) {
329: ProcessData res = new DefaultProcessData();
330: res.put(resParam, "EXPIRED");
331: act.setResult(res);
332: }
333: act.complete();
334: if (logger.isDebugEnabled()) {
335: logger.debug("Result set for " + auk
336: + " and completed");
337: }
338: return;
339: } catch (InvalidDataException e) {
340: logger.error("Cannot set result of wait tool (" + auk
341: + " will be terminated): " + e.getMessage());
342: } catch (CannotCompleteException e) {
343: logger.error("Cannot complete " + auk
344: + " (will be terminated): " + e.getMessage());
345: }
346: if (act.typedState().workflowState() == State.OPEN) {
347: try {
348: act.terminate();
349: } catch (CannotStopException e) {
350: logger.error("Cannot terminate " + auk + ": "
351: + e.getMessage());
352: } catch (NotRunningException e) {
353: logger.warn(auk
354: + " not running although state is open?");
355: }
356: }
357: } catch (RemoteException e) {
358: throw new EJBException(e);
359: }
360: }
361:
362: }
|