2 * Copyright © 2016 Red Hat, Inc. and others.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.aaa.h2.persistence;
10 import java.sql.Connection;
11 import java.sql.DatabaseMetaData;
12 import java.sql.PreparedStatement;
13 import java.sql.ResultSet;
14 import java.sql.SQLException;
15 import java.sql.Statement;
16 import java.util.ArrayList;
17 import java.util.List;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
23 * Base class for H2 stores.
25 abstract class AbstractStore<T> {
29 private static final Logger LOG = LoggerFactory.getLogger(AbstractStore.class);
32 * The name of the table used to represent this store.
34 private final String tableName;
37 * Database connection, only used for tests.
39 Connection dbConnection = null;
42 * Table types we're interested in (when checking tables' existence).
44 public static final String[] TABLE_TYPES = new String[] { "TABLE" };
47 * Creates an instance.
49 * @param tableName The name of the table being managed.
51 protected AbstractStore(String tableName) {
52 this.tableName = tableName;
56 * Returns a database connection. It is the caller's responsibility to close it. If the managed table does not
57 * exist, it will be created (using {@link #getTableCreationStatement()}).
59 * @return A database connection.
61 * @throws StoreException if an error occurs.
63 protected Connection dbConnect() throws StoreException {
64 Connection conn = H2Store.getConnection(dbConnection);
66 // Ensure table check/creation is atomic
68 DatabaseMetaData dbm = conn.getMetaData();
69 try (ResultSet rs = dbm.getTables(null, null, tableName, TABLE_TYPES)) {
71 LOG.debug("Table {} already exists", tableName);
73 LOG.info("Table {} does not exist, creating it", tableName);
74 try (Statement stmt = conn.createStatement()) {
75 stmt.executeUpdate(getTableCreationStatement());
80 } catch (SQLException e) {
81 LOG.error("Error connecting to the H2 database", e);
82 throw new StoreException("Cannot connect to database server", e);
90 * @throws StoreException if a connection error occurs.
92 protected void dbClean() throws StoreException {
93 try (Connection c = dbConnect()) {
94 // The table name can't be a parameter in a prepared statement
95 String sql = "DELETE FROM " + tableName;
96 c.createStatement().execute(sql);
97 } catch (SQLException e) {
98 LOG.error("Error clearing table {}", tableName, e);
99 throw new StoreException("Error clearing table " + tableName, e);
104 * Returns the SQL code required to create the managed table.
106 * @return The SQL table creation statement.
108 protected abstract String getTableCreationStatement();
111 * Lists all the stored items.
113 * @return The stored item.
115 * @throws StoreException if an error occurs.
117 protected List<T> listAll() throws StoreException {
118 List<T> result = new ArrayList<>();
119 String query = "SELECT * FROM " + tableName;
120 try (Connection conn = dbConnect();
121 Statement stmt = conn.createStatement();
122 ResultSet rs = stmt.executeQuery(query)) {
124 result.add(fromResultSet(rs));
126 } catch (SQLException e) {
127 LOG.error("Error listing all items from {}", tableName, e);
128 throw new StoreException(e);
134 * Lists the stored items returned by the given statement.
136 * @param ps The statement (which must be ready for execution). It is the caller's reponsibility to close this.
138 * @return The stored items.
140 * @throws StoreException if an error occurs.
142 protected List<T> listFromStatement(PreparedStatement ps) throws StoreException {
143 List<T> result = new ArrayList<>();
144 try (ResultSet rs = ps.executeQuery()) {
146 result.add(fromResultSet(rs));
148 } catch (SQLException e) {
149 LOG.error("Error listing matching items from {}", tableName, e);
150 throw new StoreException(e);
156 * Extracts the first item returned by the given statement, if any.
158 * @param ps The statement (which must be ready for execution). It is the caller's reponsibility to close this.
160 * @return The first item, or {@code null} if none.
162 * @throws StoreException if an error occurs.
164 protected T firstFromStatement(PreparedStatement ps) throws StoreException {
165 try (ResultSet rs = ps.executeQuery()) {
167 return fromResultSet(rs);
171 } catch (SQLException e) {
172 LOG.error("Error listing first matching item from {}", tableName, e);
173 throw new StoreException(e);
178 * Converts a single row in a result set to an instance of the managed type.
180 * @param rs The result set (which is ready for extraction; {@link ResultSet#next()} must <b>not</b> be called).
182 * @return The corresponding instance.
184 * @throws SQLException if an error occurs.
186 protected abstract T fromResultSet(ResultSet rs) throws SQLException;