Source Code Cross Referenced for TransitionManager.java in  » Workflow-Engines » wfmopen-2.1.1 » de » danet » an » workflow » domain » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Workflow Engines » wfmopen 2.1.1 » de.danet.an.workflow.domain 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * This file is part of the WfMOpen project.
0003:         * Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
0004:         * All rights reserved.
0005:         *
0006:         * This program is free software; you can redistribute it and/or modify
0007:         * it under the terms of the GNU General Public License as published by
0008:         * the Free Software Foundation; either version 2 of the License, or
0009:         * (at your option) any later version.
0010:         *
0011:         * This program is distributed in the hope that it will be useful,
0012:         * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014:         * GNU General Public License for more details.
0015:         *
0016:         * You should have received a copy of the GNU General Public License
0017:         * along with this program; if not, write to the Free Software
0018:         * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0019:         *
0020:         * $Id: TransitionManager.java,v 1.7.2.1 2007/11/02 16:00:33 drmlipp Exp $
0021:         *
0022:         * $Log: TransitionManager.java,v $
0023:         * Revision 1.7.2.1  2007/11/02 16:00:33  drmlipp
0024:         * Merged bug fixes from HEAD.
0025:         *
0026:         * Revision 1.8  2007/09/20 21:28:55  mlipp
0027:         * Removed superfluous import and fixed line break.
0028:         *
0029:         * Revision 1.7  2007/05/03 21:58:18  mlipp
0030:         * Internal refactoring for making better use of local EJBs.
0031:         *
0032:         * Revision 1.6  2006/12/14 16:05:51  drmlipp
0033:         * Changed handling of conditions in exception transitions.
0034:         *
0035:         * Revision 1.5  2006/09/29 12:32:08  drmlipp
0036:         * Consistently using WfMOpen as projct name now.
0037:         *
0038:         * Revision 1.4  2005/10/10 20:02:12  mlipp
0039:         * Synchronized with 1.3.3.
0040:         *
0041:         * Revision 1.3  2005/02/04 14:25:26  drmlipp
0042:         * Synchronized with 1.3rc2.
0043:         *
0044:         * Revision 1.2.2.4  2005/02/01 21:15:53  drmlipp
0045:         * Fixed audit event generation for deferred choice.
0046:         *
0047:         * Revision 1.2.2.3  2005/02/01 16:08:44  drmlipp
0048:         * Implemented deferred choice.
0049:         *
0050:         * Revision 1.2.2.2  2005/01/31 21:07:31  drmlipp
0051:         * Fixed debug statement.
0052:         *
0053:         * Revision 1.2.2.1  2005/01/31 15:41:12  drmlipp
0054:         * Started implementation of deferred choice.
0055:         *
0056:         * Revision 1.2  2005/01/05 21:29:22  mlipp
0057:         * Added method to retrieve handled exceptions.
0058:         *
0059:         * Revision 1.1.1.1  2004/08/18 15:17:38  drmlipp
0060:         * Update to 1.2
0061:         *
0062:         * Revision 1.3  2004/05/06 19:39:18  lipp
0063:         * Restructured block activity handling.
0064:         *
0065:         * Revision 1.2  2004/03/21 20:33:52  lipp
0066:         * Optimized exception condition evaluation.
0067:         *
0068:         * Revision 1.1  2004/02/21 14:42:43  lipp
0069:         * Moved TransitionManager to domain package. Having it in its own
0070:         * package caused too many circular dependencies (and there are good
0071:         * points for having it in the domain package anyway).
0072:         *
0073:         * Revision 1.68  2003/12/16 14:40:18  lipp
0074:         * Improved debug message.
0075:         *
0076:         * Revision 1.67  2003/11/17 16:16:02  lipp
0077:         * Improved debug output once more.
0078:         *
0079:         * Revision 1.66  2003/11/17 14:15:17  lipp
0080:         * Better debug output.
0081:         *
0082:         * Revision 1.65  2003/09/25 11:01:20  lipp
0083:         * Fixed usage of jsScope (may not be used remotely).
0084:         *
0085:         * Revision 1.64  2003/09/24 13:49:20  lipp
0086:         * Fixed thread handling for block activities.
0087:         *
0088:         * Revision 1.63  2003/09/23 17:05:04  lipp
0089:         * Fixed various problems with block activities.
0090:         *
0091:         * Revision 1.62  2003/09/22 20:50:13  lipp
0092:         * Most of deadline handling for block activities.
0093:         *
0094:         * Revision 1.61  2003/09/22 12:32:57  lipp
0095:         * Implemented deadline creation for block activities.
0096:         *
0097:         * Revision 1.60  2003/09/21 21:28:14  lipp
0098:         * Introducing "virtual" block activity.
0099:         *
0100:         * Revision 1.59  2003/09/17 11:51:47  lipp
0101:         * Fixed evaluation context initialization.
0102:         *
0103:         * Revision 1.58  2003/09/16 15:36:56  lipp
0104:         * Added exception condition evaluation.
0105:         *
0106:         * Revision 1.57  2003/09/15 19:00:42  lipp
0107:         * Fixed quick transition check.
0108:         *
0109:         * Revision 1.56  2003/09/15 15:43:40  lipp
0110:         * Initial version of handling exceptions in transition manager.
0111:         *
0112:         * Revision 1.55  2003/09/04 08:46:44  lipp
0113:         * Fixed client dependency on rhino.jar.
0114:         *
0115:         * Revision 1.54  2003/06/29 18:38:43  lipp
0116:         * Fixed activity set instance successor behaviour.
0117:         *
0118:         * Revision 1.53  2003/06/27 08:51:45  lipp
0119:         * Fixed copyright/license information.
0120:         *
0121:         * Revision 1.52  2003/05/31 20:55:48  lipp
0122:         * Implemented OTHERWISE for AND_SPLIT.
0123:         *
0124:         * Revision 1.51  2003/05/31 18:26:43  lipp
0125:         * Fixed bug in evaluation sequence.
0126:         *
0127:         * Revision 1.50  2003/04/26 18:56:23  lipp
0128:         * Moved extended interfaces to own package.
0129:         *
0130:         * Revision 1.49  2003/03/31 16:50:28  huaiyang
0131:         * Logging using common-logging.
0132:         *
0133:         * Revision 1.48  2003/03/13 14:07:13  lipp
0134:         * Improved implementation of condition evaluation.
0135:         *
0136:         * Revision 1.47  2003/01/30 16:35:26  lipp
0137:         * Support for nested loops.
0138:         *
0139:         * Revision 1.46  2003/01/30 13:47:29  lipp
0140:         * Optimized evaluation, prefer triggering activity in xor join.
0141:         *
0142:         * Revision 1.45  2003/01/29 15:51:31  lipp
0143:         * Loops should work now.
0144:         *
0145:         * Revision 1.44  2003/01/27 15:57:25  lipp
0146:         * Added loop detection.
0147:         *
0148:         * Revision 1.43  2003/01/24 16:47:09  lipp
0149:         * Implemented thread logging.
0150:         *
0151:         * Revision 1.42  2002/12/05 16:38:20  lipp
0152:         * Implemented activity dependencies on process.terminate and .abort.
0153:         *
0154:         * Revision 1.41  2002/12/04 16:04:19  lipp
0155:         * Implemented condition evaluation.
0156:         *
0157:         * Revision 1.40  2002/11/26 11:23:29  lipp
0158:         * Modified RemoteException comment.
0159:         *
0160:         * Revision 1.39  2002/11/21 21:35:59  lipp
0161:         * Proper handling of activity set predecessors.
0162:         *
0163:         * Revision 1.38  2002/11/21 16:24:51  lipp
0164:         * Proper handling of activity set successors.
0165:         *
0166:         * Revision 1.37  2002/11/19 15:14:53  lipp
0167:         * New transition manager.
0168:         *
0169:         * Revision 1.36  2002/11/12 17:10:03  lipp
0170:         * Adapted to new State usage.
0171:         *
0172:         * Revision 1.35  2002/11/09 19:02:04  lipp
0173:         * Better handling of RemoteException.
0174:         *
0175:         * Revision 1.34  2002/10/21 19:08:05  lipp
0176:         * Continuing implementation of new state handling.
0177:         *
0178:         * Revision 1.33  2002/10/21 11:54:23  lipp
0179:         * Better suspend state handling.
0180:         *
0181:         * Revision 1.32  2002/10/15 13:22:32  huaiyang
0182:         * Remove system.out.println and printStackTrace.
0183:         *
0184:         * Revision 1.31  2002/10/08 13:52:36  lipp
0185:         * Merged branch new-state-handling.
0186:         *
0187:         * Revision 1.30.2.1  2002/10/07 15:04:53  lipp
0188:         * Reimplementing state handling, process start to activity start.
0189:         *
0190:         * Revision 1.30  2002/08/26 20:23:14  lipp
0191:         * Lots of method renames.
0192:         *
0193:         * Revision 1.29  2002/08/26 14:17:07  lipp
0194:         * JavaDoc fixes.
0195:         *
0196:         * Revision 1.28  2002/08/16 10:22:10  lipp
0197:         * Removed usage of equals().
0198:         *
0199:         * Revision 1.27  2002/05/18 11:58:22  lipp
0200:         * New interface for Transition in api (instead of using the domain class
0201:         * directly).
0202:         *
0203:         * Revision 1.26  2002/04/03 12:53:05  lipp
0204:         * JavaDoc fixes.
0205:         *
0206:         * Revision 1.25  2002/01/15 14:27:01  robert
0207:         * replace Activity, Process, ExecutionObject
0208:         * 	from workflow/domain to workflow/api
0209:         *
0210:         * Revision 1.24  2002/01/11 12:13:14  robert
0211:         * cleanup not needed import statements
0212:         *
0213:         * Revision 1.23  2001/12/18 22:16:53  lipp
0214:         * Restructured DOM generation, implemented "assignments" method from ras.
0215:         *
0216:         * Revision 1.22  2001/12/10 09:53:58  robert
0217:         * changed Vector to ArrayList
0218:         *
0219:         * Revision 1.21  2001/10/26 11:24:50  montag
0220:         * corrected using of getNextActivities() in TransitionManager
0221:         *
0222:         * Revision 1.20  2001/10/25 17:15:51  lipp
0223:         * Renamed getTransitionsRefs() to getNextActivities() (more appropriate
0224:         * name) and added TransitionRefs to DOM representatiosn.
0225:         *
0226:         * Revision 1.19  2001/10/09 13:48:28  montag
0227:         * doccheck
0228:         *
0229:         * Revision 1.18  2001/10/09 11:29:42  montag
0230:         * TransitionManager now without ejbs.
0231:         * transitionref list now contains Activity elements.
0232:         *
0233:         * Revision 1.17  2001/10/08 08:28:11  montag
0234:         * redundant primary key getters removed.
0235:         *
0236:         * Revision 1.16  2001/10/06 11:40:28  lipp
0237:         * Some javadoc fixes.
0238:         *
0239:         * Revision 1.15  2001/10/04 08:01:54  montag
0240:         * unittests adopt for new transitionmanager
0241:         *
0242:         * Revision 1.14  2001/10/02 13:15:09  montag
0243:         * allNotCompletedActivities() adapted.
0244:         *
0245:         * Revision 1.13  2001/10/02 12:35:59  montag
0246:         * hasClosedSuccessor() finished.
0247:         * TO DO: Dont allow state change of activity,
0248:         * if process is already closed.
0249:         *
0250:         * Revision 1.12  2001/10/02 10:50:28  montag
0251:         * cleanup obsolte code.
0252:         * TO DO: finish hasClosedSuccessor()
0253:         *
0254:         * Revision 1.11  2001/10/01 14:33:03  montag
0255:         * check process completeness
0256:         * TO DO: clean up obsolete code
0257:         *
0258:         * Revision 1.10  2001/10/01 10:29:52  montag
0259:         * rewriting of transition manager (fourth part):
0260:         * XOR splits now works.
0261:         * TO DO: if a process is finished now depends not
0262:         * on that all activities closed.
0263:         *
0264:         * Revision 1.9  2001/09/27 15:17:53  montag
0265:         * rewriting of transition manager (third part):
0266:         * XOR and AND join now works.
0267:         * TO DO: XOR splits
0268:         *
0269:         * Revision 1.8  2001/09/27 14:38:16  montag
0270:         * rewriting of transition manager (second part):
0271:         * now the old behaviour is available
0272:         *
0273:         * Revision 1.7  2001/09/27 12:09:50  montag
0274:         * rewriting of transition manager (first part)
0275:         *
0276:         * Revision 1.6  2001/09/26 13:13:26  montag
0277:         * new method description for allOpenableActivities().
0278:         *
0279:         * Revision 1.5  2001/09/26 08:15:39  montag
0280:         * allToActivities() implemented.
0281:         * test case for transition manager.
0282:         *
0283:         * Revision 1.4  2001/09/11 09:59:07  montag
0284:         * transition manager now checks all not completed activities
0285:         * when an activitiy is completed. next step: determine next activities
0286:         * when an activity is complete
0287:         *
0288:         * Revision 1.3  2001/09/11 09:16:50  montag
0289:         * transition manager now finds openable activities
0290:         * at process start. next step: determine next activities
0291:         * when an activity is complete
0292:         *
0293:         * Revision 1.2  2001/09/10 15:51:53  montag
0294:         * usage of bean resources, first part
0295:         *
0296:         * Revision 1.1  2001/09/06 15:48:33  montag
0297:         * initial version
0298:         *
0299:         *
0300:         */
0301:        package de.danet.an.workflow.domain;
0302:
0303:        import java.io.Serializable;
0304:
0305:        import java.util.ArrayList;
0306:        import java.util.Collection;
0307:        import java.util.Collections;
0308:        import java.util.Comparator;
0309:        import java.util.HashMap;
0310:        import java.util.HashSet;
0311:        import java.util.Iterator;
0312:        import java.util.List;
0313:        import java.util.Map;
0314:        import java.util.Set;
0315:
0316:        import java.rmi.RemoteException;
0317:
0318:        import org.mozilla.javascript.Context;
0319:        import org.mozilla.javascript.JavaScriptException;
0320:        import org.mozilla.javascript.NotAFunctionException;
0321:        import org.mozilla.javascript.PropertyException;
0322:        import org.mozilla.javascript.Scriptable;
0323:        import org.mozilla.javascript.ScriptableObject;
0324:
0325:        import de.danet.an.workflow.internalapi.ExtActivityLocal;
0326:        import de.danet.an.workflow.internalapi.ExtTransitionLocal;
0327:        import de.danet.an.workflow.internalapi.ThreadInfo;
0328:        import de.danet.an.workflow.internalapi.ExtActivityLocal.NotStartedState;
0329:        import de.danet.an.workflow.localapi.ActivityLocal;
0330:        import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
0331:        import de.danet.an.workflow.omgcore.WfExecutionObject.ClosedState;
0332:        import de.danet.an.workflow.omgcore.WfExecutionObject.NotRunningState;
0333:        import de.danet.an.workflow.omgcore.WfExecutionObject.State;
0334:
0335:        import de.danet.an.workflow.api.InvalidKeyException;
0336:        import de.danet.an.workflow.api.Activity.ClosedCompletedState;
0337:
0338:        /**
0339:         * <code>TransitionManager</code> calculates lists of activities
0340:         * with special properties of a 
0341:         * {@link de.danet.an.workflow.localcoreapi.WfProcessLocal <code>WfProcess</code>}.<P>
0342:         *
0343:         * If log level is set to <code>DEBUG</code> messages about
0344:         * intermediate evaluation results will be generated.
0345:         */
0346:        public class TransitionManager implements  Serializable {
0347:            private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
0348:                    .getLog(TransitionManager.class);
0349:
0350:            /** The associated process. */
0351:            private AbstractProcess myProcess;
0352:
0353:            /** The associated process' scriptable. */
0354:            private Scriptable procScopeCache = null;
0355:
0356:            /** All activities that are not started yet but may be started. */
0357:            private Map startableActs = new HashMap();
0358:
0359:            /** All activities that are closed. */
0360:            private Map closedActs = new HashMap();
0361:
0362:            /** All activities that are being executed. */
0363:            private Map runningActs = new HashMap();
0364:
0365:            /** Transition from preceeding activities. */
0366:            private Map transByTo = new HashMap();
0367:
0368:            /** Transition to following activities. */
0369:            private Map transByFrom = new HashMap();
0370:
0371:            /** Comparator for transitions. */
0372:            private static Comparator transComp = new Comparator() {
0373:                public int compare(Object o1, Object o2) {
0374:                    return ((ExtTransitionLocal) o1).order()
0375:                            - ((ExtTransitionLocal) o2).order();
0376:                }
0377:            };
0378:
0379:            /** 
0380:             * Helper for transition condition evaluation.
0381:             */
0382:            private class EvaluationContext {
0383:
0384:                private ExtActivityLocal fromAct = null;
0385:                private String exception = null;
0386:                private Scriptable exceptionScopeCache = null;
0387:
0388:                /**
0389:                 * Create a context with all context information initialized
0390:                 * to defaults values.
0391:                 */
0392:                public EvaluationContext() {
0393:                }
0394:
0395:                /**
0396:                 * Create a context for evaluating conditions of transitions
0397:                 * that origin at the given activity and for which the given
0398:                 * exception has been received.<P>
0399:                 *
0400:                 * @param from the origin activity
0401:                 * @param exc the exception
0402:                 */
0403:                public EvaluationContext(ExtActivityLocal from, String exc) {
0404:                    fromAct = from;
0405:                    exception = exc;
0406:                }
0407:
0408:                private final Scriptable exceptionScope()
0409:                        throws RemoteException {
0410:                    if (exceptionScopeCache == null) {
0411:                        try {
0412:                            exceptionScopeCache = (new Context())
0413:                                    .newObject(myProcess.jsScope());
0414:                            exceptionScopeCache.setPrototype(myProcess
0415:                                    .jsScope());
0416:                            exceptionScopeCache.setParentScope(null);
0417:                            ((ScriptableObject) exceptionScopeCache)
0418:                                    .defineProperty("occurredException",
0419:                                            exception,
0420:                                            ScriptableObject.READONLY);
0421:                        } catch (NotAFunctionException e) {
0422:                            logger.error(e.getMessage(), e);
0423:                        } catch (PropertyException e) {
0424:                            logger.error(e.getMessage(), e);
0425:                        } catch (JavaScriptException e) {
0426:                            logger.error(e.getMessage(), e);
0427:                        }
0428:                    }
0429:                    return exceptionScopeCache;
0430:                }
0431:
0432:                /**
0433:                 * Test if we may walk along a transition. Not that this method
0434:                 * does not verify whether the to-activity is in state "not started".<P>
0435:                 *
0436:                 * If the constructor {@link
0437:                 * #EvaluationContext(ExtActivityLocal,String) for a given
0438:                 * activity} was used, the transition must have this activity
0439:                 * as "from" activity.
0440:                 *
0441:                 * @param trans the transition to check.
0442:                 * @param exceptions <code>null</code> if normal transitions are to
0443:                 * be evaluated, else the exceptions defined for the activity with
0444:                 * the first one being the triggered exception
0445:                 * @return <code>true</code> if transit is possible.
0446:                 */
0447:                public boolean transitOK(ExtTransitionLocal trans) {
0448:                    // try special shortcuts
0449:                    switch (trans.conditionType()) {
0450:                    case de.danet.an.workflow.api.Transition.COND_TYPE_DEFAULTEXCEPTION:
0451:                        // this is OK iff the origin event is an exception
0452:                        return exception != null;
0453:
0454:                    case de.danet.an.workflow.api.Transition.COND_TYPE_EXCEPTION:
0455:                        // not OK if origin event was not an exception
0456:                        if (exception == null) {
0457:                            return false;
0458:                        }
0459:                        if (trans.condition().equals(exception)) {
0460:                            return true;
0461:                        }
0462:                        // No need to evaluate something that looks 
0463:                        // like an exception name
0464:                        if (trans.condition().trim().matches("^[\\w\\$\\s]*$")) {
0465:                            return false;
0466:                        }
0467:                        break;
0468:
0469:                    case de.danet.an.workflow.api.Transition.COND_TYPE_OTHERWISE:
0470:                    case de.danet.an.workflow.api.Transition.COND_TYPE_CONDITION:
0471:                        // not OK if origin event was an exception
0472:                        if (exception != null) {
0473:                            return false;
0474:                        }
0475:                        // always OK if target not completed and no condition
0476:                        if (trans.conditionType() == de.danet.an.workflow.api.Transition.COND_TYPE_OTHERWISE
0477:                                || trans.condition() == null) {
0478:                            if (logger.isDebugEnabled()) {
0479:                                logger.debug("Evaluating condition for "
0480:                                        + trans + " results in true");
0481:                            }
0482:                            return true;
0483:                        }
0484:                        break;
0485:                    }
0486:
0487:                    // shortcuts didn't work, evaluate
0488:                    Context cx = Context.enter();
0489:                    try {
0490:                        Scriptable proto = (exception == null ? myProcess
0491:                                .jsScope() : exceptionScope());
0492:                        Scriptable scope = cx.newObject(proto);
0493:                        scope.setPrototype(proto);
0494:                        scope.setParentScope(null);
0495:
0496:                        Object res = cx.evaluateString(scope,
0497:                                trans.condition(), "<condition>", 1, null);
0498:                        if (!(res instanceof  Boolean)) {
0499:                            logger.error("Evaluating transition condition \""
0500:                                    + trans.condition()
0501:                                    + "\" does not yield a boolean result.");
0502:                            return false;
0503:                        }
0504:                        if (logger.isDebugEnabled()) {
0505:                            logger.debug("Evaluating condition for " + trans
0506:                                    + " results in " + res);
0507:                        }
0508:                        return ((Boolean) res).booleanValue();
0509:                    } catch (Exception e) {
0510:                        if (trans.conditionType() == de.danet.an.workflow.api.Transition.COND_TYPE_EXCEPTION) {
0511:                            logger
0512:                                    .warn("Cannot evaluate transition condition \""
0513:                                            + trans.condition()
0514:                                            + "\""
0515:                                            + " (may be ignored if the exception name"
0516:                                            + " has intentionally been chosen not to look"
0517:                                            + " like an identifier): "
0518:                                            + e.getMessage());
0519:                        } else {
0520:                            logger
0521:                                    .error("Problem evaluating transition condition \""
0522:                                            + trans.condition()
0523:                                            + "\": "
0524:                                            + e.getMessage());
0525:                        }
0526:                        return false;
0527:                    } finally {
0528:                        cx.exit();
0529:                    }
0530:                }
0531:            }
0532:
0533:            /**
0534:             * Default constructor. Sets up the activity groups and transition
0535:             * maps.  Activities are grouped as "not started", "running" and
0536:             * "closed".  The additional, transient state "startable" is
0537:             * assigned if an activity is not started and has no
0538:             * predecessors. Note that activities with these properties can
0539:             * only exist in a not yet started process. Once the process has
0540:             * started, all activities that have no predecessors will no
0541:             * longer be in state "not started".
0542:             *
0543:             * @param process the process associated with this object
0544:             */
0545:            public TransitionManager(AbstractProcess process) {
0546:                if (logger.isDebugEnabled()) {
0547:                    logger.debug("Initializing transition manager for "
0548:                            + process);
0549:                }
0550:                myProcess = process;
0551:                for (Iterator i = process.stepsLocal().iterator(); i.hasNext();) {
0552:                    ExtActivityLocal a = (ExtActivityLocal) i.next();
0553:                    String key = a.key();
0554:                    transByTo.put(key, new ArrayList());
0555:                    transByFrom.put(key, new ArrayList());
0556:                }
0557:                for (Iterator i = process.transitionsLocal().iterator(); i
0558:                        .hasNext();) {
0559:                    ExtTransitionLocal t = (ExtTransitionLocal) i.next();
0560:                    Collection fts = (Collection) transByFrom.get(t.from()
0561:                            .key());
0562:                    if (fts == null) {
0563:                        // this may happen in the case of block activites with
0564:                        // exception triggered transitions, because there are
0565:                        // no real activities instantiated
0566:                        fts = new ArrayList();
0567:                        transByFrom.put(t.from().key(), fts);
0568:                    }
0569:                    fts.add(t);
0570:                    ((Collection) transByTo.get(t.to().key())).add(t);
0571:                }
0572:                for (Iterator i = transByFrom.keySet().iterator(); i.hasNext();) {
0573:                    String key = (String) i.next();
0574:                    Collections.sort((List) transByFrom.get(key), transComp);
0575:                }
0576:                for (Iterator i = transByTo.keySet().iterator(); i.hasNext();) {
0577:                    String key = (String) i.next();
0578:                    Collections.sort((List) transByTo.get(key), transComp);
0579:                }
0580:                for (Iterator i = process.stepsLocal().iterator(); i.hasNext();) {
0581:                    ExtActivityLocal a = (ExtActivityLocal) i.next();
0582:                    State as = a.typedState();
0583:                    String ak = a.key();
0584:                    if (as.isSameOrSubState(NotRunningState.NOT_STARTED)) {
0585:                        if (as.isSameOrSubState(NotStartedState.STARTABLE)) {
0586:                            startableActs.put(ak, a);
0587:                        } else if (as.isSameOrSubState(NotStartedState.UNKNOWN)
0588:                                && (((Collection) transByTo.get(ak)).size() == 0)) {
0589:                            a.setStartable(null, false);
0590:                            if (logger.isDebugEnabled()) {
0591:                                logger.debug(a + " set to startable");
0592:                            }
0593:                            startableActs.put(ak, a);
0594:                        }
0595:                    } else if (as.isSameOrSubState(State.CLOSED)) {
0596:                        closedActs.put(ak, a);
0597:                    } else {
0598:                        runningActs.put(ak, a);
0599:                    }
0600:                }
0601:                if (logger.isDebugEnabled()) {
0602:                    logState("transition manager created");
0603:                }
0604:            }
0605:
0606:            /**
0607:             * Find out if the activity with the given key is the source of
0608:             * any transition.
0609:             * @param act the activity
0610:             * @return <code>true</code> if there is at least one transition
0611:             * originating from the given activity
0612:             */
0613:            public boolean isTransitionSource(String act) {
0614:                Collection c = (Collection) transByFrom.get(act);
0615:                return c != null && c.size() > 0;
0616:            }
0617:
0618:            /**
0619:             * Verifies if no work remains to be done for this process.
0620:             * @return <code>true</code> if there are no startable or running
0621:             * activities.
0622:             */
0623:            public boolean isAtEnd() {
0624:                return startableActs.size() == 0 && runningActs.size() == 0;
0625:            }
0626:
0627:            /**
0628:             * Informs the transition manager about a change of an activity's state.
0629:             * @param act the activity that has changed its state.
0630:             */
0631:            public void update(ExtActivityLocal act) {
0632:                update(act, null);
0633:            }
0634:
0635:            /**
0636:             * Informs the transition manager about an event on an activity.
0637:             *
0638:             * @param act the activity that has received the exception.
0639:             * @param exception an exception name if an exception has occured
0640:             * on the activity or <code>null</code> if the activity has simply
0641:             * reached a new state
0642:             */
0643:            public void update(ExtActivityLocal act, String exception) {
0644:                String key = act.key();
0645:                State ats = act.typedState();
0646:                if (logger.isDebugEnabled()) {
0647:                    logState("adjusting to " + act + " updated to " + ats);
0648:                }
0649:                // Update the sets of activities in a certain state, but only
0650:                // if this is a real activity, not a block activity
0651:                // representation
0652:                if (!(act instanceof  BlockActivity)) {
0653:                    if (ats.isSameOrSubState(NotRunningState.NOT_STARTED)) {
0654:                        startableActs.remove(key);
0655:                        runningActs.remove(key);
0656:                        closedActs.remove(key);
0657:                    } else if (ats.isSameOrSubState(State.CLOSED)) {
0658:                        startableActs.remove(key);
0659:                        runningActs.remove(key);
0660:                        closedActs.put(key, act);
0661:                        if (ats.isSameOrSubState(ClosedCompletedState.NORMAL)) {
0662:                            // this implies exception == null, no conflict
0663:                            // with code below
0664:                            updateDependend(act, null);
0665:                        }
0666:                    } else {
0667:                        startableActs.remove(key);
0668:                        runningActs.put(key, act);
0669:                    }
0670:                }
0671:                if (exception != null) {
0672:                    updateDependend(act, exception);
0673:                }
0674:                if (logger.isDebugEnabled()) {
0675:                    logState("adjusted to " + act + " updated to " + ats);
0676:                }
0677:            }
0678:
0679:            /**
0680:             * Update the activities that depend on the given one. If an
0681:             * activity is closed or an exception occurs, "not started"
0682:             * activities may become startable.
0683:             *
0684:             * @param act the activity that has changed its state.
0685:             * @param exception the name of the exception that has occured
0686:             */
0687:            private void updateDependend(ExtActivityLocal act, String exception) {
0688:                if (logger.isDebugEnabled()) {
0689:                    logger.debug("Updating dependend on "
0690:                            + act.toString()
0691:                            + (exception == null ? (" split mode is " + act
0692:                                    .splitMode())
0693:                                    : (" due to exception " + exception)));
0694:                }
0695:                if (!act.typedState().isSameOrSubState(State.CLOSED)
0696:                        && exception == null) {
0697:                    throw new IllegalArgumentException(
0698:                            "State must be closed (is " + act.typedState()
0699:                                    + ")"
0700:                                    + " or there must be an exception (is "
0701:                                    + exception + ")");
0702:                }
0703:                Collection resets = new ArrayList();
0704:                Collection starts = new ArrayList();
0705:                EvaluationContext ctx = new EvaluationContext(act, exception);
0706:                if (act.splitMode().isAND() || exception != null) {
0707:                    // consider all transitions from this activity
0708:                    ExtTransitionLocal defaultTrans = null;
0709:                    boolean gotATrans = false;
0710:                    for (Iterator i = ((Collection) transByFrom.get(act.key()))
0711:                            .iterator(); i.hasNext();) {
0712:                        ExtTransitionLocal trans = (ExtTransitionLocal) i
0713:                                .next();
0714:                        if ((exception == null && trans.conditionType() == de.danet.an.workflow.api.Transition.COND_TYPE_OTHERWISE)
0715:                                || (exception != null && trans.conditionType() == de.danet.an.workflow.api.Transition.COND_TYPE_DEFAULTEXCEPTION)) {
0716:                            defaultTrans = trans;
0717:                            continue;
0718:                        }
0719:                        if (ctx.transitOK(trans)) {
0720:                            gotATrans = true;
0721:                            if (!tryTransit(trans, resets, starts)) {
0722:                                trans.setPendingToken(true);
0723:                            }
0724:                        }
0725:                    }
0726:                    if (!gotATrans && defaultTrans != null) {
0727:                        if (logger.isDebugEnabled()) {
0728:                            logger.debug("Trying default: " + defaultTrans);
0729:                        }
0730:                        if (!tryTransit(defaultTrans, resets, starts)) {
0731:                            defaultTrans.setPendingToken(true);
0732:                        }
0733:                    }
0734:                } else /* act.splitMode().isXOR() */{
0735:                    // consider only first match of all transitions from this activity
0736:                    String group = null;
0737:                    for (Iterator i = ((Collection) transByFrom.get(act.key()))
0738:                            .iterator(); i.hasNext();) {
0739:                        ExtTransitionLocal trans = (ExtTransitionLocal) i
0740:                                .next();
0741:                        boolean contTransit = false;
0742:                        if (group != null) {
0743:                            if (!trans.group().equals(group)) {
0744:                                break; // end of group, quit.
0745:                            }
0746:                            contTransit = true;
0747:                        }
0748:                        if (logger.isDebugEnabled()) {
0749:                            logger.debug("Trying transition " + trans);
0750:                        }
0751:                        // note that reevalution of transit conditions is not
0752:                        // necessary within a group, it's the same condition.
0753:                        if (contTransit || ctx.transitOK(trans)) {
0754:                            if (logger.isDebugEnabled()) {
0755:                                logger.debug("... transit OK (continued: "
0756:                                        + contTransit + ")");
0757:                            }
0758:                            if (!tryTransit(trans, resets, starts)) {
0759:                                trans.setPendingToken(true);
0760:                            }
0761:                            group = trans.group(); // continue for group
0762:                        }
0763:                    }
0764:                }
0765:                boolean deferChoiceOnSplit = act.deferChoiceOnSplit();
0766:                for (Iterator items = starts.iterator(); items.hasNext();) {
0767:                    Object[] item = (Object[]) items.next();
0768:                    ExtActivityLocal startAct = (ExtActivityLocal) item[0];
0769:                    Collection triggers = (Collection) item[1];
0770:                    setStartable(startAct, triggers, deferChoiceOnSplit);
0771:                }
0772:                for (Iterator items = resets.iterator(); items.hasNext();) {
0773:                    Object[] item = (Object[]) items.next();
0774:                    ExtActivityLocal toAct = (ExtActivityLocal) item[0];
0775:                    Collection resetActs = (Collection) item[1];
0776:
0777:                    if (logger.isDebugEnabled()) {
0778:                        logger.debug("Resetting (true): " + toAct);
0779:                    }
0780:                    toAct.reset(true, false);
0781:                    resetTokens(toAct);
0782:                    for (Iterator i = resetActs.iterator(); i.hasNext();) {
0783:                        ExtActivityLocal resAct = (ExtActivityLocal) i.next();
0784:                        if (logger.isDebugEnabled()) {
0785:                            logger.debug("Resetting (false): " + resAct);
0786:                        }
0787:                        resAct.reset(false, false);
0788:                        resetTokens(resAct);
0789:                        closedActs.remove(resAct);
0790:                    }
0791:                    setStartable(toAct, null, deferChoiceOnSplit);
0792:                }
0793:            }
0794:
0795:            private void resetTokens(ExtActivityLocal act) {
0796:                for (Iterator i = ((Collection) transByFrom.get(act.key()))
0797:                        .iterator(); i.hasNext();) {
0798:                    ExtTransitionLocal trans = (ExtTransitionLocal) i.next();
0799:                    trans.setPendingToken(false);
0800:                }
0801:            }
0802:
0803:            private void setStartable(ExtActivityLocal act,
0804:                    Collection triggers, boolean deferChoiceOnSplit) {
0805:                if (logger.isDebugEnabled()) {
0806:                    StringBuffer s = new StringBuffer();
0807:                    s.append("Setting " + act + " startable, triggers: ");
0808:                    if (triggers != null) {
0809:                        boolean first = true;
0810:                        for (Iterator i = triggers.iterator(); i.hasNext();) {
0811:                            ExtActivityLocal ta = (ExtActivityLocal) i.next();
0812:                            if (!first) {
0813:                                s.append(", ");
0814:                            }
0815:                            s.append(ta.key());
0816:                            first = false;
0817:                        }
0818:                    }
0819:                    s.append(", preliminary chosen: " + deferChoiceOnSplit);
0820:                    logger.debug(s.toString());
0821:                }
0822:                act.setStartable(triggers, deferChoiceOnSplit);
0823:                startableActs.put(act.key(), act);
0824:            }
0825:
0826:            /**
0827:             * Test if a transition is possible considering the states and
0828:             * history of the activities. Note that the condition for the
0829:             * transition passed as parameter must have been verified and must
0830:             * have evaluated to <code>true</code>.
0831:             *
0832:             * @param trans the transition.
0833:             * @param resets activities that should be reset
0834:             * @param starts activities that should be started
0835:             * @return <code>true</code> if the transition can be made
0836:             */
0837:            private boolean tryTransit(ExtTransitionLocal trans,
0838:                    Collection resets, Collection starts) {
0839:                if (logger.isDebugEnabled()) {
0840:                    logger.debug("Trying transit from " + trans.from() + " to "
0841:                            + trans.to());
0842:                }
0843:                ExtActivityLocal toAct = (ExtActivityLocal) trans.to();
0844:                if (toAct.typedState()
0845:                        .isSameOrSubState(NotStartedState.UNKNOWN)) {
0846:                    Collection triggers = isStartable(trans, toAct);
0847:                    if (triggers != null) {
0848:                        starts.add(new Object[] { toAct, triggers });
0849:                        return true;
0850:                    }
0851:                    return false;
0852:                }
0853:                ExtActivityLocal fromAct = (ExtActivityLocal) trans.from();
0854:                String toActKey = toAct.key();
0855:                if (!((toAct.typedState().isSameOrSubState(
0856:                        ClosedState.COMPLETED) && (fromAct.threadInfo()
0857:                        .includes(toActKey) || fromAct.key().equals(toActKey))))) {
0858:                    return false;
0859:                }
0860:                // Now we reset the activities in the loop
0861:                if (logger.isDebugEnabled()) {
0862:                    logger.debug("Loop detected: " + fromAct + " triggers "
0863:                            + toAct);
0864:                }
0865:                if (isStartable(trans, toAct) != null) {
0866:                    Collection resetActs = collectPredecessors(fromAct,
0867:                            toActKey);
0868:                    resets.add(new Object[] { toAct, resetActs });
0869:                    return true;
0870:                }
0871:                return false;
0872:            }
0873:
0874:            /**
0875:             * Collect all predecessors of the given activity on the way back
0876:             * to the given origin (including the start activity but excluding
0877:             * the origin).
0878:             * @param fromAct the activity to start from.
0879:             * @param origin the activity to stop with.
0880:             * @return the collected <code>Activity</code> objects. Note that
0881:             * the result is a set although this cannot formally be specified
0882:             * as the equals method is not defined for remote interfaces.
0883:             */
0884:            private Collection collectPredecessors(ExtActivityLocal fromAct,
0885:                    String origin) {
0886:                Collection collActs = new ArrayList();
0887:                collectPredecessors(collActs, new HashSet(), fromAct, origin);
0888:                return collActs;
0889:            }
0890:
0891:            /**
0892:             * Helper that collects the keys as well as the activities.
0893:             * @param collected the collected activities.
0894:             * @param collKeys the collected keys.
0895:             * @param fromAct the activity to start from.
0896:             * @param origin the activity to stop with.
0897:             */
0898:            private void collectPredecessors(Collection collected,
0899:                    Set collKeys, ExtActivityLocal fromAct, String origin) {
0900:                try {
0901:                    if (fromAct.key().equals(origin)) {
0902:                        return;
0903:                    }
0904:                    collected.add(fromAct);
0905:                    collKeys.add(fromAct.key());
0906:                    Set pres = fromAct.threadInfo().predecessorsFor(origin);
0907:                    if (pres.contains(origin)) {
0908:                        return; // optimization, avoids unnecessary activity
0909:                        // lookups in loop, not required for proper function.
0910:                    }
0911:                    for (Iterator p = pres.iterator(); p.hasNext();) {
0912:                        String k = (String) p.next();
0913:                        if (collKeys.contains(k)) {
0914:                            continue;
0915:                        }
0916:                        ExtActivityLocal a = (ExtActivityLocal) myProcess
0917:                                .activityByKeyLocal(k);
0918:                        collectPredecessors(collected, collKeys, a, origin);
0919:                    }
0920:                } catch (InvalidKeyException e) {
0921:                    // cannot happen
0922:                    throw new IllegalStateException();
0923:                }
0924:            }
0925:
0926:            /**
0927:             * Verify if an activity may be started, i.e. its predecessor(s)
0928:             * are closed and the transition conditions evaluate to true.
0929:             *
0930:             * @param toTrans the transition leading to the activity to be
0931:             * verified or <code>null</code> if the activity is to be
0932:             * re-verified without a known trigger.
0933:             * @param act the activity to be verified.
0934:             * @param exceptions <code>null</code> if normal transitions are to
0935:             * be evaluated, else the exceptions defined for the activity with
0936:             * the first one being the triggered exception
0937:             * @return the activities that trigger the given one or
0938:             * <code>null</code> if the activity is not startable
0939:             */
0940:            private Collection isStartable(ExtTransitionLocal toTrans,
0941:                    ExtActivityLocal act) {
0942:                Collection triggers = new ArrayList();
0943:                // now things depend on the join mode
0944:                if (logger.isDebugEnabled()) {
0945:                    logger.debug("Testing join for " + act + ", mode "
0946:                            + act.joinMode());
0947:                }
0948:                if (act.joinMode().isAND()) {
0949:                    ExtTransitionLocal trans = null;
0950:                    String curGroup = null;
0951:                    boolean groupOK = false;
0952:                    for (Iterator i = ((Collection) transByTo.get(act.key()))
0953:                            .iterator(); i.hasNext();) {
0954:                        trans = (ExtTransitionLocal) i.next();
0955:                        if (curGroup == null) {
0956:                            curGroup = trans.group();
0957:                        } else if (!curGroup.equals(trans.group())) {
0958:                            if (!groupOK) {
0959:                                break;
0960:                            }
0961:                            curGroup = trans.group();
0962:                            groupOK = false;
0963:                        }
0964:                        if (logger.isDebugEnabled()) {
0965:                            if (toTrans != null && trans.equals(toTrans)) {
0966:                                logger.debug("Transition " + trans
0967:                                        + " is triggering (implicitly true)");
0968:                            }
0969:                        }
0970:                        if (!(toTrans != null && trans.equals(toTrans))
0971:                                && !trans.hasPendingToken()) {
0972:                            continue;
0973:                        }
0974:                        groupOK = true;
0975:                        addToTriggers(triggers, trans.from());
0976:                    }
0977:                    if (!groupOK) {
0978:                        if (logger.isDebugEnabled()) {
0979:                            logger.debug(act + " not startable");
0980:                        }
0981:                        return null;
0982:                    }
0983:                } else { // act.joinMode().isXOR()
0984:                    addToTriggers(triggers, toTrans.from());
0985:                }
0986:                if (logger.isDebugEnabled()) {
0987:                    logger.debug(act + " is startable");
0988:                }
0989:                return triggers;
0990:            }
0991:
0992:            private void addToTriggers(Collection triggers, ActivityLocal act) {
0993:                // do not record sources of asynchronous deadlines
0994:                if (act.typedState().isSameOrSubState(State.CLOSED)) {
0995:                    if (act instanceof  BAForExceptionHandling) {
0996:                        triggers.addAll(((BAForExceptionHandling) act)
0997:                                .predecessors());
0998:                    } else {
0999:                        triggers.add(act);
1000:                    }
1001:                }
1002:            }
1003:
1004:            /**
1005:             * Return the startable activities.
1006:             * @return a collection of startable activities.
1007:             */
1008:            public Collection startableActivities() {
1009:                Collection res = new ArrayList();
1010:                for (Iterator i = startableActs.values().iterator(); i
1011:                        .hasNext();) {
1012:                    ExtActivityLocal a = (ExtActivityLocal) i.next();
1013:                    if (a.typedState().isSameOrSubState(
1014:                            NotRunningState.NOT_STARTED)) {
1015:                        res.add(a);
1016:                    }
1017:                }
1018:                return res;
1019:            }
1020:
1021:            /**
1022:             * Given an activity from a set of activities started in a
1023:             * deferred choice, reset all other (competing) activities.
1024:             * @param act the activity
1025:             */
1026:            public void resetCompeting(ExtActivityLocal act)
1027:                    throws TransitionNotAllowedException {
1028:                ThreadInfo threadInfo = act.threadInfo();
1029:                Collection res = new ArrayList();
1030:                Collection predecessors = (Collection) transByTo.get(act.key());
1031:                if (predecessors.size() == 0) {
1032:                    throw new IllegalArgumentException(
1033:                            act
1034:                                    + " has no predecessors, cannot be preliminarily chosen");
1035:                }
1036:                for (Iterator i = predecessors.iterator(); i.hasNext();) {
1037:                    ExtTransitionLocal trans = (ExtTransitionLocal) i.next();
1038:                    ExtActivityLocal fromAct = (ExtActivityLocal) trans.from();
1039:                    if (fromAct.deferChoiceOnSplit()
1040:                            && threadInfo.includes(fromAct.key())) {
1041:                        for (Iterator j = ((Collection) transByFrom.get(fromAct
1042:                                .key())).iterator(); j.hasNext();) {
1043:                            ExtTransitionLocal ft = (ExtTransitionLocal) j
1044:                                    .next();
1045:                            res.add(ft.to());
1046:                            ft.setPendingToken(false);
1047:                        }
1048:                    }
1049:                }
1050:                if (logger.isDebugEnabled()) {
1051:                    StringBuffer s = new StringBuffer();
1052:                    s.append("Preliminary chosen with " + act + ": ");
1053:                    boolean first = true;
1054:                    for (Iterator i = res.iterator(); i.hasNext();) {
1055:                        ExtActivityLocal a = (ExtActivityLocal) i.next();
1056:                        if (first) {
1057:                            first = false;
1058:                        } else {
1059:                            s.append(", ");
1060:                        }
1061:                        s.append(a.toString());
1062:                    }
1063:                    logger.debug(s);
1064:                }
1065:                // terminate others
1066:                for (Iterator i = res.iterator(); i.hasNext();) {
1067:                    ExtActivityLocal dcAct = (ExtActivityLocal) i.next();
1068:                    if (!dcAct.key().equals(act.key())) {
1069:                        try {
1070:                            if (dcAct.preliminarilyChosen()) {
1071:                                dcAct.withdrawPreliminaryChoice(true);
1072:                                update(dcAct);
1073:                            }
1074:                        } catch (TransitionNotAllowedException e) {
1075:                            logger.error("Inconsistent state: "
1076:                                    + e.getMessage(), e);
1077:                        }
1078:                    }
1079:                }
1080:                act.withdrawPreliminaryChoice(false);
1081:            }
1082:
1083:            private void logState(String context) {
1084:                logger.debug("Engine state for " + myProcess.toString()
1085:                        + "(context: " + context + "):");
1086:                logger.debug("  Transition manager: " + this );
1087:                logger.debug("  Process is at end: " + isAtEnd());
1088:                for (Iterator i = startableActs.values().iterator(); i
1089:                        .hasNext();) {
1090:                    ExtActivityLocal a = (ExtActivityLocal) i.next();
1091:                    logger.debug("  Startable: " + a);
1092:                }
1093:                for (Iterator i = runningActs.values().iterator(); i.hasNext();) {
1094:                    ExtActivityLocal a = (ExtActivityLocal) i.next();
1095:                    logger.debug("  Running: " + a);
1096:                }
1097:            }
1098:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.