Merge "Unit test base"
[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 org.apache.log4j.Logger;\r
28 \r
29 import java.io.*;\r
30 import java.sql.*;\r
31 import java.util.*;\r
32 \r
33 /**\r
34  * Load the DB JDBC driver, and manage a simple pool of connections to the DB.\r
35  *\r
36  * @author Robert Eby\r
37  * @version $Id$\r
38  */\r
39 public class DB {\r
40     /**\r
41      * The name of the properties file (in CLASSPATH)\r
42      */\r
43     private static final String CONFIG_FILE = "provserver.properties";\r
44 \r
45     private static String DB_URL;\r
46     private static String DB_LOGIN;\r
47     private static String DB_PASSWORD;\r
48     private static Properties props;\r
49     private static Logger intlogger = Logger.getLogger("org.onap.dmaap.datarouter.provisioning.internal");\r
50     private static final Queue<Connection> queue = new LinkedList<>();\r
51 \r
52     public static String HTTPS_PORT;\r
53     public static String HTTP_PORT;\r
54 \r
55     /**\r
56      * Construct a DB object.  If this is the very first creation of this object, it will load a copy\r
57      * of the properties for the server, and attempt to load the JDBC driver for the database.  If a fatal\r
58      * error occurs (e.g. either the properties file or the DB driver is missing), the JVM will exit.\r
59      */\r
60     public DB() {\r
61         if (props == null) {\r
62             props = new Properties();\r
63             try (InputStream inStream = getClass().getClassLoader().getResourceAsStream(CONFIG_FILE)) {\r
64                 props.load(inStream);\r
65                 String DB_DRIVER = (String) props.get("org.onap.dmaap.datarouter.db.driver");\r
66                 DB_URL = (String) props.get("org.onap.dmaap.datarouter.db.url");\r
67                 DB_LOGIN = (String) props.get("org.onap.dmaap.datarouter.db.login");\r
68                 DB_PASSWORD = (String) props.get("org.onap.dmaap.datarouter.db.password");\r
69                 HTTPS_PORT = (String) props.get("org.onap.dmaap.datarouter.provserver.https.port");\r
70                 HTTP_PORT = (String) props.get("org.onap.dmaap.datarouter.provserver.http.port");\r
71                 Class.forName(DB_DRIVER);\r
72             } catch (IOException e) {\r
73                 intlogger.fatal("PROV9003 Opening properties: " + e.getMessage());\r
74                 e.printStackTrace();\r
75                 System.exit(1);\r
76             } catch (ClassNotFoundException e) {\r
77                 intlogger.fatal("PROV9004 cannot find the DB driver: " + e);\r
78                 e.printStackTrace();\r
79                 System.exit(1);\r
80             }\r
81         }\r
82     }\r
83 \r
84     /**\r
85      * Get the provisioning server properties (loaded from provserver.properties).\r
86      *\r
87      * @return the Properties object\r
88      */\r
89     public Properties getProperties() {\r
90         return props;\r
91     }\r
92 \r
93     /**\r
94      * Get a JDBC connection to the DB from the pool.  Creates a new one if none are available.\r
95      *\r
96      * @return the Connection\r
97      * @throws SQLException\r
98      */\r
99     @SuppressWarnings("resource")\r
100     public Connection getConnection() throws SQLException {\r
101         Connection connection = null;\r
102         while (connection == null) {\r
103             synchronized (queue) {\r
104                 try {\r
105                     connection = queue.remove();\r
106                 } catch (NoSuchElementException nseEx) {\r
107                     int n = 0;\r
108                     do {\r
109                         // Try up to 3 times to get a connection\r
110                         try {\r
111                             connection = DriverManager.getConnection(DB_URL, DB_LOGIN, DB_PASSWORD);\r
112                         } catch (SQLException sqlEx) {\r
113                             if (++n >= 3)\r
114                                 throw sqlEx;\r
115                         }\r
116                     } while (connection == null);\r
117                 }\r
118             }\r
119             if (connection != null && !connection.isValid(1)) {\r
120                 connection.close();\r
121                 connection = null;\r
122             }\r
123         }\r
124         return connection;\r
125     }\r
126 \r
127     /**\r
128      * Returns a JDBC connection to the pool.\r
129      *\r
130      * @param connection the Connection to return\r
131      */\r
132     public void release(Connection connection) {\r
133         if (connection != null) {\r
134             synchronized (queue) {\r
135                 if (!queue.contains(connection))\r
136                     queue.add(connection);\r
137             }\r
138         }\r
139     }\r
140 \r
141     /**\r
142      * Run all necessary retrofits required to bring the database up to the level required for this version\r
143      * of the provisioning server.  This should be run before the server itself is started.\r
144      *\r
145      * @return true if all retrofits worked, false otherwise\r
146      */\r
147     public boolean runRetroFits() {\r
148         return retroFit1();\r
149     }\r
150 \r
151     /**\r
152      * Retrofit 1 - Make sure the expected tables are in DB and are initialized.\r
153      * Uses sql_init_01.sql to setup the DB.\r
154      *\r
155      * @return true if the retrofit worked, false otherwise\r
156      */\r
157     private boolean retroFit1() {\r
158         final String[] expectedTables = {\r
159                 "FEEDS", "FEED_ENDPOINT_ADDRS", "FEED_ENDPOINT_IDS", "PARAMETERS",\r
160                 "SUBSCRIPTIONS", "LOG_RECORDS", "INGRESS_ROUTES", "EGRESS_ROUTES",\r
161                 "NETWORK_ROUTES", "NODESETS", "NODES", "GROUPS"\r
162         };\r
163         Connection connection = null;\r
164         try {\r
165             connection = getConnection();\r
166             Set<String> actualTables = getTableSet(connection);\r
167             boolean initialize = false;\r
168             for (String table : expectedTables) {\r
169                 initialize |= !actualTables.contains(table);\r
170             }\r
171             if (initialize) {\r
172                 intlogger.info("PROV9001: First time startup; The database is being initialized.");\r
173                 runInitScript(connection, 1);\r
174             }\r
175         } catch (SQLException e) {\r
176             intlogger.fatal("PROV9000: The database credentials are not working: " + e.getMessage());\r
177             return false;\r
178         } finally {\r
179             if (connection != null)\r
180                 release(connection);\r
181         }\r
182         return true;\r
183     }\r
184 \r
185     /**\r
186      * Get a set of all table names in the DB.\r
187      *\r
188      * @param connection a DB connection\r
189      * @return the set of table names\r
190      */\r
191     private Set<String> getTableSet(Connection connection) {\r
192         Set<String> tables = new HashSet<String>();\r
193         try {\r
194             DatabaseMetaData md = connection.getMetaData();\r
195             ResultSet rs = md.getTables("datarouter", "", "", null);\r
196             if (rs != null) {\r
197                 while (rs.next()) {\r
198                     tables.add(rs.getString("TABLE_NAME"));\r
199                 }\r
200                 rs.close();\r
201             }\r
202         } catch (SQLException e) {\r
203         }\r
204         return tables;\r
205     }\r
206 \r
207     /**\r
208      * Initialize the tables by running the initialization scripts located in the directory specified\r
209      * by the property <i>org.onap.dmaap.datarouter.provserver.dbscripts</i>.  Scripts have names of\r
210      * the form sql_init_NN.sql\r
211      *\r
212      * @param connection a DB connection\r
213      * @param scriptId   the number of the sql_init_NN.sql script to run\r
214      */\r
215     private void runInitScript(Connection connection, int scriptId) {\r
216         String scriptDir = (String) props.get("org.onap.dmaap.datarouter.provserver.dbscripts");\r
217         StringBuilder sb = new StringBuilder();\r
218         try {\r
219             String scriptFile = String.format("%s/sql_init_%02d.sql", scriptDir, scriptId);\r
220             if (!(new File(scriptFile)).exists())\r
221                 return;\r
222 \r
223             LineNumberReader in = new LineNumberReader(new FileReader(scriptFile));\r
224             String line;\r
225             while ((line = in.readLine()) != null) {\r
226                 if (!line.startsWith("--")) {\r
227                     line = line.trim();\r
228                     sb.append(line);\r
229                     if (line.endsWith(";")) {\r
230                         // Execute one DDL statement\r
231                         String sql = sb.toString();\r
232                         sb.setLength(0);\r
233                         Statement s = connection.createStatement();\r
234                         s.execute(sql);\r
235                         s.close();\r
236                     }\r
237                 }\r
238             }\r
239             in.close();\r
240             sb.setLength(0);\r
241         } catch (Exception e) {\r
242             intlogger.fatal("PROV9002 Error when initializing table: " + e.getMessage());\r
243             System.exit(1);\r
244         }\r
245     }\r
246 }\r