1 package com.atlassian.sal.core.rdbms;
2
3 import com.atlassian.sal.api.rdbms.ConnectionCallback;
4 import com.atlassian.sal.api.rdbms.TransactionalExecutor;
5 import com.atlassian.sal.spi.HostConnectionAccessor;
6 import com.google.common.annotations.VisibleForTesting;
7 import org.slf4j.Logger;
8 import org.slf4j.LoggerFactory;
9
10 import java.sql.Connection;
11 import java.sql.SQLException;
12 import javax.annotation.Nonnull;
13
14
15
16
17
18
19
20
21 public class DefaultTransactionalExecutor implements TransactionalExecutor
22 {
23 private static final Logger log = LoggerFactory.getLogger(DefaultTransactionalExecutor.class);
24
25 private final HostConnectionAccessor hostConnectionAccessor;
26
27 @VisibleForTesting
28 boolean readOnly;
29
30 @VisibleForTesting
31 boolean newTransaction;
32
33 public DefaultTransactionalExecutor(@Nonnull final HostConnectionAccessor hostConnectionAccessor, final boolean readOnly, final boolean newTransaction)
34 {
35 this.hostConnectionAccessor = hostConnectionAccessor;
36 this.readOnly = readOnly;
37 this.newTransaction = newTransaction;
38 }
39
40 @Override
41 public <A> A execute(@Nonnull final ConnectionCallback<A> callback)
42 {
43 return hostConnectionAccessor.execute(readOnly, newTransaction, new ConnectionCallback<A>()
44 {
45 @Override
46 public A execute(final Connection connection)
47 {
48 return executeInternal(connection, callback);
49 }
50 });
51 }
52
53 @Override
54 @Nonnull
55 public TransactionalExecutor readOnly()
56 {
57 readOnly = true;
58 return this;
59 }
60
61 @Override
62 @Nonnull
63 public TransactionalExecutor readWrite()
64 {
65 readOnly = false;
66 return this;
67 }
68
69 @Override
70 @Nonnull
71 public TransactionalExecutor newTransaction()
72 {
73 newTransaction = true;
74 return this;
75 }
76
77 @Override
78 @Nonnull
79 public TransactionalExecutor existingTransaction()
80 {
81 newTransaction = false;
82 return this;
83 }
84
85 @VisibleForTesting
86 <A> A executeInternal(@Nonnull final Connection connection, @Nonnull final ConnectionCallback<A> callback)
87 {
88 assertAutoCommitFalse(connection);
89
90
91 final WrappedConnection wrappedConnection = new WrappedConnection(connection);
92 try
93 {
94
95 final A result = callback.execute(wrappedConnection);
96 try
97 {
98
99 connection.commit();
100 }
101 catch (final SQLException se)
102 {
103
104 throw new RuntimeException("Unable to commit connection", se);
105 }
106 return result;
107 }
108 catch (final RuntimeException re)
109 {
110 try
111 {
112
113 connection.rollback();
114 }
115 catch (SQLException se)
116 {
117
118 log.error("Unable to rollback connection: " + se.getMessage());
119 }
120 throw re;
121 }
122 finally
123 {
124
125 wrappedConnection.expire();
126 }
127 }
128
129 private void assertAutoCommitFalse(final Connection connection)
130 {
131 try
132 {
133 if (connection.getAutoCommit())
134 {
135 throw new IllegalStateException("com.atlassian.sal.spi.HostConnectionAccessor returned connection with autocommit set");
136 }
137 }
138 catch (SQLException e)
139 {
140 throw new RuntimeException("unable to invoke java.sql.Connection#getAutoCommit", e);
141 }
142 }
143 }