001: package dalma;
002:
003: import java.io.File;
004: import java.io.IOException;
005: import java.text.ParseException;
006: import java.util.Collection;
007: import java.util.Date;
008: import java.util.Map;
009: import java.util.Properties;
010: import java.util.logging.Logger;
011:
012: /**
013: * Workflow engine.
014: *
015: * @author Kohsuke Kawaguchi
016: */
017: public abstract class Engine {
018: /**
019: * Creates a new managed workflow instance.
020: *
021: * <p>
022: * This method starts the given {@link Workflow} as a dalma-managed
023: * workflow instance inside this engine.
024: *
025: * @param workflow
026: * The workflow program to be run. Must not be null.
027: * @return
028: * A {@link Conversation} object that represents the started workflow.
029: * Always non-null.
030: *
031: * @throws IllegalStateException
032: * if the engine has not {@link #start() stareted} yet.
033: */
034: public abstract Conversation createConversation(Workflow workflow)
035: throws IOException;
036:
037: /**
038: * Creates a new managed workflow instance.
039: *
040: * <p>
041: * This method is similar to {@link #createConversation(Workflow)},
042: * but it allows you to run any {@link Runnable} as a {@link Workflow}.
043: *
044: * @throws IllegalStateException
045: * if the engine has not {@link #start() stareted} yet.
046: */
047: public abstract Conversation createConversation(Runnable workflow)
048: throws IOException;
049:
050: /**
051: * Gets the {@link Conversation} of a specific ID.
052: *
053: * @return
054: * null if no such conversation exists.
055: *
056: * @throws IllegalStateException
057: * if the engine has not {@link #start() stareted} yet.
058: */
059: public abstract Conversation getConversation(int id);
060:
061: /**
062: * Returns the list of {@link Conversation}s in this engine.
063: *
064: * @return
065: * always return non-null collection. The returned object
066: * is a snapshot of the conversations.
067: *
068: * @throws IllegalStateException
069: * if the engine has not {@link #start() stareted} yet.
070: */
071: public abstract Collection<Conversation> getConversations();
072:
073: // snapshot, because of the synchronization issue
074:
075: /**
076: * Gets the number of {@link Conversation}s in this engine.
077: * <p>
078: * Short for <tt>getConversations().size()</tt>, but more efficient.
079: *
080: * @return
081: * a non-negative integer.
082: */
083: public abstract int getConversationsSize();
084:
085: /**
086: * Gets the timestamp when any of the conversations run last time.
087: *
088: * <p>
089: * For example, if all the conversations are blocked on a particular
090: * condition for 15 minutes, this method returns "now-15min".
091: *
092: * <p>
093: * This information is persisted and carried across engine lifespan,
094: * but due to a performance reason, it's only persisted lazily,
095: * and as a consequence it may fail to report a correct value when
096: * the engine terminated abnormally.
097: *
098: * <p>
099: * If nothing has ever run before (such as right after a new engine
100: * is instanciated), this method returns <tt>new Date(0)</tt>.
101: *
102: * @return
103: * always non-null.
104: */
105: public abstract Date getLastActiveTime();
106:
107: /**
108: * Gets a read-only copy of all the {@link EndPoint}s in this engine.
109: *
110: * @return
111: * always retrun non-null (but possibly empty) collection.
112: */
113: public abstract Map<String, EndPoint> getEndPoints();
114:
115: /**
116: * Gets the {@link EndPoint} of the given name.
117: *
118: * @return
119: * null if no such {@link EndPoint} is found.
120: */
121: public abstract EndPoint getEndPoint(String name);
122:
123: /**
124: * Adds a new {@link EndPoint} to this engine.
125: *
126: * @throws IllegalArgumentException
127: * if there's already an {@link EndPoint} that has the same name.
128: * @throws IllegalStateException
129: * if the engine is already started.
130: */
131: public abstract void addEndPoint(EndPoint endPoint);
132:
133: /**
134: * Creates and adds a new {@link EndPoint} to this engine.
135: *
136: * <p>
137: * See <a href="https://dalma.dev.java.net/nonav/maven/endpointURL.html">
138: * this document for details</a>.
139: *
140: * @param endPointName
141: * name of the endpoint. this will become the value
142: * returned from {@link EndPoint#getName()}. Must not be null.
143: * @param endpointURL
144: * configuration of an endpoint encoded in an URI form.
145: * must not be null.
146: * @throws ParseException
147: * if there's an error in the connection string.
148: * @throws IllegalArgumentException
149: * if there's already an {@link EndPoint} that has the same name.
150: * @throws IllegalStateException
151: * if the engine is already started.
152: * @return
153: * the endpoint created from the connection string.
154: */
155: public abstract EndPoint addEndPoint(String endPointName,
156: String endpointURL) throws ParseException;
157:
158: /**
159: * Adds multiple {@link EndPoint}s to this engine at once.
160: *
161: * <p>
162: * Suppose you have a property file like this:
163: * <pre>
164: * email1=smtp://hangman@kohsuke.org!pop3://username:password@mail.kohsuke.org
165: * email2=smtp://oracle@kohsuke.org!pop3://username:password@mail.kohsuke.org
166: * </pre>
167: * <p>
168: * You can then read this file into {@link Properties}, and then pass that
169: * into this method to add two {@link EndPoint}s with one method call.
170: * <p>
171: * This is convenient when you are externalizing the endpoint configuration
172: * in a property file.
173: *
174: * @param endpointURLs
175: * {@link Properties} that has the endpoint name as a key and
176: * endpoint URL as a value. Can be empty but must not be null.
177: * @return
178: * a map that contains the newly created {@link EndPoint}s keyed by their names.
179: * @throws ParseException
180: * if Dalma fails to parse any of the endpoint URLs.
181: * @throws IllegalStateException
182: * if the engine is already started.
183: */
184: public abstract Map<String, EndPoint> addEndPoints(
185: Properties endpointURLs) throws ParseException;
186:
187: /**
188: * Configures an engine by using <a href="http://jakarta.apache.org/bsf/">Bean Scripting Framework</a>.
189: *
190: * <p>
191: * This method is intended to run a script that configures endpoints.
192: * By moving the endpoint configuration to a script, you can allow it
193: * to be changed at runtime.
194: *
195: * <p>
196: * For this method to work, you need to have:
197: * <ol>
198: * <li><tt>bsf.jar</tt> in your classpath
199: * <li>scripting language engine that you use in your classpath
200: * (for example, if you use BeanShell, you need <tt>bsh.jar</tt>)
201: * </ol>
202: *
203: * <p>
204: * The file extension is used to determine the scripting language engine.
205: * For the list of languages available out-of-box with BSF and their
206: * registered file extensions, see
207: * <a href="http://svn.apache.org/viewcvs.cgi/jakarta/bsf/trunk/src/org/apache/bsf/Languages.properties?view=markup">this document</a>.
208: * For example, beanshell is ".bsh", groovy is ".groovy", JavaScript is ".js".
209: *
210: * <p>
211: * The {@link Engine} object is made available to the script with the name 'engine'.
212: *
213: * @param scriptFile
214: * The file that contains the script to be run. Must not be null.
215: * @see http://dalma.dev.java.net/nonav/maven/configure.html#Configuring_with_Bean_Scripting_Framework
216: */
217: public abstract void configureWithBSF(File scriptFile)
218: throws IOException;
219:
220: /**
221: * Starts the engine and activates all the {@link EndPoint}s.
222: *
223: * <p>
224: * This method must be called after all the {@link EndPoint}s are
225: * added to the engine, and before a conversation is submitted.
226: *
227: * <p>
228: * This method may throw an exception if any of the dalma components
229: * (such as the engine and endpoints) fail to start correctly.
230: *
231: * @throws IllegalStateException
232: * if the engin has already been started.
233: */
234: public abstract void start();
235:
236: /**
237: * Stops the engine and releases all the resources it acquired.
238: *
239: * <p>
240: * This method blocks until all the running {@link Conversation} suspends/completes,
241: * so it may take some time.
242: */
243: public abstract void stop();
244:
245: /**
246: * Returns true if the engine has already {@link #start() started}.
247: */
248: public abstract boolean isStarted();
249:
250: /**
251: * Sets the logger that this engine uses.
252: *
253: * <p>
254: * The default logger, if this method is not invoked,
255: * is "dalma.impl.....".
256: *
257: * @param logger
258: * if null, the engine will stop logging.
259: */
260: public abstract void setLogger(Logger logger);
261:
262: /**
263: * Waits until all the conversation in the engine exits.
264: *
265: * <p>
266: * This is different from the {@link #stop()} method;
267: * this method simply blocks the calling thread until
268: * all the conversations in this engine completes.
269: * Just because there's no conversation in the engine doesn't mean
270: * that the engine is going to shutdown.
271: */
272: public abstract void waitForCompletion()
273: throws InterruptedException;
274:
275: /**
276: * Gets the {@link ErrorHandler}.
277: *
278: * This method returns the value set by the last {@link #setErrorHandler(ErrorHandler)}
279: * invocation. The property is initially null, in which case the engine uses
280: * {@link ErrorHandler#DEFAULT}.
281: */
282: public abstract ErrorHandler getErrorHandler();
283:
284: /**
285: * Sets the {@link ErrorHandler}.
286: *
287: * <p>
288: * This {@link ErrorHandler} receives uncaught exceptions thrown from conversations.
289: *
290: * @see ErrorHandler
291: */
292: public abstract void setErrorHandler(ErrorHandler errorHandler);
293:
294: /**
295: * Adds an {@link EngineListener} to this engine.
296: *
297: * @param listener
298: * must not be null.
299: */
300: public abstract void addListener(EngineListener listener);
301:
302: /**
303: * Removes an existing {@link EngineListener} to this engine.
304: *
305: * @param listener
306: * must not be null.
307: * @throws IllegalArgumentException
308: * if the given listener was not registered to this engine.
309: */
310: public abstract void removeListener(EngineListener listener);
311: }
|