Clover Coverage Report - Atlassian XWork(Aggregated)
Coverage timestamp: Wed Jul 27 2011 23:39:31 CDT
50   163   22   5.56
22   129   0.44   9
9     2.44  
1    
 
 
  TransactionalInvocation       Line # 18 50 22 79% 0.79012346
 
  (8)
 
1    package com.atlassian.xwork.interceptors;
2   
3    import org.springframework.transaction.TransactionException;
4    import org.springframework.transaction.interceptor.TransactionAttribute;
5    import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
6    import org.springframework.transaction.PlatformTransactionManager;
7    import org.springframework.transaction.TransactionStatus;
8    import org.apache.log4j.Logger;
9    import com.opensymphony.xwork.ActionInvocation;
10    import com.opensymphony.xwork.ActionProxy;
11    import com.opensymphony.xwork.interceptor.PreResultListener;
12    import com.atlassian.util.profiling.ProfilingUtils;
13   
14    /**
15    * Invoke an XWork ActionInvocation within a transaction. If the invocation throws an unchecked exception, roll
16    * back the txn.
17    */
 
18    class TransactionalInvocation
19    {
20    private static final Logger log = Logger.getLogger(TransactionalInvocation.class);
21   
22    private final TransactionAttribute transactionAttribute = new DefaultTransactionAttribute(TransactionAttribute.PROPAGATION_REQUIRED);
23    /** @deprecated since atlassian-xwork 1.1 - please remove once we've weeded out anyone who uses this (broken) functionality */
24    static final ThreadLocal currentTransactionThreadLocal = new ThreadLocal();
25    private final PlatformTransactionManager transactionManager;
26   
27    private TransactionStatus transactionStatus;
28   
 
29  8 toggle public TransactionalInvocation(PlatformTransactionManager transactionManager)
30    {
31  8 this.transactionManager = transactionManager;
32    }
33   
 
34  8 toggle public String invokeInTransaction(final ActionInvocation invocation) throws Exception
35    {
36  8 if (log.isDebugEnabled())
37  0 log.debug("Creating transaction for action invocation: " + getDetails(invocation));
38   
39  8 TransactionStatus oldTransactionStatus = (TransactionStatus) currentTransactionThreadLocal.get();
40  8 setTransactionStatus(getNewTransaction());
41   
42    // Add listener to commit transaction between action and result. If we do not do this, we run the risk
43    // that if the result is a redirect, the user will follow the redirect before the transaction has been
44    // committed, and see stale data.
45  8 invocation.addPreResultListener(new PreResultListener()
46    {
 
47  2 toggle public void beforeResult(ActionInvocation actionInvocation, String s)
48    {
49  2 commitOrRollbackTransaction(invocation, false);
50   
51  2 if (log.isDebugEnabled())
52  0 log.debug("Creating transaction for action result: " + getDetails(invocation));
53   
54  2 setTransactionStatus(getNewTransaction());
55    }
56    });
57   
58  8 boolean swallowCommitErrors = true;
59  8 try
60    {
61  8 String result = invokeAndHandleExceptions(invocation);
62  5 swallowCommitErrors = false;
63  5 return result;
64    }
65    finally
66    {
67  8 commitOrRollbackTransaction(invocation, swallowCommitErrors);
68  8 currentTransactionThreadLocal.set(oldTransactionStatus);
69    }
70    }
71   
 
72  8 toggle private String invokeAndHandleExceptions(ActionInvocation invocation) throws Exception
73    {
74  8 try
75    {
76  8 return invocation.invoke();
77    }
78    catch (Exception ex)
79    {
80  3 handleInvocationException(invocation, transactionAttribute, transactionStatus, ex);
81  3 throw ex;
82    }
83    }
84   
 
85  10 toggle private void commitOrRollbackTransaction(ActionInvocation actionInvocation, boolean swallowCommitErrors)
86    {
87  10 try
88    {
89    // If you try to commit a transaction that is completed or marked for rollback,
90    // you'll get an UnexpectedRollbackException
91  10 if (transactionStatus.isCompleted())
92    {
93  1 log.error("Action " + getDetails(actionInvocation) + " is already completed and can not be committed again.");
94    }
95  9 else if (transactionStatus.isRollbackOnly())
96    {
97  5 if (log.isDebugEnabled())
98  0 log.debug("Transaction status for action " + getDetails(actionInvocation) + " set to rollback only. Invoking rollback()");
99   
100  5 transactionManager.rollback(transactionStatus);
101    }
102    else
103    {
104  4 if (log.isDebugEnabled())
105  0 log.debug("Committing transaction for action " + getDetails(actionInvocation));
106   
107  4 transactionManager.commit(transactionStatus);
108    }
109    }
110    catch (RuntimeException e)
111    {
112  1 if (swallowCommitErrors)
113    {
114  1 log.error("Commit/Rollback exception occurred but was swallowed", e);
115    }
116    else
117    {
118  0 throw e;
119    }
120    }
121    }
122   
 
123  3 toggle private void handleInvocationException(ActionInvocation invocation, TransactionAttribute txAtt, TransactionStatus status, Throwable ex)
124    {
125  3 if (status == null)
126  0 return;
127   
128  3 if (txAtt.rollbackOn(ex))
129    {
130  3 log.info("Invoking rollback for transaction on action '" + getDetails(invocation) + "' due to throwable: " + ex, ex);
131  3 status.setRollbackOnly();
132    }
133  0 else if (log.isDebugEnabled())
134    {
135  0 log.debug("Action " + getDetails(invocation) + " threw exception " + ex + " but did not trigger a rollback.");
136    }
137    }
138   
 
139  10 toggle private TransactionStatus getNewTransaction()
140    {
141  10 return transactionManager.getTransaction(transactionAttribute);
142    }
143   
 
144  10 toggle private void setTransactionStatus(TransactionStatus transactionStatus)
145    {
146  10 this.transactionStatus = transactionStatus;
147  10 currentTransactionThreadLocal.set(transactionStatus);
148    }
149   
 
150  4 toggle private String getDetails(ActionInvocation invocation)
151    {
152  4 ActionProxy proxy = invocation.getProxy();
153  4 String methodName = proxy.getConfig().getMethodName();
154   
155  4 if (methodName == null)
156  1 methodName = "execute";
157   
158  4 String actionClazz = ProfilingUtils.getJustClassName(proxy.getConfig().getClassName());
159   
160  4 return proxy.getNamespace() + "/" + proxy.getActionName() + ".action (" + actionClazz + "." + methodName + "())";
161    }
162   
163    }