View Javadoc

1   package com.atlassian.sal.spring.connection;
2   
3   import com.atlassian.fugue.Option;
4   import com.atlassian.sal.api.rdbms.ConnectionCallback;
5   import com.atlassian.sal.spi.HostConnectionAccessor;
6   import org.slf4j.Logger;
7   import org.slf4j.LoggerFactory;
8   import org.springframework.transaction.PlatformTransactionManager;
9   import org.springframework.transaction.TransactionDefinition;
10  import org.springframework.transaction.TransactionStatus;
11  import org.springframework.transaction.support.DefaultTransactionDefinition;
12  import org.springframework.transaction.support.TransactionCallback;
13  import org.springframework.transaction.support.TransactionTemplate;
14  
15  import javax.annotation.Nonnull;
16  import java.sql.Connection;
17  import java.sql.SQLException;
18  
19  /**
20   * Default implementation for spring environments.
21   * <p/>
22   * Host should instantiate and export this.
23   * <p/>
24   * Host must implement ConnectionProvider.
25   *
26   * @since 3.0
27   */
28  public class SpringHostConnectionAccessor implements HostConnectionAccessor {
29      private static final Logger log = LoggerFactory.getLogger(SpringHostConnectionAccessor.class);
30  
31      private final ConnectionProvider connectionProvider;
32  
33      private final PlatformTransactionManager transactionManager;
34  
35      public SpringHostConnectionAccessor(@Nonnull final ConnectionProvider connectionProvider, @Nonnull final PlatformTransactionManager transactionManager) {
36          this.connectionProvider = connectionProvider;
37          this.transactionManager = transactionManager;
38      }
39  
40      @Override
41      public <A> A execute(final boolean readOnly, final boolean newTransaction, @Nonnull final ConnectionCallback<A> callback) {
42          // create a transaction with the properties requested
43          final DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
44          transactionDefinition.setName("SALSpringTx");
45          transactionDefinition.setReadOnly(readOnly);
46          transactionDefinition.setPropagationBehavior(newTransaction ? TransactionDefinition.PROPAGATION_REQUIRES_NEW : TransactionDefinition.PROPAGATION_REQUIRED);
47  
48          // transaction template
49          final TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager, transactionDefinition);
50  
51          // execute the callback within the transaction
52          //noinspection unchecked
53          return (A) transactionTemplate.execute(new TransactionCallback() {
54              @Override
55              public Object doInTransaction(final TransactionStatus status) {
56  
57                  // retrieve the connection
58                  final Connection connection = connectionProvider.getConnection();
59  
60                  // execute the user's callback
61                  return callback.execute(connection);
62              }
63          });
64      }
65  
66      @Nonnull
67      @Override
68      public Option<String> getSchemaName() {
69          return connectionProvider.getSchemaName();
70      }
71  
72      /**
73       * Host specific implementation.
74       */
75      public interface ConnectionProvider {
76          /**
77           * Supply a connection to be used within {@link org.springframework.transaction.support.TransactionTemplate#execute(org.springframework.transaction.support.TransactionCallback)}
78           * <p/>
79           * Note that this should be a "regular" connection, not a {@link com.atlassian.sal.core.rdbms.WrappedConnection}.
80           * <p/>
81           * Example implementations might be:
82           * <code>return dataSource.getConnection();</code>
83           * or
84           * <code>return sessionFactory.getSession().connection();</code>
85           */
86          @Nonnull
87          Connection getConnection();
88  
89          /**
90           * Returns the configured schema name (if any), for connections provided by {@link #getConnection()}.
91           * <p/>
92           * The host must ensure that the provided connection has <code>autoCommit=false</code>.
93           *
94           * @return schema name, if there is one
95           */
96          @Nonnull
97          Option<String> getSchemaName();
98      }
99  }