3e2436fa3764533da2772589c982390fadcdc40e
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / utils / DB.java
1 /*******************************************************************************\r
2  * ============LICENSE_START==================================================\r
3  * * org.onap.dmaap\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * ===========================================================================\r
7  * * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * * you may not use this file except in compliance with the License.\r
9  * * You may obtain a copy of the License at\r
10  * *\r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * *\r
13  *  * Unless required by applicable law or agreed to in writing, software\r
14  * * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * * See the License for the specific language governing permissions and\r
17  * * limitations under the License.\r
18  * * ============LICENSE_END====================================================\r
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 \r
24 \r
25 package org.onap.dmaap.datarouter.provisioning.utils;\r
26 \r
27 import java.io.File;\r
28 import java.io.FileInputStream;\r
29 import java.io.FileReader;\r
30 import java.io.IOException;\r
31 import java.io.LineNumberReader;\r
32 import java.sql.Connection;\r
33 import java.sql.DatabaseMetaData;\r
34 import java.sql.DriverManager;\r
35 import java.sql.ResultSet;\r
36 import java.sql.SQLException;\r
37 import java.sql.Statement;\r
38 import java.util.HashSet;\r
39 import java.util.LinkedList;\r
40 import java.util.NoSuchElementException;\r
41 import java.util.Properties;\r
42 import java.util.Queue;\r
43 import java.util.Set;\r
44 import org.apache.log4j.Logger;\r
45 \r
46 /**\r
47  * Load the DB JDBC driver, and manage a simple pool of connections to the DB.\r
48  *\r
49  * @author Robert Eby\r
50  * @version $Id$\r
51  */\r
52 public class DB {\r
53 \r
54     private static Logger intlogger = Logger\r
55         .getLogger("org.onap.dmaap.datarouter.provisioning.internal");\r
56 \r
57     private static String DB_URL;\r
58     private static String DB_LOGIN;\r
59     private static String DB_PASSWORD;\r
60     private static Properties props;\r
61     private static final Queue<Connection> queue = new LinkedList<>();\r
62 \r
63     private static String HTTPS_PORT;\r
64     private static String HTTP_PORT;\r
65 \r
66     /**\r
67      * Construct a DB object.  If this is the very first creation of this object, it will load a copy of the properties\r
68      * for the server, and attempt to load the JDBC driver for the database. If a fatal error occurs (e.g. either the\r
69      * properties file or the DB driver is missing), the JVM will exit.\r
70      */\r
71     public DB() {\r
72         if (props == null) {\r
73             props = new Properties();\r
74             try {\r
75                 props.load(new FileInputStream(System.getProperty(\r
76                     "org.onap.dmaap.datarouter.provserver.properties",\r
77                     "/opt/app/datartr/etc/provserver.properties")));\r
78                 String DB_DRIVER = (String) props.get("org.onap.dmaap.datarouter.db.driver");\r
79                 DB_URL = (String) props.get("org.onap.dmaap.datarouter.db.url");\r
80                 DB_LOGIN = (String) props.get("org.onap.dmaap.datarouter.db.login");\r
81                 DB_PASSWORD = (String) props.get("org.onap.dmaap.datarouter.db.password");\r
82                 HTTPS_PORT = (String) props.get("org.onap.dmaap.datarouter.provserver.https.port");\r
83                 HTTP_PORT = (String) props.get("org.onap.dmaap.datarouter.provserver.http.port");\r
84                 Class.forName(DB_DRIVER);\r
85             } catch (IOException e) {\r
86                 intlogger.fatal("PROV9003 Opening properties: " + e.getMessage());\r
87                 System.exit(1);\r
88             } catch (ClassNotFoundException e) {\r
89                 intlogger.fatal("PROV9004 cannot find the DB driver: " + e);\r
90                 System.exit(1);\r
91             }\r
92         }\r
93     }\r
94 \r
95     /**\r
96      * Get the provisioning server properties (loaded from provserver.properties).\r
97      *\r
98      * @return the Properties object\r
99      */\r
100     public Properties getProperties() {\r
101         return props;\r
102     }\r
103 \r
104     /**\r
105      * Get a JDBC connection to the DB from the pool.  Creates a new one if none are available.\r
106      *\r
107      * @return the Connection\r
108      */\r
109     @SuppressWarnings("resource")\r
110     public Connection getConnection() throws SQLException {\r
111         Connection connection = null;\r
112         while (connection == null) {\r
113             synchronized (queue) {\r
114                 try {\r
115                     connection = queue.remove();\r
116                 } catch (NoSuchElementException nseEx) {\r
117                     int n = 0;\r
118                     do {\r
119                         // Try up to 3 times to get a connection\r
120                         try {\r
121                             connection = DriverManager.getConnection(DB_URL, DB_LOGIN, DB_PASSWORD);\r
122                         } catch (SQLException sqlEx) {\r
123                             if (++n >= 3) {\r
124                                 throw sqlEx;\r
125                             }\r
126                         }\r
127                     } while (connection == null);\r
128                 }\r
129             }\r
130             if (connection != null && !connection.isValid(1)) {\r
131                 connection.close();\r
132                 connection = null;\r
133             }\r
134         }\r
135         return connection;\r
136     }\r
137 \r
138     /**\r
139      * Returns a JDBC connection to the pool.\r
140      *\r
141      * @param connection the Connection to return\r
142      */\r
143     public void release(Connection connection) {\r
144         if (connection != null) {\r
145             synchronized (queue) {\r
146                 if (!queue.contains(connection)) {\r
147                     queue.add(connection);\r
148                 }\r
149             }\r
150         }\r
151     }\r
152 \r
153     /**\r
154      * Run all necessary retrofits required to bring the database up to the level required for this version of the\r
155      * provisioning server.  This should be run before the server itself is started.\r
156      *\r
157      * @return true if all retrofits worked, false otherwise\r
158      */\r
159     public boolean runRetroFits() {\r
160         return retroFit1();\r
161     }\r
162 \r
163 \r
164     public static String getHttpsPort() {\r
165         return HTTPS_PORT;\r
166     }\r
167 \r
168     public static String getHttpPort() {\r
169         return HTTP_PORT;\r
170     }\r
171 \r
172     /**\r
173      * Retrofit 1 - Make sure the expected tables are in DB and are initialized. Uses sql_init_01.sql to setup the DB.\r
174      *\r
175      * @return true if the retrofit worked, false otherwise\r
176      */\r
177     private boolean retroFit1() {\r
178         final String[] expectedTables = {\r
179             "FEEDS", "FEED_ENDPOINT_ADDRS", "FEED_ENDPOINT_IDS", "PARAMETERS",\r
180             "SUBSCRIPTIONS", "LOG_RECORDS", "INGRESS_ROUTES", "EGRESS_ROUTES",\r
181             "NETWORK_ROUTES", "NODESETS", "NODES", "GROUPS"\r
182         };\r
183         Connection connection = null;\r
184         try {\r
185             connection = getConnection();\r
186             Set<String> actualTables = getTableSet(connection);\r
187             boolean initialize = false;\r
188             for (String table : expectedTables) {\r
189                 initialize |= !actualTables.contains(table.toLowerCase());\r
190             }\r
191             if (initialize) {\r
192                 intlogger.info("PROV9001: First time startup; The database is being initialized.");\r
193                 runInitScript(connection, 1);\r
194             }\r
195         } catch (SQLException e) {\r
196             intlogger\r
197                 .fatal("PROV9000: The database credentials are not working: " + e.getMessage());\r
198             return false;\r
199         } finally {\r
200             if (connection != null) {\r
201                 release(connection);\r
202             }\r
203         }\r
204         return true;\r
205     }\r
206 \r
207     /**\r
208      * Get a set of all table names in the DB.\r
209      *\r
210      * @param connection a DB connection\r
211      * @return the set of table names\r
212      */\r
213     private Set<String> getTableSet(Connection connection) {\r
214         Set<String> tables = new HashSet<String>();\r
215         try {\r
216             DatabaseMetaData md = connection.getMetaData();\r
217             ResultSet rs = md.getTables(null, null, "%", null);\r
218             if (rs != null) {\r
219                 while (rs.next()) {\r
220                     tables.add(rs.getString("TABLE_NAME"));\r
221                 }\r
222                 rs.close();\r
223             }\r
224         } catch (SQLException e) {\r
225             intlogger.fatal("PROV9010: Failed to get TABLE data from DB: " + e.getMessage());\r
226         }\r
227         return tables;\r
228     }\r
229 \r
230     /**\r
231      * Initialize the tables by running the initialization scripts located in the directory specified by the property\r
232      * <i>org.onap.dmaap.datarouter.provserver.dbscripts</i>.  Scripts have names of the form\r
233      * sql_init_NN.sql\r
234      *\r
235      * @param connection a DB connection\r
236      * @param scriptId the number of the sql_init_NN.sql script to run\r
237      */\r
238     private void runInitScript(Connection connection, int scriptId) {\r
239         String scriptDir = (String) props.get("org.onap.dmaap.datarouter.provserver.dbscripts");\r
240         StringBuilder strBuilder = new StringBuilder();\r
241         try {\r
242             String scriptFile = String.format("%s/sql_init_%02d.sql", scriptDir, scriptId);\r
243             if (!(new File(scriptFile)).exists()) {\r
244                 intlogger.fatal("PROV9005 Failed to load sql script from : " + scriptFile);\r
245                 System.exit(1);\r
246             }\r
247             LineNumberReader lineReader = new LineNumberReader(new FileReader(scriptFile));\r
248             String line;\r
249             while ((line = lineReader.readLine()) != null) {\r
250                 if (!line.startsWith("--")) {\r
251                     line = line.trim();\r
252                     strBuilder.append(line);\r
253                     if (line.endsWith(";")) {\r
254                         // Execute one DDL statement\r
255                         String sql = strBuilder.toString();\r
256                         strBuilder.setLength(0);\r
257                         Statement statement = connection.createStatement();\r
258                         statement.execute(sql);\r
259                         statement.close();\r
260                     }\r
261                 }\r
262             }\r
263             lineReader.close();\r
264             strBuilder.setLength(0);\r
265         } catch (Exception e) {\r
266             intlogger.fatal("PROV9002 Error when initializing table: " + e.getMessage());\r
267             System.exit(1);\r
268         }\r
269     }\r
270 }\r