View Javadoc

1   /* $HeadURL::                                                                            $
2    * $Id$
3    *
4    * Copyright (c) 2009-2010 DuraSpace
5    * http://duraspace.org
6    *
7    * In collaboration with Topaz Inc.
8    * http://www.topazproject.org
9    *
10   * Licensed under the Apache License, Version 2.0 (the "License");
11   * you may not use this file except in compliance with the License.
12   * You may obtain a copy of the License at
13   *
14   *     http://www.apache.org/licenses/LICENSE-2.0
15   *
16   * Unless required by applicable law or agreed to in writing, software
17   * distributed under the License is distributed on an "AS IS" BASIS,
18   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   * See the License for the specific language governing permissions and
20   * limitations under the License.
21   */
22  
23  package org.akubraproject.txn;
24  
25  import java.io.IOException;
26  import java.net.URI;
27  import java.sql.Connection;
28  import java.sql.ResultSet;
29  import java.sql.SQLException;
30  import java.util.Map;
31  
32  import javax.sql.XAConnection;
33  import javax.transaction.Transaction;
34  
35  import com.google.common.collect.AbstractIterator;
36  import com.google.common.collect.PeekingIterator;
37  
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  import org.akubraproject.BlobStore;
42  import org.akubraproject.BlobStoreConnection;
43  
44  /**
45   * A basic superclass for sql-based transactional store connections. This just closes the sql
46   * connection at the end and provides an Iterator implementation for implementing
47   * {@link BlobStoreConnection#listBlobIds listBlobIds}.
48   *
49   * @author Ronald Tschalär
50   */
51  public abstract class SQLTransactionalConnection extends AbstractTransactionalConnection {
52    private static final Logger logger = LoggerFactory.getLogger(SQLTransactionalConnection.class);
53  
54    /** The xa connection being used */
55    protected final XAConnection xaCon;
56    /** The db connection being used */
57    protected final Connection   con;
58  
59    /**
60     * Create a new sql-based transactional connection.
61     *
62     * @param owner   the blob-store we belong to
63     * @param bStore  the underlying blob-store to use
64     * @param xaCon   the xa connection to use
65     * @param con     the db connection to use
66     * @param tx      the transaction we belong to
67     * @param hints   the hints to pass to <code>openConnection<code> on <var>bStore</var>
68     * @throws IOException if an error occurs initializing this connection
69     */
70    protected SQLTransactionalConnection(BlobStore owner, BlobStore bStore, XAConnection xaCon,
71                                         Connection con, Transaction tx, Map<String, String> hints)
72        throws IOException {
73      super(owner, bStore, tx, hints);
74      this.con   = con;
75      this.xaCon = xaCon;
76    }
77  
78    @Override
79    public void afterCompletion(int status) {
80      if (isCompleted)
81        return;
82  
83      try {
84        xaCon.close();
85      } catch (SQLException sqle) {
86        logger.error("Error closing db connection", sqle);
87      } finally {
88        super.afterCompletion(status);
89      }
90    }
91  
92    /**
93     * A ResultSet based Iterator of blob-id's. Useful for {@link BlobStoreConnection#listBlobIds
94     * listBlobIds}.
95     *
96     * @author Ronald Tschalär
97     */
98    protected static class RSBlobIdIterator extends AbstractIterator<URI>
99          implements PeekingIterator<URI> {
100     protected final ResultSet rs;
101     private   final boolean   closeStmt;
102 
103     /**
104      * Create a new iterator.
105      *
106      * @param rs        the underlying result-set to use; the result-set must either have at least
107      *                  one column such that <code>rs.getString(1)</code> returns a URI, or you
108      *                  must override {@link #getNextId}.
109      * @param closeStmt whether to close the associated statement when closing the result-set at
110      *                  the end of the iteration.
111      * @throws SQLException if thrown while attempting to advance to the first row
112      */
113     public RSBlobIdIterator(ResultSet rs, boolean closeStmt) throws SQLException {
114       this.rs        = rs;
115       this.closeStmt = closeStmt;
116     }
117 
118     @Override
119     protected URI computeNext() {
120       try {
121         URI id = getNextId();
122         if (id != null)
123           return id;
124 
125         close();
126         return endOfData();
127       } catch (SQLException sqle) {
128         throw new RuntimeException("error reading db results", sqle);
129       }
130     }
131 
132     /**
133      * Get the next id.
134      *
135      * @return the next id, or null if there are none left
136      */
137     protected URI getNextId() throws SQLException {
138       if (!rs.next())
139         return null;
140 
141       return URI.create(rs.getString(1));
142     }
143 
144     /**
145      * Close the underlying result-set and statement (if <var>closeStmt</var> is true).
146      */
147     protected void close() {
148       try {
149         if (closeStmt)
150           rs.getStatement().close();
151         else
152           rs.close();
153       } catch (SQLException sqle) {
154         logger.error("Error closing statement or result-set", sqle);
155       }
156     }
157   }
158 }