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