View Javadoc

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   * Default implementation that invokes {@link com.atlassian.sal.spi.HostConnectionAccessor}.
17   * <p/>
18   * Created by {@link com.atlassian.sal.core.rdbms.DefaultTransactionalExecutorFactory}
19   *
20   * @since 3.0
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          // give the user the restricted connection
92          try (final WrappedConnection wrappedConnection = new WrappedConnection(connection))
93          {
94              // execute the user's callback
95              final A result = callback.execute(wrappedConnection);
96              try
97              {
98                  // no exception indicates success
99                  connection.commit();
100             }
101             catch (final Throwable se)
102             {
103                 // failure to commit should be propagated, to halt any further processing
104                 throw new RdbmsException("Unable to commit connection", se);
105             }
106             return result;
107         }
108         catch (final Throwable re)
109         {
110             try
111             {
112                 // any exception indicates failure
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 }