1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.caleigo.core;
20
21 import java.io.Serializable;
22 import java.util.ArrayList;
23
24 import org.caleigo.core.exception.*;
25 import org.caleigo.toolkit.log.Log;
26 import org.caleigo.toolkit.tunnel.IDistributable;
27
28
29 /*** IDataTransaction handles transactions for IDataService API.
30 *
31 * IDataTransaction differ somewhat from traditional transactions in the fact
32 * that none off the operations are actually performed until the commit method
33 * is called. Before that the operations is simple stored in ordered queue.
34 *
35 * IDataTransaction can be used as a reusable batch queue of operations
36 * that can be called and recalled at any convinance. The classes that
37 * implements the interface may NOT lock any system resources.
38 *
39 * @author Dennis Zikovic
40 * @version 1.00
41 *
42 *//*
43 *
44 * WHEN WHO WHY & WHAT
45 * ------------------------------------------------------------------------------
46 * 2001-07-20 Dennis Zikovic Creation
47 * 2004-03-01 Mattias Hagstrand Added commitAsynchroniesly and surrounding classes.
48 */
49 public interface IDataTransaction extends java.io.Serializable, org.caleigo.toolkit.tunnel.IDistributable
50 {
51
52
53 /*** Commit performs all the stored operations in the transaction.
54 * If any of the operations fail a rollback on all operations will be
55 * automatically performed and a TransactionFailedException will be thrown.
56 */
57 public void commit() throws DataServiceException;
58
59 /*** This method creates a Job that when executed will perform the same actions
60 * as the <code>commit</code> method.
61 *
62 * @param lock when the commit finishes all threads that waits on this object
63 * will be notified. This parameter may be <code>null</code>.
64 */
65 public Job commitAsynchroniesly(Object lock) throws DataServiceException;
66
67 /*** Tries to abort the currently executing transaction.
68 */
69 public void abortTransaction() throws DataServiceException;
70
71
72 /*** Returns a unique id for this data transaction.
73 */
74 public int getID();
75
76 public void addLoad(Qualifier identitQualifier, IEntity resultEntity);
77 public void addLoadSelection(IEntityDescriptor entityDescriptor, Qualifier qualifier, ISelection resultSelection);
78 public void addLoadSelection(IEntityDescriptor entityDescriptor, Qualifier qualifier, IEntityRelationPath relationPath, ISelection resultSelection);
79 public void addLoadSelection(DataQuery dataQuery, ISelection resultSelection);
80
81 public void addStore(IEntity entity);
82 public void addDelete(IEntity entity);
83 public void addRefresh(IEntity entity);
84 public void addCreate(IEntityDescriptor entityDescriptor);
85 public void addRelation(IEntityRelation relation);
86 /*** Help methods that returns true if the transaction is empty, wich means
87 * that no operations has been added to it and that commit neads no be
88 * called.
89 */
90 public boolean isEmpty();
91
92 /*** Help methods that returns true if one or more store or delete methods
93 * has been added to the transaction.
94 */
95 public boolean isMutating();
96
97
98
99 /*** Instances of this class is returned from the <code>commitAsynchroniesly</code>
100 * method.
101 */
102 public class Job implements Serializable
103 {
104
105 public final static int WAITING_TO_START = 1;
106 public final static int STARTED = 2;
107 public final static int CANCELED = 3;
108 public final static int FAILED = 4;
109 public final static int COMPLETED = 5;
110
111
112 private static int JOB_ID_COUNTER = 1;
113
114
115 private int mId;
116 private transient IDataTransaction mDataTransaction;
117 private int mStatus = WAITING_TO_START;
118 private Throwable mException;
119
120 private ArrayList mListeners = new ArrayList();
121
122
123
124 /***
125 * @param dataTransaction the data transaction that should be commited when
126 * this job is executed.
127 */
128 public Job(IDataTransaction dataTransaction)
129 {
130 mId = JOB_ID_COUNTER++;
131 mDataTransaction = dataTransaction;
132 }
133
134
135
136 /*** Tries to cancel this job. All registered listeners will receive a
137 * cancel event when the job has been successfully canceled.
138 */
139 public void cancel() throws DataServiceException
140 {
141 Log.print(this, "Job canceled");
142 mDataTransaction.abortTransaction();
143 this.fireJobCanceledEvent();
144 }
145
146
147
148 /*** Returns a unique id for this job.
149 */
150 public int getId()
151 {
152 return mId;
153 }
154
155 /*** Returns the current status of this job. This method is thread safe.
156 */
157 public synchronized int getStatus()
158 {
159 return mStatus;
160 }
161
162 public synchronized boolean isFinished()
163 {
164 return (mStatus == CANCELED || mStatus == FAILED || mStatus == COMPLETED);
165 }
166
167 /*** Adds a listener that will recevie events from this jab.
168 */
169 public void addProgressListener(final IProgressListener listener)
170 {
171 synchronized (mListeners)
172 {
173 mListeners.add(listener);
174 }
175
176
177 Runnable runnable =
178 new Runnable()
179 {
180 public void run()
181 {
182 int currentStatus = 0;
183 synchronized (this)
184 {
185 currentStatus = mStatus;
186 }
187
188 if(currentStatus>=STARTED)
189 listener.jobStarted(new ProgressEvent(Job.this, ProgressEvent.STARTED));
190 if(currentStatus==CANCELED)
191 listener.jobCanceled(new ProgressEvent(Job.this, ProgressEvent.CANCELED));
192 else if(currentStatus==FAILED)
193 listener.jobFailed(new ProgressEvent(Job.this, ProgressEvent.FAILED));
194 else if(currentStatus==COMPLETED)
195 listener.jobCompleted(new ProgressEvent(Job.this, ProgressEvent.COMPLETED));
196 }
197 };
198
199 new Thread(runnable).start();
200 }
201
202 /*** Removes a progress listener.
203 */
204 public void removeProgressListener(IProgressListener listener)
205 {
206 synchronized (mListeners)
207 {
208 mListeners.remove(listener);
209 }
210 }
211
212 public IProgressListener[] getAllProgressListeners()
213 {
214 synchronized (mListeners)
215 {
216 return (IProgressListener[]) mListeners.toArray(new IProgressListener[0]);
217 }
218 }
219
220 /*** Returns the data transaction that this job commits.
221 */
222 public IDataTransaction getDataTransaction()
223 {
224 return mDataTransaction;
225 }
226
227 /*** Returns the most recent exception that was caught while this job
228 * was executed. This is typically called after receiving a job failed
229 * event.
230 */
231 public Throwable getException()
232 {
233 return mException;
234 }
235
236
237 public void fireJobStartedEvent()
238 {
239 synchronized (this)
240 {
241 mStatus = STARTED;
242 }
243
244 new FireStartedEventThread().start();
245 }
246
247 public void fireJobCompletedEvent()
248 {
249 synchronized (this)
250 {
251 mStatus = COMPLETED;
252 }
253
254 new FireCompletedEventThread().start();
255 }
256
257 public void fireJobCanceledEvent()
258 {
259 synchronized (this)
260 {
261 mStatus = CANCELED;
262 }
263
264 new FireCancelEventThread().start();
265 }
266
267 public void fireJobFailedEvent(Throwable exception)
268 {
269 synchronized (this)
270 {
271 mStatus = FAILED;
272 }
273 mException = exception;
274
275 new FireFailedEventThread().start();
276 }
277
278 public void fireProgressChangedEvent(int maxProgress, int currentProgress)
279 {
280 new FireProgressEventThread(maxProgress, currentProgress).start();
281 }
282
283
284 private class FireStartedEventThread extends Thread
285 {
286
287 public FireStartedEventThread()
288 {
289 super("Fire Started Event Thread");
290 }
291
292
293 public void run()
294 {
295 synchronized (mListeners)
296 {
297 ProgressEvent event = new ProgressEvent(Job.this, ProgressEvent.STARTED);
298 for (int i = 0; i < mListeners.size(); i++)
299 ((IProgressListener) mListeners.get(i)).jobStarted(event);
300 }
301 }
302 }
303
304 private class FireCancelEventThread extends Thread
305 {
306
307 public FireCancelEventThread()
308 {
309 super("Fire Cancel Event Thread");
310 }
311
312
313 public void run()
314 {
315 synchronized (mListeners)
316 {
317 ProgressEvent event = new ProgressEvent(Job.this, ProgressEvent.CANCELED);
318 for (int i = 0; i < mListeners.size(); i++)
319 ((IProgressListener) mListeners.get(i)).jobCanceled(event);
320 }
321 }
322 }
323
324 private class FireFailedEventThread extends Thread
325 {
326
327 public FireFailedEventThread()
328 {
329 super("Fire Failed Event Thread");
330 }
331
332
333 public void run()
334 {
335 synchronized (mListeners)
336 {
337 ProgressEvent event = new ProgressEvent(Job.this, ProgressEvent.FAILED);
338 for (int i = 0; i < mListeners.size(); i++)
339 ((IProgressListener) mListeners.get(i)).jobFailed(event);
340 }
341 }
342 }
343
344 private class FireProgressEventThread extends Thread
345 {
346
347 private int mMaxProgress;
348 private int mCurrentProgress;
349
350
351 public FireProgressEventThread(int maxProgress, int currentProgress)
352 {
353 super("Fire Progress Event Thread");
354
355 mMaxProgress = maxProgress;
356 mCurrentProgress = currentProgress;
357 }
358
359
360 public void run()
361 {
362 synchronized (mListeners)
363 {
364 ProgressEvent event = new ProgressEvent(Job.this, ProgressEvent.PROGRESS_CHANGED, mMaxProgress, mCurrentProgress);
365 for (int i = 0; i < mListeners.size(); i++)
366 ((IProgressListener) mListeners.get(i)).progressChanged(event);
367 }
368 }
369 }
370
371 private class FireCompletedEventThread extends Thread
372 {
373
374 public FireCompletedEventThread()
375 {
376 super("Fire Completed Event Thread");
377 }
378
379
380 public void run()
381 {
382 synchronized (mListeners)
383 {
384 ProgressEvent event = new ProgressEvent(Job.this, ProgressEvent.COMPLETED);
385 for (int i = 0; i < mListeners.size(); i++)
386 ((IProgressListener) mListeners.get(i)).jobCompleted(event);
387 }
388 }
389 }
390 }
391
392 /*** Listener interface for the Job class.
393 * When the job is started the <code>jobStarted</code> method is invoked.
394 * When the job has stopped executing exactly one of the methods <code>jobCompleted</code>,
395 * <code>jobCanceled</code> or <code>jobFailed</code> is invoked. During the execution
396 * zero or more invokations of the <code>progressChanged</code> method may occur.<p>
397 * NOTE! Be careful not to add or remove progress listeners from the same
398 * job that the event was received.
399 */
400 public static interface IProgressListener extends IDistributable
401 {
402 /*** Invoked when the job has been started.
403 */
404 public void jobStarted(ProgressEvent e);
405
406 /*** Invoked when the job has been successfully completed without any
407 * errors and without beeing canceled.
408 */
409 public void jobCompleted(ProgressEvent e);
410
411 /*** Invoked when the job has been successfully canceled.
412 */
413 public void jobCanceled(ProgressEvent e);
414
415 /*** Invoked when the job has failed. Use the method <code>getException</code>
416 * on the job to get the cause of the failure.
417 */
418 public void jobFailed(ProgressEvent e);
419
420 /*** Invoked to signal progress of the job.
421 */
422 public void progressChanged(ProgressEvent e);
423 }
424
425 public static class ProgressAdapter implements IProgressListener
426 {
427 /*** Invoked when the job has been started.
428 */
429 public void jobStarted(ProgressEvent e)
430 {
431 }
432
433 /*** Invoked when the job has been successfully completed without any
434 * errors and without beeing canceled.
435 */
436 public void jobCompleted(ProgressEvent e)
437 {
438 }
439
440 /*** Invoked when the job has been successfully canceled.
441 */
442 public void jobCanceled(ProgressEvent e)
443 {
444 }
445
446 /*** Invoked when the job has failed. Use the method <code>getException</code>
447 * on the job to get the cause of the failure.
448 */
449 public void jobFailed(ProgressEvent e)
450 {
451 }
452
453 /*** Invoked to signal progress of the job.
454 */
455 public void progressChanged(ProgressEvent e)
456 {
457 }
458 }
459
460 /*** Event classed used by IProgressListener.
461 */
462 public final static class ProgressEvent extends java.util.EventObject
463 {
464
465 public static final int STARTED = 1;
466 public static final int COMPLETED = 2;
467 public static final int CANCELED = 3;
468 public static final int FAILED = 4;
469 public static final int PROGRESS_CHANGED = 5;
470
471
472 private int mEventType;
473 private Job mJob;
474 private int mMaxProgress = -1;
475 private int mCurrentProgress = -1;
476
477
478 public ProgressEvent(Job job, int eventType)
479 {
480 super(job);
481
482 mEventType = eventType;
483 mJob = job;
484 }
485
486 public ProgressEvent(Job job, int eventType, int maxProgress, int currentProgress)
487 {
488 this(job, eventType);
489
490 mMaxProgress = maxProgress;
491 mCurrentProgress = currentProgress;
492 }
493
494
495 public int getEventType()
496 {
497 return mEventType;
498 }
499
500 public Job getJob()
501 {
502 return mJob;
503 }
504
505 /*** Returns the mximum progress of the job, or -1 if no progress information
506 * is avilable.
507 */
508 public int getMaxProgress()
509 {
510 return mMaxProgress;
511 }
512
513 /*** Returns the current progress of the job, or -1 if no progress information
514 * is avilable.
515 */
516 public int getCurrentProgress()
517 {
518 return mCurrentProgress;
519 }
520 }
521 }