View Javadoc

1   /* (c) Copyright 2003 Caleigo AB, All rights reserved. 
2    * 
3    * This library is free software; you can redistribute it and/or
4    * modify it under the terms of the GNU Lesser General Public
5    * License as published by the Free Software Foundation; either
6    * version 2.1 of the License, or (at your option) any later version.
7    * 
8    * This library is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   * Lesser General Public License for more details.
12   * 
13   * You should have received a copy of the GNU Lesser General Public
14   * License along with this library; if not, write to the Free Software
15   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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  	// Action methods ----------------------------------------------------------
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  	// Access methods ----------------------------------------------------------
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      // Nested classes ----------------------------------------------------------
98      
99      /*** Instances of this class is returned from the <code>commitAsynchroniesly</code>
100      * method.
101      */
102     public class Job implements Serializable
103     {
104         // Constants -----------------------------------------------------------
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         // Class members -------------------------------------------------------
112         private static int JOB_ID_COUNTER = 1;
113         
114         // Data members --------------------------------------------------------
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         // Constructors --------------------------------------------------------
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         // Action methods ------------------------------------------------------
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         // Access methods ------------------------------------------------------
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             // Fire events if status has alredy changed.
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         // Help methods --------------------------------------------------------
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         // Nested classes ------------------------------------------------------
284         private class FireStartedEventThread extends Thread
285         {
286             // Constructors ----------------------------------------------------
287             public FireStartedEventThread()
288             {
289                 super("Fire Started Event Thread");
290             }
291             
292             // Superclass overrides --------------------------------------------
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             // Constructors ----------------------------------------------------
307             public FireCancelEventThread()
308             {
309                 super("Fire Cancel Event Thread");
310             }
311             
312             // Superclass overrides --------------------------------------------
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             // Constructors ----------------------------------------------------
327             public FireFailedEventThread()
328             {
329                 super("Fire Failed Event Thread");
330             }
331             
332             // Superclass overrides --------------------------------------------
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             // Data members ----------------------------------------------------
347             private int mMaxProgress;
348             private int mCurrentProgress;
349             
350             // Constructors ----------------------------------------------------
351             public FireProgressEventThread(int maxProgress, int currentProgress)
352             {
353                 super("Fire Progress Event Thread");
354                 
355                 mMaxProgress = maxProgress;
356                 mCurrentProgress = currentProgress;
357             }
358             
359             // Superclass overrides --------------------------------------------
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             // Constructors ----------------------------------------------------
374             public FireCompletedEventThread()
375             {
376                 super("Fire Completed Event Thread");
377             }
378             
379             // Superclass overrides --------------------------------------------
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         // Constants -----------------------------------------------------------
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         // Data members --------------------------------------------------------
472         private int mEventType;
473         private Job mJob;
474         private int mMaxProgress = -1;
475         private int mCurrentProgress = -1;
476         
477         // Constructors --------------------------------------------------------
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         // Access methods ------------------------------------------------------
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 }