ce9d4dcd17b8a623d5dc86d74278f33e7f398b69
[ccsdk/features.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk features
4  * ================================================================================
5  * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  */
22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb;
23
24 import java.sql.Connection;
25 import java.sql.DriverManager;
26 import java.sql.PreparedStatement;
27 import java.sql.ResultSet;
28 import java.sql.SQLException;
29 import java.sql.Statement;
30 import java.text.ParseException;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33 import org.onap.ccsdk.features.sdnr.wt.common.database.Portstatus;
34 import org.onap.ccsdk.features.sdnr.wt.common.database.data.AliasesEntry;
35 import org.onap.ccsdk.features.sdnr.wt.common.database.data.AliasesEntryList;
36 import org.onap.ccsdk.features.sdnr.wt.common.database.data.DatabaseVersion;
37 import org.onap.ccsdk.features.sdnr.wt.common.database.data.IndicesEntryList;
38 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.data.SqlDBIndicesEntry;
39 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.database.SqlDBMapper;
40 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.database.SqlDBMapper.UnableToMapClassException;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.Entity;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 public class SqlDBClient {
46
47     private static final Logger LOG = LoggerFactory.getLogger(SqlDBClient.class);
48
49     // matches:
50     //  1=>type, e.g. mariadb, mysql, ...
51     //  2=>host
52     //  3=>port
53     //  4=>dbname
54     private static final String DBURL_REGEX = "^jdbc:([^:]+):\\/\\/([^:]+):([0-9]+)\\/(.+)$";
55     private static final Pattern DBURL_PATTERN = Pattern.compile(DBURL_REGEX);
56     private static final String DBVERSION_REGEX = "^([\\d]+\\.[\\d]+\\.[\\d]+)";
57     private static final Pattern DBVERSION_PATTERN = Pattern.compile(DBVERSION_REGEX);
58     private static final String SELECT_VERSION_QUERY = "SELECT @@version as version";
59     private static final String LOG_PROBLEM_CLOSING_CONNECTION = "problem closing connection: ";
60
61     private static final String DBNAME_DEFAULT = "sdnrdb";
62     private final String dbConnectionString;
63     private final String dbName;
64     private final String dbHost;
65     private final int dbPort;
66
67     /**
68      *
69      * @param dbUrl e.g. jdbc:mysql://sdnrdb:3306/sdnrdb
70      * @param username
71      * @param password
72      */
73     public SqlDBClient(String dbUrl, String username, String password) throws IllegalArgumentException {
74         this.dbConnectionString = String.format("%s?user=%s&password=%s", dbUrl, username, password);
75         final Matcher matcher = DBURL_PATTERN.matcher(dbUrl);
76         if (!matcher.find()) {
77             throw new IllegalArgumentException("unable to parse databaseUrl " + dbUrl);
78         }
79         this.dbHost = matcher.group(2);
80         this.dbPort = Integer.parseInt(matcher.group(3));
81         this.dbName = matcher.group(4);
82     }
83
84     public AliasesEntryList readViews() {
85         return this.readViews(DBNAME_DEFAULT);
86     }
87
88     public AliasesEntryList readViews(String dbName) {
89         AliasesEntryList list = new AliasesEntryList();
90         final String query = "SELECT v.`TABLE_NAME` AS vn, t.`TABLE_NAME` AS tn\n"
91                 + "FROM `information_schema`.`TABLES` AS v\n"
92                 + "LEFT JOIN `information_schema`.`TABLES` AS t ON t.`TABLE_NAME` LIKE CONCAT(v.`TABLE_NAME`,'%')"
93                 + " AND t.`TABLE_TYPE`='BASE TABLE'\n" + "WHERE v.`TABLE_SCHEMA`='" + dbName
94                 + "' AND v.`TABLE_TYPE`='VIEW'";
95         ResultSet data = this.read(query);
96         try {
97             while (data.next()) {
98                 list.add(new AliasesEntry(data.getString(2), data.getString(1)));
99             }
100         } catch (SQLException e) {
101             LOG.warn("problem reading views: ", e);
102         }
103         return list;
104     }
105
106     public IndicesEntryList readTables() {
107         final String query = "SHOW FULL TABLES WHERE `Table_type` = 'BASE TABLE'";
108         IndicesEntryList list = new IndicesEntryList();
109         ResultSet data = this.read(query);
110         try {
111             while (data.next()) {
112                 list.add(new SqlDBIndicesEntry(data.getString(1)));
113             }
114         } catch (SQLException e) {
115             LOG.warn("problem reading tables: ", e);
116         }
117         return list;
118     }
119
120     public void waitForYellowStatus(long timeoutms) {
121         Portstatus.waitSecondsTillAvailable(timeoutms / 1000, this.dbHost, this.dbPort);
122     }
123
124     public DatabaseVersion readActualVersion() throws SQLException, ParseException {
125         ResultSet data;
126         try {
127             data = this.read(SELECT_VERSION_QUERY);
128             if (data.next()) {
129                 final String s = data.getString(1);
130                 final Matcher matcher = DBVERSION_PATTERN.matcher(s);
131                 data.afterLast();
132                 data.close();
133                 if (matcher.find()) {
134                     return new DatabaseVersion(matcher.group(1));
135                 } else {
136                     throw new ParseException(String.format("unable to extract version out of string '%s'", s), 0);
137                 }
138             }
139         } catch (SQLException e) {
140             LOG.warn("problem reading actual version: ", e);
141         }
142         throw new SQLException("unable to read version from database");
143     }
144
145     public boolean createTable(Entity entity, Class<?> clazz, String suffix) throws UnableToMapClassException {
146         String createStatement = SqlDBMapper.createTable(clazz, entity, suffix);
147         return this.createTable(createStatement);
148     }
149
150     public boolean createTable(String tableName, String tableMappings) {
151         final String createStatement = String.format("CREATE TABLE IF NOT EXISTS `%s` (%s)", tableName, tableMappings);
152         return this.createTable(createStatement);
153     }
154
155     public boolean createTable(String query) {
156         boolean result = false;
157         PreparedStatement stmt = null;
158         try {
159             Connection connection = this.getConnection();
160             stmt = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
161             stmt.execute();
162             connection.close();
163             result = true;
164         } catch (SQLException e) {
165             LOG.warn("problem creating table:", e);
166         } finally {
167             if (stmt != null) {
168                 try {
169                     stmt.close();
170                 } catch (SQLException e) {
171                     LOG.warn("problem closing stmt:", e);
172                 }
173             }
174         }
175         return result;
176     }
177
178     public boolean createView(String tableName, String viewName) throws SQLException {
179         try {
180             this.write(String.format("CREATE VIEW IF NOT EXISTS `%s` AS SELECT * FROM `%s`", viewName, tableName));
181             return true;
182         } catch (SQLException e) {
183             LOG.warn("problem deleting table:", e);
184         }
185         return false;
186     }
187
188     public boolean deleteView(String viewName) throws SQLException {
189         try {
190             this.write(String.format("DROP VIEW IF EXISTS `%s`", viewName));
191             return true;
192         } catch (SQLException e) {
193             LOG.warn("problem deleting view:", e);
194         }
195         return false;
196     }
197
198     public boolean update(String query) throws SQLException {
199         boolean result = false;
200         SQLException innerE = null;
201         Statement stmt = null;
202         try (Connection connection = this.getConnection()) {
203             stmt = connection.createStatement();
204             result = stmt.execute(query);
205             result = stmt.getUpdateCount() > 0 ? stmt.getUpdateCount() > 0 : result;
206         } catch (SQLException e) {
207             innerE = e;
208         } finally {
209             try {
210                 if (stmt != null) {
211                     stmt.close();
212                 }
213             } catch (SQLException e) {
214                 LOG.warn(LOG_PROBLEM_CLOSING_CONNECTION, e);
215             }
216         }
217         if (innerE != null) {
218             throw innerE;
219         }
220         return result;
221     }
222
223     public boolean write(String query) throws SQLException {
224         boolean result = false;
225         SQLException innerE = null;
226         PreparedStatement stmt = null;
227         try (Connection connection = this.getConnection()) {
228             stmt = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
229             result = stmt.execute();
230             result = stmt.getUpdateCount() > 0 ? stmt.getUpdateCount() > 0 : result;
231         } catch (SQLException e) {
232             innerE = e;
233         } finally {
234             try {
235                 if (stmt != null) {
236                     stmt.close();
237                 }
238             } catch (SQLException e) {
239                 LOG.warn(LOG_PROBLEM_CLOSING_CONNECTION, e);
240             }
241         }
242         if (innerE != null) {
243             throw innerE;
244         }
245         return result;
246     }
247
248     public String writeAndReturnId(String query) throws SQLException {
249         String result = null;
250         SQLException innerE = null;
251         PreparedStatement stmt = null;
252         try (Connection connection = this.getConnection()) {
253             stmt = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
254             stmt.execute();
255             ResultSet generatedKeys = stmt.getGeneratedKeys();
256             if (generatedKeys.next()) {
257                 result = String.valueOf(generatedKeys.getLong(1));
258             }
259         } catch (SQLException e) {
260             innerE = e;
261         } finally {
262             try {
263                 if (stmt != null) {
264                     stmt.close();
265                 }
266
267             } catch (SQLException e) {
268                 LOG.warn(LOG_PROBLEM_CLOSING_CONNECTION, e);
269             }
270         }
271         if (innerE != null) {
272             throw innerE;
273         }
274         return result;
275     }
276
277     public boolean deleteTable(String tableName) throws SQLException {
278         try {
279             this.write(String.format("DROP TABLE IF EXISTS `%s`", tableName));
280             return true;
281         } catch (SQLException e) {
282             LOG.warn("problem deleting table:", e);
283         }
284         return false;
285     }
286
287     public String getDatabaseName() {
288         return this.dbName;
289     }
290
291     public ResultSet read(String query) {
292         ResultSet data = null;
293         Statement stmt = null;
294         try (Connection connection = this.getConnection()) {
295             stmt = connection.createStatement();
296             data = stmt.executeQuery(query);
297         } catch (SQLException e) {
298             LOG.warn("problem reading db for query '{}': ", query, e);
299         } finally {
300             try {
301                 if (stmt != null) {
302                     stmt.close();
303                 }
304             } catch (SQLException e) {
305                 LOG.warn(LOG_PROBLEM_CLOSING_CONNECTION, e);
306             }
307         }
308         return data;
309     }
310
311     public Connection getConnection() throws SQLException {
312         return DriverManager.getConnection(this.dbConnectionString);
313     }
314
315     public boolean delete(String query) throws SQLException {
316         this.write(query);
317         return true;
318     }
319 }