EMMA Coverage Report (generated Tue Feb 12 22:23:49 ICT 2008)
[all classes][net.sourceforge.hivetranse.transaction]

COVERAGE SUMMARY FOR SOURCE FILE [AbstractTransactionService.java]

nameclass, %method, %block, %line, %
AbstractTransactionService.java100% (4/4)97%  (29/30)92%  (466/508)94%  (137.9/146)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AbstractTransactionService$1100% (1/1)100% (1/1)87%  (41/47)87%  (0.9/1)
<static initializer> 100% (1/1)87%  (41/47)87%  (0.9/1)
     
class AbstractTransactionService100% (1/1)95%  (18/19)91%  (366/402)93%  (115/123)
removeTransactionEventListener (TransactionEventListener): void 0%   (0/1)0%   (0/5)0%   (0/2)
setRollbackOnly (): void 100% (1/1)44%  (7/16)60%  (3/5)
cleanUp (): void 100% (1/1)78%  (21/27)80%  (8/10)
begin (TransactionDemarcation): void 100% (1/1)84%  (81/97)93%  (27/29)
AbstractTransactionService (Log, boolean): void 100% (1/1)100% (22/22)100% (7/7)
activateService (): void 100% (1/1)100% (7/7)100% (3/3)
addTransactionEventListener (TransactionEventListener): void 100% (1/1)100% (5/5)100% (2/2)
beginTransaction (): Object 100% (1/1)100% (31/31)100% (9/9)
cleanUp (AbstractTransactionService$Context): Exception 100% (1/1)100% (44/44)100% (11/11)
end (): void 100% (1/1)100% (57/57)100% (12/12)
fireAfterNewTransactionBegins (): void 100% (1/1)100% (15/15)100% (6/6)
fireAfterTransactionEnds (boolean): void 100% (1/1)100% (16/16)100% (6/6)
fireBeforeNewTransactionBegins (): void 100% (1/1)100% (15/15)100% (6/6)
fireBeforeTransactionEnds (boolean): void 100% (1/1)100% (16/16)100% (6/6)
getCurrentTransaction (): Object 100% (1/1)100% (9/9)100% (1/1)
passivateService (): void 100% (1/1)100% (3/3)100% (2/2)
storeTransaction (): void 100% (1/1)100% (13/13)100% (3/3)
storeTransaction (Object): void 100% (1/1)100% (1/1)100% (1/1)
threadDidDiscardService (): void 100% (1/1)100% (3/3)100% (2/2)
     
class AbstractTransactionService$Context100% (1/1)100% (6/6)100% (40/40)100% (15/15)
AbstractTransactionService$Context (boolean, AbstractTransactionService$Trans... 100% (1/1)100% (9/9)100% (4/4)
getTransaction (): Object 100% (1/1)100% (9/9)100% (3/3)
getTransactionObject (): AbstractTransactionService$TransactionObject 100% (1/1)100% (3/3)100% (1/1)
isRollbackOnly (): boolean 100% (1/1)100% (9/9)100% (3/3)
mustCleanUp (): boolean 100% (1/1)100% (3/3)100% (1/1)
setRollbackOnly (): void 100% (1/1)100% (7/7)100% (3/3)
     
class AbstractTransactionService$TransactionObject100% (1/1)100% (4/4)100% (19/19)100% (8/8)
AbstractTransactionService$TransactionObject (Object): void 100% (1/1)100% (9/9)100% (4/4)
getTransaction (): Object 100% (1/1)100% (3/3)100% (1/1)
isRollbackOnly (): boolean 100% (1/1)100% (3/3)100% (1/1)
setRollbackOnly (): void 100% (1/1)100% (4/4)100% (2/2)

1//  Copyright 2004-2007 Jean-Francois Poilpret
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14 
15package net.sourceforge.hivetranse.transaction;
16 
17import java.util.Iterator;
18 
19import org.apache.commons.logging.Log;
20import org.apache.hivemind.Discardable;
21import org.apache.hivemind.PoolManageable;
22import org.apache.hivemind.util.EventListenerList;
23 
24import net.sourceforge.hiveutils.collections.Stack;
25import net.sourceforge.hiveutils.collections.impl.StackImpl;
26 
27/**
28 * Base class for every actual implementation of <code>TransactionService</code>.
29 * This class manages the stack of transaction contexts and the notification
30 * of <code>TransactionEventListener</code>s.
31 * <p>
32 * Subclasses just have to implement two abstract methods to begin and close an
33 * actual transaction, and possibly a third method to store the changing context
34 * somewhere. For those subclasses, the notion of transaction can be any
35 * <code>Object</code>.
36 * <p>
37 * <b>ServiceModel of subclasses must be threaded or pooled</b>
38 *
39 * @author Jean-Francois Poilpret
40 */
41public abstract class AbstractTransactionService
42        implements TransactionService, Discardable, PoolManageable
43{
44        protected AbstractTransactionService(Log logger, boolean wrapRuntimeExceptions)
45        {
46                _logger = logger;
47                _wrapRuntimeExceptions = wrapRuntimeExceptions;
48        }
49 
50        // Implementation of Discardable interface
51        //-----------------------------------------
52        public void                threadDidDiscardService()
53        {
54                cleanUp();
55        }
56        
57        // Implementation of PoolManageable interface
58        //--------------------------------------------
59        public void                activateService()
60        {
61                // Make sure that current contexts are erased
62                _stack.clear();
63                _current = null;
64                //#### What about listeners? Should be taken care of by HiveMind
65                // This must be checked!
66        }
67        
68        public void                passivateService()
69        {
70                cleanUp();
71        }
72 
73        // Implementation of TransactionService interface
74        //------------------------------------------------
75        synchronized public void addTransactionEventListener(TransactionEventListener listener)
76        {
77                _listeners.addListener(listener);
78        }
79        
80        synchronized public void removeTransactionEventListener(TransactionEventListener listener)
81        {
82                _listeners.removeListener(listener);
83        }
84        
85        public Object        getCurrentTransaction()
86        {
87                return (_current != null ? _current.getTransaction() : null);
88        }
89 
90        //CSOFF: RedundantThrowsCheck
91        public void        begin(TransactionDemarcation demarcation)
92                throws        TransactionException,
93                                MandatoryTransactionException,
94                                ForbiddenTransactionException
95        {
96                boolean mustCleanUp = false;
97                TransactionObject currentTx = null;
98                if (_current != null)
99                {
100                        currentTx = _current.getTransactionObject();
101                }
102 
103                // Find out the next Connection to be used
104                // (either use the same, use new, or use none)
105                switch (demarcation)
106                {
107                        case RequiresNew:
108                        // Use new Connection
109                        currentTx = new TransactionObject(beginTransaction());
110                        mustCleanUp = true;
111                        break;
112 
113                        case Required:
114                        // Use current Connection, or create if no Connection yet
115                        if (currentTx == null)
116                        {
117                                currentTx = new TransactionObject(beginTransaction());
118                                mustCleanUp = true;
119                        }
120                        break;
121 
122                        case Supports:
123                        // Use current Connection
124                        break;
125 
126                        case Mandatory:
127                        if (currentTx == null)
128                        {
129                                _logger.error("No transaction available for Mandatory demarcation");
130                                throw new MandatoryTransactionException(
131                                                "No transaction available for Mandatory demarcation");
132                        }
133                        break;
134 
135                        case Never:
136                        if (currentTx != null)
137                        {
138                                _logger.error("Existing transaction for Never demarcation");
139                                throw new ForbiddenTransactionException(
140                                                "Existing transaction for Never demarcation");
141                        }
142                        break;
143 
144                        case NotSupported:
145                        currentTx = null;
146                        break;
147 
148                        default:
149                        _logger.error("Unknown transaction demarcation, code=" + demarcation);
150                        throw new TransactionException("Unknown transaction demarcation");
151                }
152 
153                _current = new Context(mustCleanUp, currentTx);
154                _stack.push(_current);
155                storeTransaction();
156                // If a new transaction has been created then fire the matching event
157                if (mustCleanUp)
158                {
159                        fireAfterNewTransactionBegins();
160                }
161        }
162        //CSON: RedundantThrowsCheck
163 
164        public void        end() throws TransactionException
165        {
166                // Pop current context from stack 
167                _stack.pop();
168                // Check if there is something to do
169                Exception e = cleanUp(_current);
170                boolean commit = !_current.isRollbackOnly();
171                // Set the new current transaction context
172                _current = (_stack.isEmpty() ? null : _stack.peek());
173                storeTransaction();
174                if (e != null)
175                {
176                        if (        (e instanceof RuntimeException)
177                                &&        !_wrapRuntimeExceptions)
178                        {
179                                throw (RuntimeException) e;
180                        }
181                        if (commit)
182                        {
183                                throw new TransactionException("Error occurred during transaction commit", e);
184                        }
185                        else
186                        {
187                                throw new TransactionException("Error occurred during transaction rollback", e);
188                        }
189                }
190        }
191        
192        public void        setRollbackOnly() throws TransactionException
193        {
194                if (_current == null)
195                {
196                        _logger.error("No transaction available");
197                        throw new MandatoryTransactionException("No transaction available");
198                }
199                _current.setRollbackOnly();
200        }
201 
202        protected void        fireBeforeNewTransactionBegins()
203        {
204                Iterator i = _listeners.getListeners();
205                while (i.hasNext())
206                {
207                        TransactionEventListener l = (TransactionEventListener) i.next();
208                        l.beforeNewTransactionBegins();
209                }
210        }
211 
212        protected void        fireAfterNewTransactionBegins()
213        {
214                Iterator i = _listeners.getListeners();
215                while (i.hasNext())
216                {
217                        TransactionEventListener l = (TransactionEventListener) i.next();
218                        l.afterNewTransactionBegins();
219                }
220        }
221 
222        protected void        fireBeforeTransactionEnds(boolean committed)
223        {
224                Iterator i = _listeners.getListeners();
225                while (i.hasNext())
226                {
227                        TransactionEventListener l = (TransactionEventListener) i.next();
228                        l.beforeTransactionEnds(committed);
229                }
230        }
231 
232        protected void        fireAfterTransactionEnds(boolean committed)
233        {
234                Iterator i = _listeners.getListeners();
235                while (i.hasNext())
236                {
237                        TransactionEventListener l = (TransactionEventListener) i.next();
238                        l.afterTransactionEnds(committed);
239                }
240        }
241 
242        protected void        cleanUp()
243        {
244                // Close all pending connections
245                boolean error = false;
246                while (!_stack.isEmpty())
247                {
248                        Context context = _stack.pop();
249                        context.setRollbackOnly();
250                        if (cleanUp(context) != null)
251                        {
252                                error = true;
253                        }
254                }
255                if (error)
256                {
257                        _logger.error("threadDidDiscardService could not clean up fully");
258                }
259        }
260 
261        //CSOFF: IllegalCatchCheck
262        protected Object        beginTransaction()
263        {
264                _logger.debug("beginTransaction(): starting a new transaction");
265                fireBeforeNewTransactionBegins();
266                try
267                {
268                        Object tx = createTransaction();
269                        return tx;
270                }
271                catch (Exception e)
272                {
273                        _logger.error("beginTransaction()", e);
274                        if (        (e instanceof RuntimeException)
275                                &&        !_wrapRuntimeExceptions)
276                        {
277                                throw (RuntimeException) e;
278                        }
279                        else
280                        {
281                                throw new TransactionException(e);
282                        }
283                }
284        }
285        //CSON: IllegalCatchCheck
286        
287        /**
288         * Method called when creation of a new "transaction" is needed.
289         * It must be implemented by subclasses.
290         * <p>
291         * Please note that "transaction" may be anything and does not have to be
292         * just a transaction, ie it may be a JDBC Connection, a Hibernate Session...
293         * @return the new transaction created. Internal storage of this object is
294         * performed by <code>AbstractTransactionService</code>.
295         * @exception Exception any exception, that will eventually be wrapped into
296         * an -unchecked- TransactionException
297         */
298        abstract protected Object        createTransaction() throws Exception;
299        
300        /**
301         * Method called when the current "transaction" must end, either committed
302         * or rolled back.
303         * Ending a transaction may have more meaning than just ending an actual
304         * transaction, it may as well mean closing a JDBC Connection, an Hibernate
305         * Session...
306         * @param tx the "transaction" object (ie, the same object returned by
307         * <code>createTransaction</code>)
308         * @param commit <code>true</code> if the transaction represented by
309         * <code>tx</code> must be committed, <code>false</code> if it must be
310         * rolled back
311         * @exception Exception any exception, that will eventually be wrapped into
312         * an -unchecked- TransactionException
313         */
314        abstract protected void                endTransaction(Object tx, boolean commit)
315                throws Exception;
316        
317        /**
318         * Method called whenever the current transaction context changes.
319         * Does nothing by default.
320         * <p>
321         * Override this method if you have specific needs when a transaction
322         * context changes occur. For instance, subclasses could store the new
323         * current transaction context into ThreadLocalStorage for use by other
324         * services.
325         * <p>
326         * Please carefully note that overriding this method is not equivalent to
327         * using TransactionEvents (these are triggered when a Transaction is
328         * created or ended, but they do not indicate what the next transaction
329         * context is).
330         * @param tx new current "transaction" object, as created by
331         * <code>createTransaction</code>; might be <code>null</code> to indicate
332         * that there is no available transaction context from now on.
333         */
334        protected void        storeTransaction(Object tx)
335        {
336        }
337        
338        protected void        storeTransaction()
339        {
340                Object tx = (_current != null ? _current.getTransaction() : null);
341                storeTransaction(tx);
342        }
343        
344        //CSOFF: IllegalCatchCheck
345        protected Exception                cleanUp(Context context)
346        {
347                if (!context.mustCleanUp())
348                {
349                        return null;
350                }
351                boolean commit = !context.isRollbackOnly();
352                _logger.debug("cleanUp(): ending transaction, commit=" + commit);
353                try
354                {
355                        fireBeforeTransactionEnds(commit);
356                        endTransaction(context.getTransaction(), commit);
357                        fireAfterTransactionEnds(commit);
358                        return null;
359                }
360                catch (Exception e)
361                {
362                        _logger.error("Problem committing transaction", e);
363                        return e;
364                }
365        }
366        //CSON: IllegalCatchCheck
367 
368        // For each pass through a TransactionInterceptor, an Context instance is
369        // pushed on the stack.
370        // Each Context contains the JDBC Connection to be used, and also contains a
371        // flag defining whether the Connection is owned by this Context or not.
372        // If demarcation disables transactions (ie, Never or NotSupported), then
373        // the Connection is null.
374        static protected class Context
375        {
376                public Context(boolean mustCleanUp, TransactionObject tx)
377                {
378                        _mustCleanUp = mustCleanUp;
379                        _tx = tx;
380                }
381                
382                public TransactionObject        getTransactionObject()
383                {
384                        return _tx;
385                }
386                
387                public Object        getTransaction()
388                {
389                        if (_tx != null)
390                        {
391                                return _tx.getTransaction();
392                        }
393                        else
394                        {
395                                return null;
396                        }
397                }
398 
399                public boolean        isRollbackOnly()
400                {
401                        if (_tx != null)
402                        {
403                                return _tx.isRollbackOnly();
404                        }
405                        else
406                        {
407                                return true;
408                        }
409                }
410                
411                public void                setRollbackOnly()
412                {
413                        if (_tx != null)
414                        {
415                                _tx.setRollbackOnly();
416                        }
417                }
418 
419                public boolean        mustCleanUp()
420                {
421                        return _mustCleanUp;
422                }
423                
424                private final boolean                        _mustCleanUp;
425                private final TransactionObject        _tx;
426        }
427 
428        // The TransactionObject embed both the Transaction (Object which meaning
429        // depends on actual implementation) AND the rollback status of this
430        // transaction object
431        static protected class TransactionObject
432        {
433                public TransactionObject(Object tx)
434                {
435                        _tx = tx;
436                        _rollbackOnly = false;
437                }
438                
439                public Object        getTransaction()
440                {
441                        return _tx;
442                }
443 
444                public boolean        isRollbackOnly()
445                {
446                        return _rollbackOnly;
447                }
448                
449                public void                setRollbackOnly()
450                {
451                        _rollbackOnly = true;
452                }
453                
454                private final Object        _tx;
455                private boolean                        _rollbackOnly;
456        }
457 
458        protected final Log                                        _logger;
459        protected final boolean                                _wrapRuntimeExceptions;
460        protected final Stack<Context>                _stack = new StackImpl<Context>();
461        protected final EventListenerList        _listeners = new EventListenerList();
462        protected Context                                        _current = null;
463}

[all classes][net.sourceforge.hivetranse.transaction]
EMMA 2.0.5312 (C) Vladimir Roubtsov