2 * Copyright (c) 2014, 2017 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
3 * Copyright (c) 2022 PANTHEON.tech, s.r.o.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.aaa.datastore.h2;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.annotations.VisibleForTesting;
14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.sql.ResultSet;
16 import java.sql.SQLException;
17 import java.sql.Statement;
18 import org.apache.commons.text.StringEscapeUtils;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.aaa.api.model.Domain;
21 import org.opendaylight.aaa.api.model.Domains;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
28 final class DomainStore extends AbstractStore<Domain> {
29 private static final Logger LOG = LoggerFactory.getLogger(DomainStore.class);
32 * Name of our SQL table. This constant lives here rather than in {@link SQLTable} for brevity.
34 static final @NonNull String TABLE = "AAA_DOMAINS";
37 SQLTable.DOMAIN.verifyTable(TABLE);
41 * Column storing {@link Domain#getDomainid()}, which is a flat namespace.
43 // FIXME: rename to "id"
45 static final String COL_ID = "domainid";
47 * Column storing {@link Domain#getName()}, which is a short name.
50 static final String COL_NAME = "name";
52 * Column storing {@link Domain#getDescription()}, which is a detailed description.
55 static final String COL_DESC = "description";
57 * Column storing {@link Domain#isEnabled()}, which is ... not used anywhere.
59 // FIXME: remove or audit for potential callers of isEnabled()
61 static final String COL_ENABLED = "enabled";
63 DomainStore(final ConnectionProvider dbConnectionFactory) {
64 super(dbConnectionFactory, TABLE);
68 void createTable(final Statement stmt) throws SQLException {
69 stmt.executeUpdate("CREATE TABLE " + TABLE + " ("
70 // FIXME: on delete cascade? RoleStore.COL_DOMAIN_ID seems to reference this
71 + COL_ID + " VARCHAR(128) PRIMARY KEY, "
72 + COL_NAME + " VARCHAR(128) UNIQUE NOT NULL, "
73 + COL_DESC + " VARCHAR(128), "
74 + COL_ENABLED + " BOOLEAN NOT NULL)");
78 void cleanTable(final Statement stmt) throws SQLException {
79 stmt.execute("DELETE FROM " + TABLE);
83 protected Domain fromResultSet(final ResultSet rs) throws SQLException {
84 Domain domain = new Domain();
85 domain.setDomainid(rs.getString(COL_ID));
86 domain.setName(rs.getString(COL_NAME));
87 domain.setDescription(rs.getString(COL_DESC));
88 domain.setEnabled(rs.getBoolean(COL_ENABLED));
92 Domains getDomains() throws StoreException {
93 Domains domains = new Domains();
94 domains.setDomains(listAll());
98 // FIXME: seems to be unused
99 Domains getDomains(final String domainName) throws StoreException {
100 final Domains domains;
101 try (var conn = dbConnect();
102 var stmt = conn.prepareStatement("SELECT * FROM " + TABLE + " WHERE " + COL_NAME + " = ?")) {
103 stmt.setString(1, domainName);
105 domains = new Domains();
106 LOG.debug("getDomains() request: {}", stmt);
107 domains.setDomains(listFromStatement(stmt));
108 } catch (SQLException e) {
109 LOG.error("Error listing domains matching {}", domainName, e);
110 throw new StoreException("Error listing domains", e);
115 Domain getDomain(final String id) throws StoreException {
116 try (var conn = dbConnect();
117 var stmt = conn.prepareStatement("SELECT * FROM " + TABLE + " WHERE " + COL_ID + " = ?")) {
118 stmt.setString(1, id);
120 LOG.debug("getDomain() request: {}", stmt);
121 return firstFromStatement(stmt);
122 } catch (SQLException e) {
123 LOG.error("Error retrieving domain {}", id, e);
124 throw new StoreException("Error loading domain", e);
128 Domain createDomain(final Domain domain) throws StoreException {
129 requireNonNull(domain);
130 requireNonNull(domain.getName());
131 requireNonNull(domain.isEnabled());
132 try (var conn = dbConnect();
133 var stmt = conn.prepareStatement("INSERT INTO " + TABLE + " ("
134 + COL_ID + ", " + COL_NAME + ", " + COL_DESC + ", " + COL_ENABLED + ") VALUES (?, ?, ?, ?)")) {
135 stmt.setString(1, domain.getName());
136 stmt.setString(2, domain.getName());
137 stmt.setString(3, domain.getDescription());
138 stmt.setBoolean(4, domain.isEnabled());
140 LOG.debug("createDomain() request: {}", stmt);
141 if (stmt.executeUpdate() == 0) {
142 throw new StoreException("Creating domain failed, no rows affected.");
144 domain.setDomainid(domain.getName());
146 } catch (SQLException e) {
147 LOG.error("Error creating domain {}", domain.getName(), e);
148 throw new StoreException("Error creating domain", e);
152 Domain putDomain(final Domain domain) throws StoreException {
153 final var savedDomain = getDomain(domain.getDomainid());
154 if (savedDomain == null) {
157 if (domain.getDescription() != null) {
158 savedDomain.setDescription(domain.getDescription());
160 if (domain.getName() != null) {
161 savedDomain.setName(domain.getName());
163 if (domain.isEnabled() != null) {
164 savedDomain.setEnabled(domain.isEnabled());
167 try (var conn = dbConnect();
168 var stmt = conn.prepareStatement("UPDATE " + TABLE + " SET "
169 + COL_NAME + " = ?, " + COL_DESC + " = ?, " + COL_ENABLED + " = ? WHERE " + COL_ID + " = ?")) {
170 stmt.setString(1, savedDomain.getName());
171 stmt.setString(2, savedDomain.getDescription());
172 stmt.setBoolean(3, savedDomain.isEnabled());
173 stmt.setString(4, savedDomain.getDomainid());
175 LOG.debug("putDomain() request: {}", stmt);
176 stmt.executeUpdate();
177 } catch (SQLException e) {
178 LOG.error("Error updating domain {}", domain.getDomainid(), e);
179 throw new StoreException("Error updating domain", e);
185 @SuppressFBWarnings(value = "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE", justification = "Weird original code")
186 Domain deleteDomain(final String domainid) throws StoreException {
187 // FIXME: remove this once we have a more modern H2
188 final String escaped = StringEscapeUtils.escapeHtml4(domainid);
189 final var deletedDomain = getDomain(escaped);
190 if (deletedDomain == null) {
194 try (var conn = dbConnect();
195 var stmt = conn.createStatement()) {
196 // FIXME: prepare statement instead
197 final String query = String.format("DELETE FROM " + TABLE + " WHERE " + COL_ID + " = '%s'", escaped);
198 LOG.debug("deleteDomain() request: {}", query);
200 int deleteCount = stmt.executeUpdate(query);
201 LOG.debug("deleted {} records", deleteCount);
202 return deletedDomain;
203 } catch (SQLException e) {
204 LOG.error("Error deleting domain {}", domainid, e);
205 throw new StoreException("Error deleting domain", e);