summaryrefslogtreecommitdiffstats
path: root/core/dblib/provider/src
diff options
context:
space:
mode:
Diffstat (limited to 'core/dblib/provider/src')
-rwxr-xr-xcore/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/CachedDataSource.java593
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/CachedDataSourceFactory.java46
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBConfigException.java47
-rwxr-xr-xcore/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLIBResourceProvider.java165
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLibConnection.java387
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLibException.java39
-rwxr-xr-xcore/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBResourceManager.java983
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBResourceObserver.java27
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DataAccessor.java34
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DataSourceComparator.java33
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DbLibService.java50
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DblibConfigurationException.java45
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/NoAvailableConnectionsException.java36
-rwxr-xr-xcore/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/TerminatingCachedDataSource.java59
-rwxr-xr-xcore/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/BaseDBConfiguration.java258
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/DbConfigPool.java63
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/JDBCConfiguration.java31
-rwxr-xr-xcore/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/TerminatingConfiguration.java33
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/factory/DBConfigFactory.java105
-rwxr-xr-xcore/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/jdbc/JdbcDBCachedDataSource.java201
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/PollingWorker.java259
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/SQLExecutionMonitor.java222
-rw-r--r--core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/SQLExecutionMonitorObserver.java37
-rwxr-xr-xcore/dblib/provider/src/main/resources/OSGI-INF/blueprint/dblib-blueprint.xml18
-rwxr-xr-xcore/dblib/provider/src/main/resources/dblib.properties14
-rwxr-xr-xcore/dblib/provider/src/main/resources/org/opendaylight/blueprint/dblib-blueprint.xml24
-rw-r--r--core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/CachedDataSourceTest.java121
-rw-r--r--core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/DBConfigExceptionTest.java19
-rw-r--r--core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/DblibConfigurationExceptionTest.java24
-rw-r--r--core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/NoAvailableConnectionsExceptionTest.java16
-rw-r--r--core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TerminatingCachedDataSourceTest.java94
-rw-r--r--core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TestDBResourceManager.java70
-rw-r--r--core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TestDBResourceManager2.java95
-rw-r--r--core/dblib/provider/src/test/resources/dblib.properties38
34 files changed, 4286 insertions, 0 deletions
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/CachedDataSource.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/CachedDataSource.java
new file mode 100755
index 000000000..b9a0f071b
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/CachedDataSource.java
@@ -0,0 +1,593 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Observer;
+import javax.sql.DataSource;
+import javax.sql.rowset.CachedRowSet;
+import javax.sql.rowset.RowSetProvider;
+import org.apache.tomcat.jdbc.pool.PoolExhaustedException;
+import org.onap.ccsdk.sli.core.dblib.config.BaseDBConfiguration;
+import org.onap.ccsdk.sli.core.dblib.pm.SQLExecutionMonitor;
+import org.onap.ccsdk.sli.core.dblib.pm.SQLExecutionMonitor.TestObject;
+import org.onap.ccsdk.sli.core.dblib.pm.SQLExecutionMonitorObserver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @version $Revision: 1.14 $
+ * Change Log
+ * Author Date Comments
+ * ============== ======== ====================================================
+ * Rich Tabedzki
+ */
+
+public abstract class CachedDataSource implements DataSource, SQLExecutionMonitorObserver {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CachedDataSource.class);
+
+ private static final String SQL_FAILURE = "SQL FAILURE. time(ms): ";
+ private static final String FAILED_TO_EXECUTE = "> Failed to execute: ";
+ private static final String WITH_ARGUMENTS = " with arguments: ";
+ private static final String WITH_NO_ARGUMENTS = " with no arguments. ";
+ private static final String DATA_SOURCE_CONNECT_SUCCESS = "SQL DataSource < {} > connected to {}, read-only is {}, tested successfully";
+ private static final String DATA_SOURCE_CONNECT_FAILURE = "SQL DataSource < {} > test failed. Cause : {}> test failed. Cause : {}";
+
+ protected long connReqTimeout = 30L;
+ protected long dataReqTimeout = 100L;
+
+ private final SQLExecutionMonitor monitor;
+ protected final DataSource ds;
+ protected String connectionName = null;
+ protected boolean initialized = false;
+
+ private long interval = 1000;
+ private long initialDelay = 5000;
+ private long expectedCompletionTime = 50L;
+ private boolean canTakeOffLine = true;
+ private long unprocessedFailoverThreshold = 3L;
+
+ private long nextErrorReportTime = 0L;
+
+ private String globalHostName = null;
+ private final int index;
+
+ private boolean isDerby = false;
+
+ public CachedDataSource(BaseDBConfiguration jdbcElem) throws DBConfigException {
+ ds = configure(jdbcElem);
+ index = initializeIndex(jdbcElem);
+ if ("org.apache.derby.jdbc.EmbeddedDriver".equals(jdbcElem.getDriverName())) {
+ isDerby = true;
+ }
+ monitor = new SQLExecutionMonitor(this);
+ }
+
+ protected abstract DataSource configure(BaseDBConfiguration jdbcElem) throws DBConfigException;
+ protected abstract int getAvailableConnections();
+
+ protected int initializeIndex(BaseDBConfiguration jdbcElem) {
+ if(jdbcElem.containsKey(BaseDBConfiguration.DATABASE_HOSTS)) {
+ String hosts = jdbcElem.getProperty(BaseDBConfiguration.DATABASE_HOSTS);
+ String name = jdbcElem.getProperty(BaseDBConfiguration.CONNECTION_NAME);
+ List<String> numbers = Arrays.asList(hosts.split(","));
+ return numbers.indexOf(name);
+ } else
+ return -1;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.sql.DataSource#getConnection()
+ */
+ @Override
+ public Connection getConnection() throws SQLException {
+ LapsedTimer lt = new LapsedTimer();
+ try {
+ return ds.getConnection();
+ } finally {
+ if(LOGGER.isTraceEnabled()) {
+ LOGGER.trace(String.format("SQL Connection aquisition time : %s", lt.lapsedTime()));
+ }
+ }
+ }
+
+ public CachedRowSet getData(String statement, List<Object> arguments) throws SQLException {
+ TestObject testObject = monitor.registerRequest();
+
+ try (Connection connection = this.getConnection()) {
+ if (connection == null) {
+ throw new SQLException("Connection invalid");
+ }
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Obtained connection <{}>: {}", connectionName, connection);
+ }
+ return executePreparedStatement(connection, statement, arguments, true);
+ } finally {
+ monitor.deregisterRequest(testObject);
+ }
+ }
+
+ public boolean writeData(String statement, List<Object> arguments) throws SQLException {
+ TestObject testObject = monitor.registerRequest();
+
+ try (Connection connection = this.getConnection()) {
+ if (connection == null) {
+ throw new SQLException("Connection invalid");
+ }
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Obtained connection <{}>: {}", connectionName, connection);
+ }
+ return executeUpdatePreparedStatement(connection, statement, arguments, true);
+ } finally {
+ monitor.deregisterRequest(testObject);
+ }
+ }
+
+ CachedRowSet executePreparedStatement(Connection conn, String statement, List<Object> arguments, boolean close)
+ throws SQLException {
+ long time = System.currentTimeMillis();
+
+ CachedRowSet data = null;
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("SQL Statement: {}", statement);
+ if (arguments != null && !arguments.isEmpty()) {
+ LOGGER.debug("Argunments: {}", arguments);
+ }
+ }
+
+ ResultSet rs = null;
+ try (PreparedStatement ps = conn.prepareStatement(statement)) {
+ data = RowSetProvider.newFactory().createCachedRowSet();
+ if (arguments != null) {
+ for (int i = 0, max = arguments.size(); i < max; i++) {
+ ps.setObject(i + 1, arguments.get(i));
+ }
+ }
+ rs = ps.executeQuery();
+ data.populate(rs);
+ // Point the rowset Cursor to the start
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("SQL SUCCESS. rows returned: {}, time(ms): {}", data.size(),
+ (System.currentTimeMillis() - time));
+ }
+ } catch (SQLException exc) {
+ handleSqlExceptionForExecuteStatement(conn, statement, arguments, exc, time);
+ } finally {
+ handleFinallyBlockForExecutePreparedStatement(rs, conn, close);
+ }
+
+ return data;
+ }
+
+ private void handleSqlExceptionForExecuteStatement(Connection conn, String statement, List<Object> arguments,
+ SQLException exc, long time) throws SQLException {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug(SQL_FAILURE + (System.currentTimeMillis() - time));
+ }
+ try {
+ conn.rollback();
+ } catch (Exception thr) {
+ LOGGER.error(thr.getLocalizedMessage(), thr);
+ }
+ if (arguments != null && !arguments.isEmpty()) {
+ LOGGER.error(String.format("<%s%s%s%s%s", connectionName, FAILED_TO_EXECUTE, statement, WITH_ARGUMENTS,
+ arguments), exc);
+ } else {
+ LOGGER.error(String.format("<%s%s%s%s", connectionName, FAILED_TO_EXECUTE, statement, WITH_NO_ARGUMENTS),
+ exc);
+ }
+ throw exc;
+ }
+
+ private void handleFinallyBlockForExecutePreparedStatement(ResultSet rs, Connection conn, boolean close) {
+ try {
+ if (rs != null) {
+ rs.close();
+ }
+ } catch (Exception exc) {
+ LOGGER.error(exc.getLocalizedMessage(), exc);
+ }
+ try {
+ if (conn != null && close) {
+ conn.close();
+ }
+ } catch (Exception exc) {
+ LOGGER.error(exc.getLocalizedMessage(), exc);
+ }
+ }
+
+ boolean executeUpdatePreparedStatement(Connection conn, String statement, List<Object> arguments, boolean close)
+ throws SQLException {
+ long time = System.currentTimeMillis();
+
+ try (PreparedStatement ps = conn.prepareStatement(statement);
+ CachedRowSet data = RowSetProvider.newFactory().createCachedRowSet()) {
+ if (arguments != null) {
+ prepareStatementForExecuteUpdate(arguments, ps);
+ }
+ ps.executeUpdate();
+ // Point the rowset Cursor to the start
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("SQL SUCCESS. rows returned: {}, time(ms): {}", data.size(),
+ (System.currentTimeMillis() - time));
+ }
+ } catch (SQLException exc) {
+ handleSqlExceptionForExecuteStatement(conn, statement, arguments, exc, time);
+ } finally {
+ try {
+ if (close) {
+ conn.close();
+ }
+ } catch (Exception exc) {
+ LOGGER.error(exc.getLocalizedMessage(), exc);
+ }
+ }
+
+ return true;
+ }
+
+ private void prepareStatementForExecuteUpdate(List<Object> arguments, PreparedStatement ps) throws SQLException {
+ for (int i = 0, max = arguments.size(); i < max; i++) {
+ Object value = arguments.get(i);
+ if (value instanceof Blob) {
+ ps.setBlob(i + 1, (Blob) value);
+ } else if (value instanceof Timestamp) {
+ ps.setTimestamp(i + 1, (Timestamp) value);
+ } else if (value instanceof Integer) {
+ ps.setInt(i + 1, (Integer) value);
+ } else if (value instanceof Long) {
+ ps.setLong(i + 1, (Long) value);
+ } else if (value instanceof Date) {
+ ps.setDate(i + 1, (Date) value);
+ } else {
+ ps.setObject(i + 1, value);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
+ */
+ @Override
+ public Connection getConnection(String username, String password) throws SQLException {
+ return ds.getConnection(username, password);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.sql.DataSource#getLogWriter()
+ */
+ @Override
+ public PrintWriter getLogWriter() throws SQLException {
+ return ds.getLogWriter();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.sql.DataSource#getLoginTimeout()
+ */
+ @Override
+ public int getLoginTimeout() throws SQLException {
+ return ds.getLoginTimeout();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.sql.DataSource#setLogWriter(java.io.PrintWriter)
+ */
+ @Override
+ public void setLogWriter(PrintWriter out) throws SQLException {
+ ds.setLogWriter(out);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.sql.DataSource#setLoginTimeout(int)
+ */
+ @Override
+ public void setLoginTimeout(int seconds) throws SQLException {
+ ds.setLoginTimeout(seconds);
+ }
+
+ @Override
+ public final String getDbConnectionName() {
+ return connectionName;
+ }
+
+ protected final void setDbConnectionName(String name) {
+ this.connectionName = name;
+ }
+
+ public void cleanUp() {
+ if (ds != null && ds instanceof Closeable) {
+ try {
+ ((Closeable) ds).close();
+ } catch (IOException e) {
+ LOGGER.warn(e.getMessage());
+ }
+ }
+ monitor.deleteObservers();
+ monitor.cleanup();
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+
+ protected boolean testConnection() {
+ return testConnection(false);
+ }
+
+ protected boolean testConnection(boolean errorLevel) {
+
+ String testQuery = "SELECT @@global.read_only, @@global.hostname";
+ if (isDerby) {
+ testQuery = "SELECT 'false', 'localhost' FROM SYSIBM.SYSDUMMY1";
+ }
+ ResultSet rs = null;
+ try (Connection conn = this.getConnection(); Statement stmt = conn.createStatement()) {
+ Boolean readOnly;
+ String hostname;
+ rs = stmt.executeQuery(testQuery); // ("SELECT 1 FROM DUAL"); //"select
+ // BANNER from SYS.V_$VERSION"
+ while (rs.next()) {
+ readOnly = rs.getBoolean(1);
+ hostname = rs.getString(2);
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug(DATA_SOURCE_CONNECT_SUCCESS,getDbConnectionName(),hostname,readOnly);
+ }
+ }
+ } catch (Exception exc) {
+ if (errorLevel) {
+ LOGGER.error(DATA_SOURCE_CONNECT_FAILURE, this.getDbConnectionName(),exc.getMessage());
+ } else {
+ LOGGER.info(DATA_SOURCE_CONNECT_FAILURE, this.getDbConnectionName(),exc.getMessage());
+ }
+ return false;
+ } finally {
+ if (rs != null) {
+ try {
+ rs.close();
+ } catch (SQLException e) {
+ LOGGER.error(e.getLocalizedMessage(), e);
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException {
+ return null;
+ }
+
+ public void addObserver(Observer observer) {
+ monitor.addObserver(observer);
+ }
+
+ public void deleteObserver(Observer observer) {
+ monitor.deleteObserver(observer);
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ @Override
+ public long getInterval() {
+ return interval;
+ }
+
+ @Override
+ public long getInitialDelay() {
+ return initialDelay;
+ }
+
+ @Override
+ public void setInterval(long value) {
+ interval = value;
+ }
+
+ @Override
+ public void setInitialDelay(long value) {
+ initialDelay = value;
+ }
+
+ @Override
+ public long getExpectedCompletionTime() {
+ return expectedCompletionTime;
+ }
+
+ @Override
+ public void setExpectedCompletionTime(long value) {
+ expectedCompletionTime = value;
+ }
+
+ @Override
+ public long getUnprocessedFailoverThreshold() {
+ return unprocessedFailoverThreshold;
+ }
+
+ @Override
+ public void setUnprocessedFailoverThreshold(long value) {
+ this.unprocessedFailoverThreshold = value;
+ }
+
+ public boolean canTakeOffLine() {
+ return canTakeOffLine;
+ }
+
+ public void blockImmediateOffLine() {
+ canTakeOffLine = false;
+ final Thread offLineTimer = new Thread(() -> {
+ try {
+ Thread.sleep(30000L);
+ } catch (Exception exc) {
+ LOGGER.error(exc.getLocalizedMessage(), exc);
+ } finally {
+ canTakeOffLine = true;
+ }
+ });
+ offLineTimer.setDaemon(true);
+ offLineTimer.start();
+ }
+
+ /**
+ * @return the monitor
+ */
+ final SQLExecutionMonitor getMonitor() {
+ return monitor;
+ }
+
+ protected boolean isSlave() throws PoolExhaustedException {
+
+ // If using Apache derby, just return false
+ if (isDerby) {
+ return false;
+ }
+ CachedRowSet rs;
+ boolean isSlave;
+ String hostname = "UNDETERMINED";
+ try {
+ boolean localSlave = true;
+ rs = this.getData("SELECT @@global.read_only, @@global.hostname", new ArrayList<>());
+ while (rs.next()) {
+ localSlave = rs.getBoolean(1);
+ hostname = rs.getString(2);
+ }
+ isSlave = localSlave;
+ } catch (PoolExhaustedException peexc) {
+ throw peexc;
+ } catch (Exception e) {
+ LOGGER.error("", e);
+ isSlave = true;
+ }
+ if (isSlave) {
+ LOGGER.debug("SQL SLAVE : {} on server {}, pool {}", connectionName, getDbConnectionName(), getAvailableConnections());
+ } else {
+ LOGGER.debug("SQL MASTER : {} on server {}, pool {}", connectionName, getDbConnectionName(), getAvailableConnections());
+ }
+ return isSlave;
+ }
+
+ public boolean isFabric() {
+ return false;
+ }
+
+ protected boolean lockTable(Connection conn, String tableName) {
+ boolean retValue = false;
+ String query = "LOCK TABLES " + tableName + " WRITE";
+ try (Statement preStmt = conn.createStatement();
+ Statement lock = conn.prepareStatement(query)) {
+ if (tableName != null) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Executing 'LOCK TABLES " + tableName + " WRITE' on connection " + conn.toString());
+ if ("SVC_LOGIC".equals(tableName)) {
+ Exception e = new Exception();
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+ LOGGER.debug(sw.toString());
+ }
+ }
+ lock.execute(query);
+ retValue = true;
+ }
+ } catch (Exception exc) {
+ LOGGER.error("", exc);
+ retValue = false;
+ }
+ return retValue;
+ }
+
+ protected boolean unlockTable(Connection conn) {
+ boolean retValue;
+ try (Statement lock = conn.createStatement()) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Executing 'UNLOCK TABLES' on connection {}", conn);
+ }
+ retValue = lock.execute("UNLOCK TABLES");
+ } catch (Exception exc) {
+ LOGGER.error("", exc);
+ retValue = false;
+ }
+ return retValue;
+ }
+
+ public void getPoolInfo(boolean allocation) {
+
+ }
+
+ public long getNextErrorReportTime() {
+ return nextErrorReportTime;
+ }
+
+ public void setNextErrorReportTime(long nextTime) {
+ this.nextErrorReportTime = nextTime;
+ }
+
+ public void setGlobalHostName(String hostname) {
+ this.globalHostName = hostname;
+ }
+
+ public String getGlobalHostName() {
+ return globalHostName;
+ }
+
+ static class LapsedTimer {
+ private final long msTime = System.currentTimeMillis();
+
+ public String lapsedTime() {
+ double timediff = System.currentTimeMillis() - msTime;
+ timediff = timediff/1000;
+ return String.valueOf( timediff)+"s";
+ }
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/CachedDataSourceFactory.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/CachedDataSourceFactory.java
new file mode 100644
index 000000000..15aa7a1d0
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/CachedDataSourceFactory.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Modifications Copyright (C) 2018 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+
+import org.onap.ccsdk.sli.core.dblib.config.BaseDBConfiguration;
+import org.onap.ccsdk.sli.core.dblib.config.JDBCConfiguration;
+import org.onap.ccsdk.sli.core.dblib.jdbc.JdbcDBCachedDataSource;
+
+/**
+ * @version $Revision: 1.1 $
+ * Change Log
+ * Author Date Comments
+ * ============== ======== ====================================================
+ * Rich Tabedzki
+ */
+public class CachedDataSourceFactory {
+
+ public static CachedDataSource createDataSource(BaseDBConfiguration config) {
+ if(config instanceof JDBCConfiguration)
+ return JdbcDBCachedDataSource.createInstance(config);
+
+ return null;
+ }
+
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBConfigException.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBConfigException.java
new file mode 100644
index 000000000..e41e29b5d
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBConfigException.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+
+/**
+ * @version $Revision: 1.1 $
+ * Change Log
+ * Author Date Comments
+ * ============== ======== ====================================================
+ * Rich Tabedzki
+ */
+public class DBConfigException extends RuntimeException
+{
+ /**
+ *
+ */
+ private static final long serialVersionUID = 4752405152537680257L;
+
+ public DBConfigException(Exception e)
+ {
+ super(e.toString());
+ }
+
+ public DBConfigException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLIBResourceProvider.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLIBResourceProvider.java
new file mode 100755
index 000000000..45e5a7870
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLIBResourceProvider.java
@@ -0,0 +1,165 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Vector;
+import org.onap.ccsdk.sli.core.utils.JREFileResolver;
+import org.onap.ccsdk.sli.core.utils.KarafRootFileResolver;
+import org.onap.ccsdk.sli.core.utils.PropertiesFileResolver;
+import org.onap.ccsdk.sli.core.utils.common.CoreDefaultFileResolver;
+import org.onap.ccsdk.sli.core.utils.common.SdncConfigEnvVarFileResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Responsible for determining the properties file to use and instantiating the <code>DBResourceManager</code>
+ * Service. The priority for properties file resolution is as follows:
+ *
+ * <ol>
+ * <li>A directory identified by the system environment variable <code>SDNC_CONFIG_DIR</code></li>
+ * <li>The default directory <code>DEFAULT_DBLIB_PROP_DIR</code></li>
+ * <li>A directory identified by the JRE argument <code>dblib.properties</code></li>
+ * <li>A <code>dblib.properties</code> file located in the karaf root directory</li>
+ * </ol>
+ *
+ * Encryption Support
+ * <ol>
+ * <li>Uses ecryption provided by <code>AAAEncryptionService</code></li>
+ * <li>AAA Configuration file is <code>aaa-cert-config.xml</code></li>
+ * </ol>
+ */
+public class DBLIBResourceProvider {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DBLIBResourceProvider.class);
+
+ /**
+ * The name of the properties file for database configuration
+ */
+ private static final String DBLIB_PROP_FILE_NAME = "dblib.properties";
+
+ private static final String DBLIB_PROPERTY_NAME = "org.onap.ccsdk.sli.jdbc.password";
+
+ /**
+ * A prioritized list of strategies for resolving dblib properties files.
+ */
+ private Vector<PropertiesFileResolver> dblibPropertiesFileResolvers = new Vector<>();
+
+ /**
+ * The configuration properties for the db connection.
+ */
+ private Properties properties;
+
+ /**
+ * Set up the prioritized list of strategies for resolving dblib properties files.
+ */
+ public DBLIBResourceProvider() {
+ dblibPropertiesFileResolvers.add(new SdncConfigEnvVarFileResolver(
+ "Using property file (1) from environment variable"
+ ));
+ dblibPropertiesFileResolvers.add(new CoreDefaultFileResolver(
+ "Using property file (2) from default directory"
+ ));
+ dblibPropertiesFileResolvers.add(new JREFileResolver(
+ "Using property file (3) from JRE argument", DBLIBResourceProvider.class
+ ));
+ dblibPropertiesFileResolvers.add(new KarafRootFileResolver(
+ "Using property file (4) from karaf root", this));
+
+ // determines properties file as according to the priority described in the class header comment
+ final File propertiesFile = determinePropertiesFile(this);
+ if (propertiesFile != null) {
+ try(FileInputStream fileInputStream = new FileInputStream(propertiesFile)) {
+ properties = new Properties();
+ properties.load(fileInputStream);
+ } catch (final IOException e) {
+ LOG.error("Failed to load properties for file: {}", propertiesFile.toString(),
+ new DblibConfigurationException("Failed to load properties for file: "
+ + propertiesFile.toString(), e));
+ }
+ }
+ }
+
+ /**
+ * Extract db config properties.
+ *
+ * @return the db config properties
+ */
+ public Properties getProperties() {
+ return properties;
+ }
+
+ /**
+ * Reports the method chosen for properties resolution to the <code>Logger</code>.
+ *
+ * @param message Some user friendly message
+ * @param fileOptional The file location of the chosen properties file
+ * @return the file location of the chosen properties file
+ */
+ private static File reportSuccess(final String message, final Optional<File> fileOptional) {
+ if(fileOptional.isPresent()) {
+ final File file = fileOptional.get();
+ LOG.info("{} {}", message, file.getPath());
+ return file;
+ }
+ return null;
+ }
+
+ /**
+ * Reports fatal errors. This is the case in which no properties file could be found.
+ *
+ * @param message An appropriate fatal error message
+ * @param dblibConfigurationException An exception describing what went wrong during resolution
+ */
+ private static void reportFailure(final String message,
+ final DblibConfigurationException dblibConfigurationException) {
+
+ LOG.error("{}", message, dblibConfigurationException);
+ }
+
+ /**
+ * Determines the dblib properties file to use based on the following priority:
+ * <ol>
+ * <li>A directory identified by the system environment variable <code>SDNC_CONFIG_DIR</code></li>
+ * <li>The default directory <code>DEFAULT_DBLIB_PROP_DIR</code></li>
+ * <li>A directory identified by the JRE argument <code>dblib.properties</code></li>
+ * <li>A <code>dblib.properties</code> file located in the karaf root directory</li>
+ * </ol>
+ */
+ File determinePropertiesFile(final DBLIBResourceProvider dblibResourceProvider) {
+
+ for (final PropertiesFileResolver dblibPropertiesFileResolver : dblibPropertiesFileResolvers) {
+ final Optional<File> fileOptional = dblibPropertiesFileResolver.resolveFile(DBLIB_PROP_FILE_NAME);
+ if (fileOptional.isPresent()) {
+ return reportSuccess(dblibPropertiesFileResolver.getSuccessfulResolutionMessage(), fileOptional);
+ }
+ }
+
+ reportFailure("Missing configuration properties resource(3)",
+ new DblibConfigurationException("Missing configuration properties resource(3): "
+ + DBLIB_PROP_FILE_NAME));
+ return null;
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLibConnection.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLibConnection.java
new file mode 100644
index 000000000..65d0b9512
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLibConnection.java
@@ -0,0 +1,387 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+import javax.sql.rowset.CachedRowSet;
+import org.apache.tomcat.jdbc.pool.PooledConnection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DBLibConnection implements Connection {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DBLibConnection.class);
+
+ private final Connection connection;
+ private final CachedDataSource dataSource;
+ private boolean locked = false;
+ private String tableName = null;
+
+ public DBLibConnection(Connection con, CachedDataSource dataSource) {
+ this.connection = con;
+ this.dataSource = dataSource;
+ locked = false;
+ dataSource.getPoolInfo(true);
+ }
+
+ public boolean lockTable(String tablename) {
+ this.tableName = tablename;
+ locked = dataSource.lockTable(connection, tableName);
+ return locked;
+ }
+
+ public void resetInactivityTimer() {
+ Class<org.apache.tomcat.jdbc.pool.PooledConnection> iface = PooledConnection.class;
+ try {
+ PooledConnection pc = connection.unwrap(iface);
+ pc.setTimestamp(System.currentTimeMillis());
+ } catch (SQLException e) {
+ LOGGER.warn("Failed resetting timeout timer", e);
+ }
+ }
+
+ public boolean unlock() {
+ dataSource.unlockTable(connection);
+ locked = false;
+ return false;
+ }
+
+ public boolean writeData(String statement, List<String> arguments) throws Throwable {
+ ArrayList<Object> newList = new ArrayList<>();
+ if (arguments != null && !arguments.isEmpty()) {
+ newList.addAll(arguments);
+ }
+ resetInactivityTimer();
+ return dataSource.executeUpdatePreparedStatement(connection, statement, newList, false);
+ }
+
+ public CachedRowSet getData(String statement, List<String> arguments) throws Throwable {
+ ArrayList<Object> newList = new ArrayList<>();
+ if (arguments != null && !arguments.isEmpty()) {
+ newList.addAll(arguments);
+ }
+ resetInactivityTimer();
+ return dataSource.executePreparedStatement(connection, statement, newList, false);
+ }
+
+ @Override
+ public <T> T unwrap(Class<T> iFace) throws SQLException {
+ return connection.unwrap(iFace);
+ }
+
+ @Override
+ public boolean isWrapperFor(Class<?> iFace) throws SQLException {
+ return connection.isWrapperFor(iFace);
+ }
+
+ @Override
+ public Statement createStatement() throws SQLException {
+ return connection.createStatement();
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql) throws SQLException {
+ return connection.prepareStatement(sql);
+ }
+
+ @Override
+ public CallableStatement prepareCall(String sql) throws SQLException {
+ return connection.prepareCall(sql);
+ }
+
+ @Override
+ public String nativeSQL(String sql) throws SQLException {
+ return connection.nativeSQL(sql);
+ }
+
+ @Override
+ public void setAutoCommit(boolean autoCommit) throws SQLException {
+ connection.setAutoCommit(autoCommit);
+ }
+
+ @Override
+ public boolean getAutoCommit() throws SQLException {
+ return connection.getAutoCommit();
+ }
+
+ @Override
+ public void commit() throws SQLException {
+ connection.commit();
+ }
+
+ @Override
+ public void rollback() throws SQLException {
+ connection.rollback();
+ }
+
+ @Override
+ public void close() throws SQLException {
+ if (this.locked) {
+ try {
+ this.unlock();
+ } catch (Exception th) {
+ LOGGER.error("Failed unlocking", th);
+ }
+ }
+ if (connection != null && !connection.isClosed()) {
+ connection.close();
+ }
+ dataSource.getPoolInfo(false);
+ }
+
+ @Override
+ public boolean isClosed() throws SQLException {
+ return connection.isClosed();
+ }
+
+ @Override
+ public DatabaseMetaData getMetaData() throws SQLException {
+ return connection.getMetaData();
+ }
+
+ @Override
+ public void setReadOnly(boolean readOnly) throws SQLException {
+ connection.setReadOnly(readOnly);
+ }
+
+ @Override
+ public boolean isReadOnly() throws SQLException {
+ return connection.isReadOnly();
+ }
+
+ @Override
+ public void setCatalog(String catalog) throws SQLException {
+ connection.setCatalog(catalog);
+ }
+
+ @Override
+ public String getCatalog() throws SQLException {
+ return connection.getCatalog();
+ }
+
+ @Override
+ public void setTransactionIsolation(int level) throws SQLException {
+ connection.setTransactionIsolation(level);
+ }
+
+ @Override
+ public int getTransactionIsolation() throws SQLException {
+ return connection.getTransactionIsolation();
+ }
+
+ @Override
+ public SQLWarning getWarnings() throws SQLException {
+ return connection.getWarnings();
+ }
+
+ @Override
+ public void clearWarnings() throws SQLException {
+ connection.clearWarnings();
+ }
+
+ @Override
+ public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
+ return connection.createStatement(resultSetType, resultSetConcurrency);
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
+ throws SQLException {
+ return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
+ }
+
+ @Override
+ public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
+ return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
+ }
+
+ @Override
+ public Map<String, Class<?>> getTypeMap() throws SQLException {
+ return connection.getTypeMap();
+ }
+
+ @Override
+ public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
+ connection.setTypeMap(map);
+ }
+
+ @Override
+ public void setHoldability(int holdability) throws SQLException {
+ connection.setHoldability(holdability);
+ }
+
+ @Override
+ public int getHoldability() throws SQLException {
+ return connection.getHoldability();
+ }
+
+ @Override
+ public Savepoint setSavepoint() throws SQLException {
+ return connection.setSavepoint();
+ }
+
+ @Override
+ public Savepoint setSavepoint(String name) throws SQLException {
+ return connection.setSavepoint(name);
+ }
+
+ @Override
+ public void rollback(Savepoint savepoint) throws SQLException {
+ connection.rollback(savepoint);
+ }
+
+ @Override
+ public void releaseSavepoint(Savepoint savepoint) throws SQLException {
+ connection.releaseSavepoint(savepoint);
+ }
+
+ @Override
+ public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
+ throws SQLException {
+ return connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
+ int resultSetHoldability) throws SQLException {
+ return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
+ }
+
+ @Override
+ public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
+ int resultSetHoldability) throws SQLException {
+ return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
+ return connection.prepareStatement(sql, autoGeneratedKeys);
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
+ return connection.prepareStatement(sql, columnIndexes);
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
+ return connection.prepareStatement(sql, columnNames);
+ }
+
+ @Override
+ public Clob createClob() throws SQLException {
+ return connection.createClob();
+ }
+
+ @Override
+ public Blob createBlob() throws SQLException {
+ return connection.createBlob();
+ }
+
+ @Override
+ public NClob createNClob() throws SQLException {
+ return connection.createNClob();
+ }
+
+ @Override
+ public SQLXML createSQLXML() throws SQLException {
+ return connection.createSQLXML();
+ }
+
+ @Override
+ public boolean isValid(int timeout) throws SQLException {
+ return connection.isValid(timeout);
+ }
+
+ @Override
+ public void setClientInfo(String name, String value) throws SQLClientInfoException {
+ connection.setClientInfo(name, value);
+ }
+
+ @Override
+ public void setClientInfo(Properties properties) throws SQLClientInfoException {
+ connection.setClientInfo(properties);
+ }
+
+ @Override
+ public String getClientInfo(String name) throws SQLException {
+ return connection.getClientInfo(name);
+ }
+
+ @Override
+ public Properties getClientInfo() throws SQLException {
+ return connection.getClientInfo();
+ }
+
+ @Override
+ public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
+ return connection.createArrayOf(typeName, elements);
+ }
+
+ @Override
+ public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
+ return connection.createStruct(typeName, attributes);
+ }
+
+ @Override
+ public void setSchema(String schema) throws SQLException {
+ connection.setSchema(schema);
+ }
+
+ @Override
+ public String getSchema() throws SQLException {
+ return connection.getSchema();
+ }
+
+ @Override
+ public void abort(Executor executor) throws SQLException {
+ connection.abort(executor);
+ }
+
+ @Override
+ public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
+ connection.setNetworkTimeout(executor, milliseconds);
+ }
+
+ @Override
+ public int getNetworkTimeout() throws SQLException {
+ return connection.getNetworkTimeout();
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLibException.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLibException.java
new file mode 100644
index 000000000..de241f478
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBLibException.java
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+import java.sql.SQLException;
+
+/**
+ * An exception time for handling DBLIB specific error handling.
+ */
+public class DBLibException extends SQLException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -5345059355083984696L;
+
+ public DBLibException(String message){
+ super(message);
+ }
+
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBResourceManager.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBResourceManager.java
new file mode 100755
index 000000000..7c71bcc81
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBResourceManager.java
@@ -0,0 +1,983 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Modifications Copyright (C) 2018 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.SQLDataException;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLIntegrityConstraintViolationException;
+import java.sql.SQLNonTransientConnectionException;
+import java.sql.SQLSyntaxErrorException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Observable;
+import java.util.Properties;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.stream.Collectors;
+
+import javax.sql.DataSource;
+import javax.sql.rowset.CachedRowSet;
+
+import org.apache.tomcat.jdbc.pool.PoolExhaustedException;
+import org.onap.ccsdk.sli.core.dblib.config.DbConfigPool;
+import org.onap.ccsdk.sli.core.dblib.config.JDBCConfiguration;
+import org.onap.ccsdk.sli.core.dblib.config.TerminatingConfiguration;
+import org.onap.ccsdk.sli.core.dblib.factory.DBConfigFactory;
+import org.onap.ccsdk.sli.core.dblib.pm.PollingWorker;
+import org.onap.ccsdk.sli.core.dblib.pm.SQLExecutionMonitor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @version $Revision: 1.15 $
+ * Change Log
+ * Author Date Comments
+ * ============== ======== ====================================================
+ * Rich Tabedzki
+ */
+public class DBResourceManager implements DataSource, DataAccessor, DBResourceObserver, DbLibService {
+ private static final Logger LOGGER = LoggerFactory.getLogger(DBResourceManager.class);
+ private static final String DATABASE_URL = "org.onap.ccsdk.sli.jdbc.url";
+
+ transient boolean terminating = false;
+ transient protected long retryInterval = 10000L;
+ transient boolean recoveryMode = true;
+
+ SortedSet<CachedDataSource> dsQueue = new ConcurrentSkipListSet<>(new DataSourceComparator());
+ protected final Set<CachedDataSource> broken = Collections.synchronizedSet(new HashSet<CachedDataSource>());
+ protected final Object monitor = new Object();
+ protected final Properties configProps;
+ protected final Thread worker;
+
+ protected final long terminationTimeOut;
+ protected final boolean monitorDbResponse;
+ protected final long monitoringInterval;
+ protected final long monitoringInitialDelay;
+ protected final long expectedCompletionTime;
+ protected final long unprocessedFailoverThreshold;
+ private static final String LOGGER_ALARM_MSG="Generated alarm: DBResourceManager.getData - No active DB connection pools are available.";
+ private static final String EXCEPTION_MSG= "No active DB connection pools are available in RequestDataNoRecovery call.";
+
+ public DBResourceManager(final DBLIBResourceProvider configuration) {
+ this(configuration.getProperties());
+ }
+
+ public DBResourceManager(final Properties properties) {
+ this.configProps = processSystemVariables(properties);
+
+ // TODO : hack to force classloader to cache mariadb driver. This shouldnt be necessary,
+ // but for some reason it is (without this, dblib throws ClassNotFound on mariadb driver
+ // and fails to load).
+ LOGGER.info("Creating dummy instance of org.mariadb.jdbc.Driver");
+ Driver dvr = new org.mariadb.jdbc.Driver();
+ dvr = null;
+
+ // get retry interval value
+ retryInterval = getLongFromProperties(configProps, "org.onap.dblib.connection.retry", 10000L);
+
+ // get recovery mode flag
+ recoveryMode = getBooleanFromProperties(configProps, "org.onap.dblib.connection.recovery", true);
+ if(!recoveryMode)
+ {
+ recoveryMode = false;
+ LOGGER.info("Recovery Mode disabled");
+ }
+ // get time out value for thread cleanup
+ terminationTimeOut = getLongFromProperties(configProps, "org.onap.dblib.termination.timeout", 300000L);
+ // get properties for monitoring
+ monitorDbResponse = getBooleanFromProperties(configProps, "org.onap.dblib.connection.monitor", false);
+ monitoringInterval = getLongFromProperties(configProps, "org.onap.dblib.connection.monitor.interval", 1000L);
+ monitoringInitialDelay = getLongFromProperties(configProps, "org.onap.dblib.connection.monitor.startdelay", 5000L);
+ expectedCompletionTime = getLongFromProperties(configProps, "org.onap.dblib.connection.monitor.expectedcompletiontime", 5000L);
+ unprocessedFailoverThreshold = getLongFromProperties(configProps, "org.onap.dblib.connection.monitor.unprocessedfailoverthreshold", 3L);
+
+ // initialize performance monitor
+ PollingWorker.createInistance(configProps);
+
+ // initialize recovery thread
+ worker = new RecoveryMgr();
+ worker.setName("DBResourcemanagerWatchThread");
+ worker.setDaemon(true);
+ worker.start();
+
+ try {
+ this.config(configProps);
+ } catch (final Exception e) {
+ // TODO: config throws <code>Exception</code> which is poor practice. Eliminate this in a separate patch.
+ LOGGER.error("Fatal Exception encountered while configuring DBResourceManager", e);
+ }
+ }
+
+ public static Properties processSystemVariables(Properties properties) {
+ Map<Object, Object> hmap = new Properties();
+ hmap.putAll(properties);
+
+ Map<Object, Object> result = hmap.entrySet().stream()
+ .filter(map -> map.getValue().toString().startsWith("${"))
+ .filter(map -> map.getValue().toString().endsWith("}"))
+ .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
+
+ result.forEach((name, propEntries) -> {
+ hmap.put(name, replace(propEntries.toString()));
+ });
+
+ if(hmap.containsKey(DATABASE_URL) && hmap.get(DATABASE_URL).toString().contains("${")) {
+ String url = hmap.get(DATABASE_URL).toString();
+ String[] innerChunks = url.split("\\$\\{");
+ for(String chunk : innerChunks) {
+ if(chunk.contains("}")) {
+ String subChunk = chunk.substring(0, chunk.indexOf("}"));
+ String varValue = System.getenv(subChunk);
+ url = url.replace("${"+subChunk+"}", varValue);
+ }
+ }
+ hmap.put(DATABASE_URL, url);
+ }
+ return Properties.class.cast(hmap);
+ }
+
+
+ private static String replace(String value) {
+ String globalVariable = value.substring(2, value.length() -1);
+ String varValue = System.getenv(globalVariable);
+ return (varValue != null) ? varValue : value;
+ }
+
+
+ private void config(Properties configProps) throws Exception {
+ final ConcurrentLinkedQueue<CachedDataSource> semaphore = new ConcurrentLinkedQueue<>();
+ final DbConfigPool dbConfig = DBConfigFactory.createConfig(configProps);
+
+ long startTime = System.currentTimeMillis();
+
+ try {
+ JDBCConfiguration[] config = dbConfig.getJDBCbSourceArray();
+ CachedDataSource[] cachedDS = new CachedDataSource[config.length];
+ if (cachedDS == null || cachedDS.length == 0) {
+ LOGGER.error("Initialization of CachedDataSources failed. No instance was created.");
+ throw new Exception("Failed to initialize DB Library. No data source was created.");
+ }
+
+ for(int i = 0; i < config.length; i++) {
+ cachedDS[i] = CachedDataSourceFactory.createDataSource(config[i]);
+ if(cachedDS[i] == null)
+ continue;
+ semaphore.add(cachedDS[i]);
+ cachedDS[i].setInterval(monitoringInterval);
+ cachedDS[i].setInitialDelay(monitoringInitialDelay);
+ cachedDS[i].setExpectedCompletionTime(expectedCompletionTime);
+ cachedDS[i].setUnprocessedFailoverThreshold(unprocessedFailoverThreshold);
+ cachedDS[i].addObserver(DBResourceManager.this);
+ }
+
+ DataSourceTester[] tester = new DataSourceTester[config.length];
+
+ for(int i=0; i<tester.length; i++){
+ tester[i] = new DataSourceTester(cachedDS[i], DBResourceManager.this, semaphore);
+ tester[i].start();
+ }
+
+ // the timeout param is set is seconds.
+ long timeout = ((dbConfig.getTimeout() <= 0) ? 60L : dbConfig.getTimeout());
+ LOGGER.debug("Timeout set to {} seconds", timeout);
+ timeout *= 1000;
+
+
+ synchronized (semaphore) {
+ semaphore.wait(timeout);
+ }
+ } catch(Exception exc){
+ LOGGER.warn("DBResourceManager.initWorker", exc);
+ } finally {
+ startTime = System.currentTimeMillis() - startTime;
+ LOGGER.info("Completed wait with {} active datasource(s) in {} ms", dsQueue.size(), startTime);
+ }
+ }
+
+
+ private final class DataSourceComparator implements Comparator<CachedDataSource> {
+ @Override
+ public int compare(CachedDataSource left, CachedDataSource right) {
+ if(LOGGER.isTraceEnabled())
+ LOGGER.trace("----------SORTING-------- () : ()", left.getDbConnectionName(), right.getDbConnectionName());
+ try {
+ if(left == right) {
+ return 0;
+ }
+ if(left == null){
+ return 1;
+ }
+ if(right == null){
+ return -1;
+ }
+
+ boolean leftMaster = !left.isSlave();
+ if(leftMaster) {
+ if(left.getIndex() <= right.getIndex())
+ return -1;
+ else {
+ boolean rightMaster = !right.isSlave();
+ if(rightMaster) {
+ if(left.getIndex() <= right.getIndex())
+ return -1;
+// if(left.getIndex() > right.getIndex())
+ else {
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ }
+ if(!right.isSlave())
+ return 1;
+
+ if(left.getIndex() <= right.getIndex())
+ return -1;
+ if(left.getIndex() > right.getIndex())
+ return 1;
+
+
+ } catch (Throwable e) {
+ LOGGER.warn("", e);
+ }
+ return -1;
+ }
+ }
+
+ class DataSourceTester extends Thread {
+
+ private final CachedDataSource ds;
+ private final DBResourceManager manager;
+ private final ConcurrentLinkedQueue<CachedDataSource> semaphoreQ;
+
+ public DataSourceTester(CachedDataSource ds, DBResourceManager manager, ConcurrentLinkedQueue<CachedDataSource> semaphore) {
+ this.ds = ds;
+ this.manager = manager;
+ this.semaphoreQ = semaphore;
+ }
+
+ @Override
+ public void run() {
+ manager.setDataSource(ds);
+ boolean slave = true;
+ if(ds != null) {
+ try {
+ slave = ds.isSlave();
+ } catch (Exception exc) {
+ LOGGER.warn("", exc);
+ }
+ }
+ if(!slave) {
+ LOGGER.info("Adding MASTER {} to active queue", ds.getDbConnectionName());
+ try {
+ synchronized (semaphoreQ) {
+ semaphoreQ.notifyAll();
+ }
+ } catch(Exception exc) {
+ LOGGER.warn("", exc);
+ }
+ }
+ try {
+ synchronized (semaphoreQ) {
+ semaphoreQ.remove(ds);
+ }
+ if(semaphoreQ.isEmpty()) {
+ synchronized (semaphoreQ) {
+ semaphoreQ.notifyAll();
+ }
+ }
+ } catch(Exception exc) {
+ LOGGER.warn("", exc);
+ }
+ if(ds != null)
+ LOGGER.info("Thread DataSourceTester terminated {} for {}", this.getName(), ds.getDbConnectionName());
+ }
+
+ }
+
+
+ private long getLongFromProperties(Properties props, String property, long defaultValue)
+ {
+ String value = null;
+ long tmpLongValue = defaultValue;
+ try {
+ value = props.getProperty(property);
+ if(value != null)
+ tmpLongValue = Long.parseLong(value);
+
+ } catch(NumberFormatException exc) {
+ if(LOGGER.isWarnEnabled()){
+ LOGGER.warn("'"+property+"'=" + value+" is invalid. It should be a numeric value");
+ }
+ } catch(Exception exc) {
+ }
+ return tmpLongValue;
+
+ }
+
+ private boolean getBooleanFromProperties(Properties props, String property, boolean defaultValue)
+ {
+ boolean tmpValue = defaultValue;
+ String value = null;
+
+ try {
+ value = props.getProperty(property);
+ if(value != null)
+ tmpValue = Boolean.parseBoolean(value);
+
+ } catch(NumberFormatException exc) {
+ if(LOGGER.isWarnEnabled()){
+ LOGGER.warn("'"+property+"'=" + value+" is invalid. It should be a boolean value");
+ }
+ } catch(Exception exc) {
+ }
+ return tmpValue;
+
+ }
+
+
+ @Override
+ public void update(Observable observable, Object data) {
+ // if observable is active and there is a standby available, switch
+ if(observable instanceof SQLExecutionMonitor)
+ {
+ SQLExecutionMonitor monitor = (SQLExecutionMonitor)observable;
+ if(monitor.getParent() instanceof CachedDataSource)
+ {
+ CachedDataSource dataSource = (CachedDataSource)monitor.getParent();
+ if(dataSource == dsQueue.first())
+ {
+ if(recoveryMode && dsQueue.size() > 1){
+ handleGetConnectionException(dataSource, new Exception(data.toString()));
+ }
+ }
+ }
+ }
+ }
+
+ public void testForceRecovery()
+ {
+ CachedDataSource active = this.dsQueue.first();
+ handleGetConnectionException(active, new Exception("test"));
+ }
+
+ class RecoveryMgr extends Thread {
+
+ @Override
+ public void run() {
+ while(!terminating)
+ {
+ try {
+ Thread.sleep(retryInterval);
+ } catch (InterruptedException e1) { }
+ CachedDataSource brokenSource = null;
+ try {
+ if (!broken.isEmpty()) {
+ CachedDataSource[] sourceArray = broken.toArray(new CachedDataSource[0]);
+ for (int i = 0; i < sourceArray.length; i++)
+ {
+ brokenSource = sourceArray[i];
+ if (brokenSource instanceof TerminatingCachedDataSource)
+ break;
+ if (resetConnectionPool(brokenSource)) {
+ broken.remove(brokenSource);
+ brokenSource.blockImmediateOffLine();
+ dsQueue.add(brokenSource);
+ LOGGER.info("DataSource <"
+ + brokenSource.getDbConnectionName()
+ + "> recovered.");
+ }
+ brokenSource = null;
+ }
+ }
+ } catch (Exception exc) {
+ LOGGER.warn(exc.getMessage());
+ if(brokenSource != null){
+ try {
+ if(!broken.contains(brokenSource))
+ broken.add(brokenSource);
+ brokenSource = null;
+ } catch (Exception e1) { }
+ }
+ }
+ }
+ LOGGER.info("DBResourceManager.RecoveryMgr <"+this.toString() +"> terminated." );
+ }
+
+ private boolean resetConnectionPool(CachedDataSource dataSource){
+ try {
+ return dataSource.testConnection();
+ } catch (Exception exc) {
+ LOGGER.info("DataSource <" + dataSource.getDbConnectionName() + "> resetCache failed with error: "+ exc.getMessage());
+ return false;
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.ccsdk.sli.resource.dblib.DbLibService#getData(java.lang.String, java.util.ArrayList, java.lang.String)
+ */
+ @Override
+ public CachedRowSet getData(String statement, ArrayList<String> arguments, String preferredDS) throws SQLException {
+ ArrayList<Object> newList= new ArrayList<>();
+ if(arguments != null && !arguments.isEmpty()) {
+ newList.addAll(arguments);
+ }
+ if(recoveryMode)
+ return requestDataWithRecovery(statement, newList, preferredDS);
+ else
+ return requestDataNoRecovery(statement, newList, preferredDS);
+ }
+
+ private CachedRowSet requestDataWithRecovery(String statement, ArrayList<Object> arguments, String preferredDS) throws SQLException {
+ Throwable lastException = null;
+
+ // test if there are any connection pools available
+ if(this.dsQueue.isEmpty()){
+ LOGGER.error(LOGGER_ALARM_MSG);
+ throw new DBLibException("No active DB connection pools are available in RequestDataWithRecovery call.");
+ }
+
+ // loop through available data sources to retrieve data.
+ for(int i=0; i< 2; i++)
+ {
+ CachedDataSource active = this.dsQueue.first();
+
+ long time = System.currentTimeMillis();
+ try {
+ if(!active.isFabric()) {
+ if(this.dsQueue.size() > 1 && active.isSlave()) {
+ CachedDataSource master = findMaster();
+ if(master != null) {
+ active = master;
+ }
+ }
+ }
+
+ return active.getData(statement, arguments);
+ } catch(SQLDataException | SQLSyntaxErrorException | SQLIntegrityConstraintViolationException exc){
+ throw exc;
+ } catch(Throwable exc){
+ if(exc instanceof SQLException) {
+ SQLException sqlExc = (SQLException)exc;
+ int code = sqlExc.getErrorCode();
+ String state = sqlExc.getSQLState();
+ LOGGER.debug("SQLException code: {} state: {}", code, state);
+ if("07001".equals(sqlExc.getSQLState())) {
+ throw sqlExc;
+ }
+ }
+ lastException = exc;
+ LOGGER.error("Generated alarm: {}", active.getDbConnectionName(), exc);
+ handleGetConnectionException(active, exc);
+ } finally {
+ if(LOGGER.isDebugEnabled()){
+ time = System.currentTimeMillis() - time;
+ LOGGER.debug("getData processing time : {} {} miliseconds.", active.getDbConnectionName(), time);
+ }
+ }
+ }
+ if(lastException instanceof SQLException){
+ throw (SQLException)lastException;
+ }
+ // repackage the exception
+ // you are here because either you run out of available data sources
+ // or the last exception was not of SQLException type.
+ // repackage the exception
+ if(lastException == null) {
+ throw new DBLibException("The operation timed out while waiting to acquire a new connection." );
+ } else {
+ SQLException exception = new DBLibException(lastException.getMessage());
+ exception.setStackTrace(lastException.getStackTrace());
+ if(lastException.getCause() instanceof SQLException) {
+ throw (SQLException)lastException.getCause();
+ }
+ throw exception;
+ }
+ }
+
+ private CachedRowSet requestDataNoRecovery(String statement, ArrayList<Object> arguments, String preferredDS) throws SQLException {
+ if(dsQueue.isEmpty()){
+ LOGGER.error(LOGGER_ALARM_MSG);
+ throw new DBLibException(EXCEPTION_MSG);
+ }
+ CachedDataSource active = this.dsQueue.first();
+ long time = System.currentTimeMillis();
+ try {
+ if(!active.isFabric()) {
+ if(this.dsQueue.size() > 1 && active.isSlave()) {
+ CachedDataSource master = findMaster();
+ if(master != null) {
+ active = master;
+ }
+ }
+ }
+ return active.getData(statement, arguments);
+
+ } catch(Throwable exc){
+ String message = exc.getMessage();
+ if(message == null)
+ message = exc.getClass().getName();
+ LOGGER.error("Generated alarm: {} - {}",active.getDbConnectionName(), message);
+ if(exc instanceof SQLException)
+ throw (SQLException)exc;
+ else {
+ DBLibException excptn = new DBLibException(exc.getMessage());
+ excptn.setStackTrace(exc.getStackTrace());
+ throw excptn;
+ }
+ } finally {
+ if(LOGGER.isDebugEnabled()){
+ time = System.currentTimeMillis() - time;
+ LOGGER.debug(">> getData : {} {} miliseconds.", active.getDbConnectionName(), time);
+ }
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.onap.ccsdk.sli.resource.dblib.DbLibService#writeData(java.lang.String, java.util.ArrayList, java.lang.String)
+ */
+ @Override
+ public boolean writeData(String statement, ArrayList<String> arguments, String preferredDS) throws SQLException
+ {
+ ArrayList<Object> newList= new ArrayList<>();
+ if(arguments != null && !arguments.isEmpty()) {
+ newList.addAll(arguments);
+ }
+
+ return writeDataNoRecovery(statement, newList, preferredDS);
+ }
+
+ synchronized CachedDataSource findMaster() throws SQLException {
+ final CachedDataSource[] clone = this.dsQueue.toArray(new CachedDataSource[0]);
+
+ for(final CachedDataSource dss : clone) {
+ if(!dss.isSlave()) {
+ final CachedDataSource first = this.dsQueue.first();
+ if(first != dss) {
+ if(LOGGER.isDebugEnabled())
+ LOGGER.debug("----------REODRERING--------");
+ dsQueue.clear();
+ if(!dsQueue.addAll(Arrays.asList(clone))) {
+ LOGGER.error("Failed adding datasources");
+ }
+ }
+ return dss;
+ }
+ }
+ LOGGER.warn("MASTER not found.");
+ return null;
+ }
+
+
+ private boolean writeDataNoRecovery(String statement, ArrayList<Object> arguments, String preferredDS) throws SQLException {
+ if(dsQueue.isEmpty()){
+ LOGGER.error(LOGGER_ALARM_MSG);
+ throw new DBLibException(EXCEPTION_MSG);
+ }
+
+ boolean initialRequest = true;
+ boolean retryAllowed = true;
+ CachedDataSource active = this.dsQueue.first();
+ long time = System.currentTimeMillis();
+ while(initialRequest) {
+ initialRequest = false;
+ try {
+ if(!active.isFabric()) {
+ if(this.dsQueue.size() > 1 && active.isSlave()) {
+ CachedDataSource master = findMaster();
+ if(master != null) {
+ active = master;
+ }
+ }
+ }
+
+ return active.writeData(statement, arguments);
+ } catch(Throwable exc){
+ String message = exc.getMessage();
+ if(message == null)
+ message = exc.getClass().getName();
+ LOGGER.error("Generated alarm: {} - {}", active.getDbConnectionName(), message);
+ if(exc instanceof SQLException) {
+ SQLException sqlExc = SQLException.class.cast(exc);
+ // handle read-only exception
+ if(sqlExc.getErrorCode() == 1290 && "HY000".equals(sqlExc.getSQLState())) {
+ LOGGER.warn("retrying due to: {}", sqlExc.getMessage());
+ this.findMaster();
+ if(retryAllowed){
+ retryAllowed = false;
+ initialRequest = true;
+ continue;
+ }
+ }
+ throw (SQLException)exc;
+ } else {
+ DBLibException excptn = new DBLibException(exc.getMessage());
+ excptn.setStackTrace(exc.getStackTrace());
+ throw excptn;
+ }
+ } finally {
+ if(LOGGER.isDebugEnabled()){
+ time = System.currentTimeMillis() - time;
+ LOGGER.debug("writeData processing time : {} {} miliseconds.", active.getDbConnectionName(), time);
+ }
+ }
+ }
+ return true;
+ }
+
+ public void setDataSource(CachedDataSource dataSource) {
+ if(this.dsQueue.contains(dataSource))
+ return;
+ if(this.broken.contains(dataSource))
+ return;
+
+ if(dataSource.testConnection(true)){
+ this.dsQueue.add(dataSource);
+ } else {
+ this.broken.add(dataSource);
+ }
+ }
+
+ @Override
+ public Connection getConnection() throws SQLException {
+ Throwable lastException = null;
+ CachedDataSource active = null;
+
+ if(dsQueue.isEmpty()){
+ throw new DBLibException("No active DB connection pools are available in GetConnection call.");
+ }
+
+ try {
+ active = dsQueue.first();
+
+ if(!active.isFabric()) {
+ if(this.dsQueue.size() > 1 && active.isSlave()) {
+ LOGGER.debug("Forcing reorder on: {}", dsQueue.toString());
+ CachedDataSource master = findMaster();
+ if(master != null) {
+ active = master;
+ }
+ }
+ }
+ return new DBLibConnection(active.getConnection(), active);
+ } catch(javax.sql.rowset.spi.SyncFactoryException exc){
+ LOGGER.debug("Free memory (bytes): " + Runtime.getRuntime().freeMemory());
+ LOGGER.warn("CLASSPATH issue. Allowing retry", exc);
+ lastException = exc;
+ } catch(PoolExhaustedException exc) {
+ throw new NoAvailableConnectionsException(exc);
+ } catch(SQLNonTransientConnectionException exc){
+ throw new NoAvailableConnectionsException(exc);
+ } catch(Exception exc){
+ lastException = exc;
+ if(recoveryMode){
+ handleGetConnectionException(active, exc);
+ } else {
+ if(exc instanceof SQLException) {
+ throw (SQLException)exc;
+ } else {
+ DBLibException excptn = new DBLibException(exc.getMessage());
+ excptn.setStackTrace(exc.getStackTrace());
+ throw excptn;
+ }
+ }
+ } catch (Throwable trwb) {
+ DBLibException excptn = new DBLibException(trwb.getMessage());
+ excptn.setStackTrace(trwb.getStackTrace());
+ throw excptn;
+ } finally {
+ if(LOGGER.isDebugEnabled()){
+ displayState();
+ }
+ }
+
+ if(lastException instanceof SQLException){
+ throw (SQLException)lastException;
+ }
+ // repackage the exception
+ if(lastException == null) {
+ throw new DBLibException("The operation timed out while waiting to acquire a new connection." );
+ } else {
+ SQLException exception = new DBLibException(lastException.getMessage());
+ exception.setStackTrace(lastException.getStackTrace());
+ if(lastException.getCause() instanceof SQLException) {
+
+ throw (SQLException)lastException.getCause();
+ }
+ throw exception;
+ }
+ }
+
+ @Override
+ public Connection getConnection(String username, String password)
+ throws SQLException {
+ CachedDataSource active = null;
+
+ if(dsQueue.isEmpty()){
+ throw new DBLibException("No active DB connection pools are available in GetConnection call.");
+ }
+
+
+ try {
+ active = dsQueue.first();
+ if(!active.isFabric()) {
+ if(this.dsQueue.size() > 1 && active.isSlave()) {
+ CachedDataSource master = findMaster();
+ if(master != null) {
+ active = master;
+ }
+ }
+ }
+ return active.getConnection(username, password);
+ } catch(Throwable exc){
+ if(recoveryMode){
+ handleGetConnectionException(active, exc);
+ } else {
+ if(exc instanceof SQLException)
+ throw (SQLException)exc;
+ else {
+ DBLibException excptn = new DBLibException(exc.getMessage());
+ excptn.setStackTrace(exc.getStackTrace());
+ throw excptn;
+ }
+ }
+
+ }
+
+ throw new DBLibException("No connections available in DBResourceManager in GetConnection call.");
+ }
+
+ private void handleGetConnectionException(final CachedDataSource source, Throwable exc) {
+ try {
+ if(!source.canTakeOffLine())
+ {
+ LOGGER.error("Could not switch due to blocking");
+ return;
+ }
+
+ boolean removed = dsQueue.remove(source);
+ if(!broken.contains(source))
+ {
+ if(broken.add(source))
+ {
+ LOGGER.warn("DB Recovery: DataSource <" + source.getDbConnectionName() + "> put in the recovery mode. Reason : " + exc.getMessage());
+ } else {
+ LOGGER.warn("Error putting DataSource <" +source.getDbConnectionName()+ "> in recovery mode.");
+ }
+ } else {
+ LOGGER.info("DB Recovery: DataSource <" + source.getDbConnectionName() + "> already in recovery queue");
+ }
+ if(removed)
+ {
+ if(!dsQueue.isEmpty())
+ {
+ LOGGER.warn("DB DataSource <" + dsQueue.first().getDbConnectionName() + "> became active");
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.error("", e);
+ }
+ }
+
+ public void cleanUp() {
+ for(Iterator<CachedDataSource> it=dsQueue.iterator();it.hasNext();){
+ CachedDataSource cds = it.next();
+ it.remove();
+ cds.cleanUp();
+ }
+
+ try {
+ this.terminating = true;
+ if(broken != null)
+ {
+ try {
+ broken.add( new TerminatingCachedDataSource(new TerminatingConfiguration()));
+ } catch(Exception exc){
+ LOGGER.error("Waiting for Worker to stop", exc);
+ }
+ }
+ worker.join(terminationTimeOut);
+ LOGGER.info("DBResourceManager.RecoveryMgr <"+worker.toString() +"> termination was successful: " + worker.getState());
+ } catch(Exception exc){
+ LOGGER.error("Waiting for Worker thread to terminate ", exc);
+ }
+ }
+
+ @Override
+ public PrintWriter getLogWriter() throws SQLException {
+ return this.dsQueue.first().getLogWriter();
+ }
+
+ @Override
+ public int getLoginTimeout() throws SQLException {
+ return this.dsQueue.first().getLoginTimeout();
+ }
+
+ @Override
+ public void setLogWriter(PrintWriter out) throws SQLException {
+ this.dsQueue.first().setLogWriter(out);
+ }
+
+ @Override
+ public void setLoginTimeout(int seconds) throws SQLException {
+ this.dsQueue.first().setLoginTimeout(seconds);
+ }
+
+ public void displayState(){
+ if(LOGGER.isDebugEnabled()){
+ LOGGER.debug("POOLS : Active = "+dsQueue.size() + ";\t Broken = "+broken.size());
+ CachedDataSource current = dsQueue.first();
+ if(current != null) {
+ LOGGER.debug("POOL : Active name = \'"+current.getDbConnectionName()+ "\'");
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.ccsdk.sli.resource.dblib.DbLibService#isActive()
+ */
+ @Override
+ public boolean isActive() {
+ return this.dsQueue.size()>0;
+ }
+
+ public String getActiveStatus(){
+ return "Connected: " + dsQueue.size()+"\tIn-recovery: "+broken.size();
+ }
+
+ public String getDBStatus(boolean htmlFormat) {
+ StringBuilder buffer = new StringBuilder();
+
+ ArrayList<CachedDataSource> list = new ArrayList<>();
+ list.addAll(dsQueue);
+ list.addAll(broken);
+ if (htmlFormat)
+ {
+ buffer.append("<tr class=\"headerRow\"><th id=\"header1\">")
+ .append("Name:").append("</th>");
+ for (int i = 0; i < list.size(); i++) {
+ buffer.append("<th id=\"header").append(2 + i).append("\">");
+ buffer.append(list.get(i).getDbConnectionName()).append("</th>");
+ }
+ buffer.append("</tr>");
+
+ buffer.append("<tr><td>State:</td>");
+ for (int i = 0; i < list.size(); i++) {
+ if (broken.contains(list.get(i))) {
+ buffer.append("<td>in recovery</td>");
+ }
+ if (dsQueue.contains(list.get(i))) {
+ if (dsQueue.first() == list.get(i))
+ buffer.append("<td>active</td>");
+ else
+ buffer.append("<td>standby</td>");
+ }
+ }
+ buffer.append("</tr>");
+
+ } else {
+ for (int i = 0; i < list.size(); i++) {
+ buffer.append("Name: ").append(list.get(i).getDbConnectionName());
+ buffer.append("\tState: ");
+ if (broken.contains(list.get(i))) {
+ buffer.append("in recovery");
+ } else
+ if (dsQueue.contains(list.get(i))) {
+ if (dsQueue.first() == list.get(i))
+ buffer.append("active");
+ else
+ buffer.append("standby");
+ }
+
+ buffer.append("\n");
+
+ }
+ }
+ return buffer.toString();
+ }
+
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException {
+ return null;
+ }
+
+ /**
+ * @return the monitorDbResponse
+ */
+ @Override
+ public final boolean isMonitorDbResponse() {
+ return recoveryMode && monitorDbResponse;
+ }
+
+ public void test(){
+ CachedDataSource obj = dsQueue.first();
+ Exception ption = new Exception();
+ try {
+ for(int i=0; i<5; i++)
+ {
+ handleGetConnectionException(obj, ption);
+ }
+ } catch(Throwable exc){
+ LOGGER.warn("", exc);
+ }
+ }
+
+ @Override
+ public java.util.logging.Logger getParentLogger()
+ throws SQLFeatureNotSupportedException {
+ return null;
+ }
+
+ class RemindTask extends TimerTask {
+ @Override
+ public void run() {
+ CachedDataSource ds = dsQueue.first();
+ if(ds != null)
+ ds.getPoolInfo(false);
+ }
+ }
+
+ public int poolSize() {
+ return dsQueue.size();
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBResourceObserver.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBResourceObserver.java
new file mode 100644
index 000000000..f30212dca
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DBResourceObserver.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+import java.util.Observer;
+
+public interface DBResourceObserver extends Observer {
+ boolean isMonitorDbResponse();
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DataAccessor.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DataAccessor.java
new file mode 100644
index 000000000..93d8b6bf5
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DataAccessor.java
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+
+import javax.sql.rowset.CachedRowSet;
+
+@FunctionalInterface
+public interface DataAccessor {
+
+ CachedRowSet getData(String statement, ArrayList<String> arguments, String preferredDS)
+ throws SQLException;
+
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DataSourceComparator.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DataSourceComparator.java
new file mode 100644
index 000000000..4cfcc7318
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DataSourceComparator.java
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+import java.util.Comparator;
+
+public interface DataSourceComparator extends Comparator <CachedDataSource>{
+
+ CachedDataSource getLastUsed();
+
+ void setLastUsed(CachedDataSource lastUsed);
+
+ int compare(CachedDataSource ds1, CachedDataSource ds2);
+
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DbLibService.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DbLibService.java
new file mode 100644
index 000000000..b30ad7bbf
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DbLibService.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+
+import javax.sql.DataSource;
+import javax.sql.rowset.CachedRowSet;
+
+public interface DbLibService extends DataSource {
+
+ /* (non-Javadoc)
+ * @see DataAccessor#getData(java.lang.String, java.util.ArrayList)
+ */
+ CachedRowSet getData(String statement,
+ ArrayList<String> arguments, String preferredDS)
+ throws SQLException;
+
+ /* (non-Javadoc)
+ * @see DataAccessor#writeData(java.lang.String, java.util.ArrayList)
+ */
+ boolean writeData(String statement,
+ ArrayList<String> arguments, String preferredDS)
+ throws SQLException;
+
+ boolean isActive();
+
+ Connection getConnection() throws SQLException;
+
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DblibConfigurationException.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DblibConfigurationException.java
new file mode 100644
index 000000000..be493aa1f
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/DblibConfigurationException.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+
+public class DblibConfigurationException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public DblibConfigurationException()
+ {
+ super();
+ }
+
+ public DblibConfigurationException(String msg)
+ {
+ super(msg);
+ }
+
+ public DblibConfigurationException(String msg, Throwable t)
+ {
+ super(msg, t);
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/NoAvailableConnectionsException.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/NoAvailableConnectionsException.java
new file mode 100644
index 000000000..a39e5c5b4
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/NoAvailableConnectionsException.java
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+import java.sql.SQLException;
+
+public class NoAvailableConnectionsException extends SQLException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -6259205931674413018L;
+
+ public NoAvailableConnectionsException(Exception exc) {
+ super(exc);
+ }
+
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/TerminatingCachedDataSource.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/TerminatingCachedDataSource.java
new file mode 100755
index 000000000..852dda3c7
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/TerminatingCachedDataSource.java
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib;
+
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.logging.Logger;
+import javax.sql.DataSource;
+import org.onap.ccsdk.sli.core.dblib.config.BaseDBConfiguration;
+import org.onap.ccsdk.sli.core.dblib.pm.SQLExecutionMonitorObserver;
+
+
+public class TerminatingCachedDataSource extends CachedDataSource implements SQLExecutionMonitorObserver {
+
+ private static final int DEFAULT_AVAILABLE_CONNECTIONS = 0;
+ private static final int DEFAULT_INDEX = -1;
+
+ public TerminatingCachedDataSource(BaseDBConfiguration jdbcElem) throws DBConfigException {
+ super(jdbcElem);
+ }
+
+ @Override
+ protected DataSource configure(BaseDBConfiguration jdbcElem) throws DBConfigException {
+ return null;
+ }
+
+ @Override
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ return null;
+ }
+
+ @Override
+ protected int getAvailableConnections() {
+ return DEFAULT_AVAILABLE_CONNECTIONS;
+ }
+
+ @Override
+ protected int initializeIndex(BaseDBConfiguration jdbcElem) {
+ return DEFAULT_INDEX;
+ }
+
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/BaseDBConfiguration.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/BaseDBConfiguration.java
new file mode 100755
index 000000000..ea6bc450d
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/BaseDBConfiguration.java
@@ -0,0 +1,258 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * Modifications Copyright (C) 2018 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib.config;
+
+import java.util.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+/**
+ * Base class responsible for parsing business logic for database configuration from given <code>Properties</code>.
+ */
+public abstract class BaseDBConfiguration {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BaseDBConfiguration.class);
+ /**
+ * Property key within a properties configuration File for db type
+ */
+ public static final String DATABASE_TYPE = "org.onap.ccsdk.sli.dbtype";
+
+ /**
+ * Property key with a properties configuration File for db url
+ */
+ public static final String DATABASE_URL = "org.onap.ccsdk.sli.jdbc.url";
+
+ /**
+ * Property key with a properties configuration File for database name
+ */
+ public static final String DATABASE_NAME = "org.onap.ccsdk.sli.jdbc.database";
+
+ /**
+ * Property key with a properties configuration File for jdbc driver
+ */
+ public static final String DRIVER_NAME = "org.onap.ccsdk.sli.jdbc.driver";
+
+ /**
+ * Property key with a properties configuration File for db database connection name
+ */
+ public static final String CONNECTION_NAME = "org.onap.ccsdk.sli.jdbc.connection.name";
+
+ /**
+ * Property key with a properties configuration File for database user
+ */
+ public static final String DATABASE_USER = "org.onap.ccsdk.sli.jdbc.user";
+
+ /**
+ * Property key with a properties configuration File for database password
+ * for associated with <code>org.onap.ccsdk.sli.jdbc.user</code>.
+ */
+ public static final String DATABASE_PSSWD = "org.onap.ccsdk.sli.jdbc.password";
+
+ /**
+ * Property key with a properties configuration File for database connection
+ * timeout
+ */
+ public static final String CONNECTION_TIMEOUT="org.onap.ccsdk.sli.jdbc.connection.timeout";
+
+ /**
+ * Property key with a properties configuration File for database request
+ * timeout
+ */
+ public static final String REQUEST_TIMEOUT = "org.onap.ccsdk.sli.jdbc.request.timeout";
+
+ /**
+ * Property key with a properties configuration File for database minimum
+ * limit
+ */
+ public static final String MIN_LIMIT = "org.onap.ccsdk.sli.jdbc.limit.min";
+
+ /**
+ * Property key with a properties configuration File for database maximum
+ * limit
+ */
+ public static final String MAX_LIMIT = "org.onap.ccsdk.sli.jdbc.limit.max";
+
+ /**
+ * Property key with a properties configuration File for database initial
+ * limit
+ */
+ public static final String INIT_LIMIT = "org.onap.ccsdk.sli.jdbc.limit.init";
+
+ /**
+ * Property key with a properties configuration File for database hosts
+ */
+ public static final String DATABASE_HOSTS = "org.onap.ccsdk.sli.jdbc.hosts";
+
+ /**
+ * default value when the connection timeout is not present or cannot be
+ * parsed.
+ */
+ private static final String DEFAULT_REJECT_CHANGE_VALUE = "-1";
+
+ /**
+ * A set of properties with database configuration information.
+ */
+ protected final Properties properties;
+
+ /**
+ * Builds a configuration based on given properties
+ *
+ * @param properties
+ * properties represented by the public constant keys defined by
+ * this class
+ */
+ public BaseDBConfiguration(final Properties properties) {
+ this.properties = properties;
+ }
+
+ /**
+ * Extracts the connection timeout.
+ *
+ * @return the connection timeout, or
+ * <code>DEFAULT_REJECT_CHANGE_VALUE</code> if not present
+ */
+ public int getConnTimeout() {
+ try {
+ String value = properties.getProperty(CONNECTION_TIMEOUT, DEFAULT_REJECT_CHANGE_VALUE);
+ return Integer.parseInt(value);
+ } catch (Exception exc) {
+ LOGGER.error("Exception",exc);
+ return Integer.parseInt(DEFAULT_REJECT_CHANGE_VALUE);
+ }
+ }
+
+ /**
+ * Extracts the request timeout.
+ *
+ * @return the request timeout, or <code>DEFAULT_REQUEST_TIMEOUT</code> if
+ * not present
+ */
+ public int getRequestTimeout() {
+ try {
+ String value = properties.getProperty(REQUEST_TIMEOUT, DEFAULT_REJECT_CHANGE_VALUE);
+ return Integer.parseInt(value);
+ } catch (Exception exc) {
+ LOGGER.error("Exception",exc);
+ return Integer.parseInt(DEFAULT_REJECT_CHANGE_VALUE);
+ }
+ }
+
+ /**
+ * Extracts the db connection name.
+ *
+ * @return the db connection name, or <code>null</code> if not present
+ */
+ public String getDbConnectionName() {
+ return properties.getProperty(CONNECTION_NAME);
+ }
+
+ /**
+ * Extracts the db name.
+ *
+ * @return the db name, or <code>null</code> if not present
+ */
+ public String getDatabaseName() {
+ return properties.getProperty(DATABASE_NAME);
+ }
+
+ /**
+ * Extracts the jdbc driver's name.
+ *
+ * @return the jdbc name, or <code>com.mysql.jdbc.Driver</code> if not present
+ */
+ public String getDriverName() {
+ return properties.getProperty(DRIVER_NAME, "com.mysql.jdbc.Driver");
+ }
+
+ /**
+ * Extracts the db user id.
+ *
+ * @return the db user id, or <code>null</code> if not present
+ */
+ public String getDbUserId() {
+ return properties.getProperty(DATABASE_USER);
+ }
+
+ /**
+ * Extracts the db password.
+ *
+ * @return the db password, or <code>null</code> if not present
+ */
+ public String getDbPasswd() {
+ return properties.getProperty(DATABASE_PSSWD);
+ }
+
+ /**
+ * Extracts the db min limit.
+ *
+ * @return the db min limit
+ * @throws NumberFormatException
+ * if the property is not specified, or cannot be parsed as an
+ * <code>Integer</code>.
+ */
+ public int getDbMinLimit() throws NumberFormatException {
+ String value = properties.getProperty(MIN_LIMIT, "-1");
+ return Integer.parseInt(value);
+ }
+
+ /**
+ * Extracts the db max limit.
+ *
+ * @return the db max limit
+ * @throws NumberFormatException
+ * if the property is not specified, or cannot be parsed as an
+ * <code>Integer</code>.
+ */
+ public int getDbMaxLimit() throws NumberFormatException {
+ String value = properties.getProperty(MAX_LIMIT, "-1");
+ return Integer.parseInt(value);
+ }
+
+ /**
+ * Extracts the db initial limit.
+ *
+ * @return the db initial limit
+ * @throws NumberFormatException
+ * if the property is not specified, or cannot be parsed as an
+ * <code>Integer</code>.
+ */
+ public int getDbInitialLimit() throws NumberFormatException {
+ String value = properties.getProperty(INIT_LIMIT, "-1");
+ return Integer.parseInt(value);
+ }
+
+ /**
+ * Extracts the db url.
+ *
+ * @return the db url, or <code>null</code> if not present
+ */
+ public String getDbUrl() {
+ return properties.getProperty(DATABASE_URL);
+ }
+
+ public boolean containsKey(String propertyname) {
+ return properties.containsKey(propertyname);
+ }
+
+ public String getProperty(String propertyname) {
+ return properties.getProperty(propertyname);
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/DbConfigPool.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/DbConfigPool.java
new file mode 100644
index 000000000..ea7b3fdc8
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/DbConfigPool.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Modifications Copyright (C) 2018 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib.config;
+
+import java.util.ArrayList;
+import java.util.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @version $Revision: 1.15 $
+ * Change Log
+ * Author Date Comments
+ * ============== ======== ====================================================
+ * Rich Tabedzki
+ */
+public class DbConfigPool {
+ private static final Logger LOGGER = LoggerFactory.getLogger(DbConfigPool.class);
+ private final String type;
+ private static final int timeOut=0;
+ private ArrayList<BaseDBConfiguration> configurations = new ArrayList<>();
+
+ public DbConfigPool(Properties properties) {
+ LOGGER.debug("Initializing DbConfigType");
+ type = properties.getProperty(BaseDBConfiguration.DATABASE_TYPE, "JDBC").toUpperCase();
+ }
+
+ public int getTimeout() {
+ return timeOut;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public JDBCConfiguration[] getJDBCbSourceArray() {
+ return configurations.toArray(new JDBCConfiguration[configurations.size()]);
+ }
+
+ public void addConfiguration(BaseDBConfiguration config) {
+ configurations.add(config);
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/JDBCConfiguration.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/JDBCConfiguration.java
new file mode 100644
index 000000000..fea103569
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/JDBCConfiguration.java
@@ -0,0 +1,31 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib.config;
+
+import java.util.Properties;
+
+public class JDBCConfiguration extends BaseDBConfiguration {
+
+ public JDBCConfiguration(Properties xmlElem) {
+ super(xmlElem);
+ }
+
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/TerminatingConfiguration.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/TerminatingConfiguration.java
new file mode 100755
index 000000000..1ebd1441f
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/config/TerminatingConfiguration.java
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Modifications Copyright (C) 2018 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib.config;
+
+import java.util.Properties;
+
+public class TerminatingConfiguration extends BaseDBConfiguration {
+
+ public TerminatingConfiguration() {
+ super(new Properties());
+ }
+
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/factory/DBConfigFactory.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/factory/DBConfigFactory.java
new file mode 100644
index 000000000..1aa907837
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/factory/DBConfigFactory.java
@@ -0,0 +1,105 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Modifications Copyright (C) 2018 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib.factory;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Properties;
+import org.onap.ccsdk.sli.core.dblib.config.BaseDBConfiguration;
+import org.onap.ccsdk.sli.core.dblib.config.DbConfigPool;
+import org.onap.ccsdk.sli.core.dblib.config.JDBCConfiguration;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @version $Revision: 1.1 $
+ * Change Log
+ * Author Date Comments
+ * ============== ======== ====================================================
+ * Rich Tabedzki 01/17/08 Initial version
+ */
+public class DBConfigFactory {
+
+ public static DbConfigPool createConfig(Properties resource) {
+ return getConfigparams(resource);
+ }
+
+ static DbConfigPool getConfigparams(Properties properties) {
+ DbConfigPool xmlConfig = new DbConfigPool(properties);
+ ArrayList<Properties> propertySets = new ArrayList<>();
+
+ if ("JDBC".equalsIgnoreCase(xmlConfig.getType())) {
+ String hosts = properties.getProperty(BaseDBConfiguration.DATABASE_HOSTS);
+ if (hosts == null || hosts.isEmpty()) {
+ propertySets.add(properties);
+ } else {
+ setPropertyWhenHostsNonEmpty(hosts, properties, propertySets);
+ }
+ } else {
+ propertySets.add(properties);
+ }
+ try {
+ Iterator<Properties> it = propertySets.iterator();
+ while (it.hasNext()) {
+ BaseDBConfiguration config = parse(it.next());
+ xmlConfig.addConfiguration(config);
+ }
+ } catch (Exception e) {
+ LoggerFactory.getLogger(DBConfigFactory.class).warn("", e);
+ }
+
+ return xmlConfig;
+ }
+
+ private static void setPropertyWhenHostsNonEmpty(String hosts, Properties properties, ArrayList<Properties>
+ propertySets) {
+ String[] newhost = hosts.split(",");
+ for (String aNewhost : newhost) {
+ Properties localSet = new Properties();
+ localSet.putAll(properties);
+ String url = localSet.getProperty(BaseDBConfiguration.DATABASE_URL);
+ if (url.contains("DBHOST")) {
+ url = url.replace("DBHOST", aNewhost);
+ }
+ if (url.contains("dbhost")) {
+ url = url.replace("dbhost", aNewhost);
+ }
+ localSet.setProperty(BaseDBConfiguration.DATABASE_URL, url);
+ localSet.setProperty(BaseDBConfiguration.CONNECTION_NAME, aNewhost);
+ propertySets.add(localSet);
+ }
+ }
+
+ public static BaseDBConfiguration parse(Properties props) throws Exception {
+
+ String type = props.getProperty(BaseDBConfiguration.DATABASE_TYPE);
+
+ BaseDBConfiguration config = null;
+
+ if ("JDBC".equalsIgnoreCase(type)) {
+ config = new JDBCConfiguration(props);
+ }
+
+ return config;
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/jdbc/JdbcDBCachedDataSource.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/jdbc/JdbcDBCachedDataSource.java
new file mode 100755
index 000000000..a53d18639
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/jdbc/JdbcDBCachedDataSource.java
@@ -0,0 +1,201 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib.jdbc;
+
+import java.sql.SQLFeatureNotSupportedException;
+import org.apache.tomcat.jdbc.pool.DataSource;
+import org.apache.tomcat.jdbc.pool.PoolProperties;
+import org.onap.ccsdk.sli.core.dblib.CachedDataSource;
+import org.onap.ccsdk.sli.core.dblib.DBConfigException;
+import org.onap.ccsdk.sli.core.dblib.config.BaseDBConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class JdbcDBCachedDataSource extends CachedDataSource {
+
+ private String dbDriver;
+ private String dbUserId;
+ private String dbPasswd;
+ private String dbUrl;
+
+ private int minLimit;
+ private int maxLimit;
+ private int initialLimit;
+
+ private static final String AS_CONF_ERROR = "AS_CONF_ERROR: ";
+ private static final int MIN_LIMIT = 5;
+ private static final int MAX_LIMIT = 10;
+ private static final int INITIAL_LIMIT = 10;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JdbcDBCachedDataSource.class);
+
+ /**
+ * @param jdbcElem
+ */
+ public JdbcDBCachedDataSource(BaseDBConfiguration jdbcElem) {
+ super(jdbcElem);
+ }
+
+ @Override
+ protected DataSource configure(BaseDBConfiguration xmlElem) throws DBConfigException {
+ BaseDBConfiguration jdbcConfig = xmlElem;
+ if (jdbcConfig.getConnTimeout() > 0) {
+ this.connReqTimeout = jdbcConfig.getConnTimeout();
+ }
+ if (jdbcConfig.getRequestTimeout() > 0) {
+ this.dataReqTimeout = jdbcConfig.getRequestTimeout();
+ }
+
+ // set connection pool name
+ String dbConnectionName = jdbcConfig.getDbConnectionName();
+ super.setDbConnectionName(dbConnectionName);
+ // Configure the JDBC connection
+ dbUserId = jdbcConfig.getDbUserId();
+ if (dbUserId == null) {
+ String errorMsg = "Invalid XML contents: JDBCConnection missing dbUserId attribute";
+ LOGGER.error(AS_CONF_ERROR + errorMsg);
+ throw new DBConfigException(errorMsg);
+ }
+
+ dbPasswd = jdbcConfig.getDbPasswd();
+ if (dbPasswd == null) {
+ String errorMsg = "Invalid XML contents: JDBCConnection missing dbPasswd attribute";
+ LOGGER.error(AS_CONF_ERROR + errorMsg);
+ throw new DBConfigException(errorMsg);
+ }
+
+ dbDriver = jdbcConfig.getDriverName();
+ if (dbDriver == null) {
+ String errorMsg = "Invalid XML contents: JDBCConnection missing dbDriver attribute";
+ LOGGER.error(AS_CONF_ERROR + errorMsg);
+ throw new DBConfigException(errorMsg);
+ }
+
+ minLimit = jdbcConfig.getDbMinLimit();
+ if (minLimit == -1)
+ {
+ String errorMsg = "Invalid XML contents: JDBC Connection missing minLimit attribute";
+ LOGGER.error(AS_CONF_ERROR + errorMsg);
+ minLimit = MIN_LIMIT;
+ }
+ maxLimit = jdbcConfig.getDbMaxLimit();
+ if (maxLimit == -1)
+ {
+ String errorMsg = "Invalid XML contents: JDBC Connection missing maxLimit attribute";
+ LOGGER.error(AS_CONF_ERROR + errorMsg);
+ maxLimit = MAX_LIMIT;
+ }
+ initialLimit = jdbcConfig.getDbInitialLimit();
+ if (initialLimit == -1)
+ {
+ String errorMsg = "Invalid XML contents: JDBC Connection missing initialLimit attribute";
+ LOGGER.error(AS_CONF_ERROR + errorMsg);
+ initialLimit = INITIAL_LIMIT;
+ }
+
+ dbUrl = jdbcConfig.getDbUrl();
+ if (dbUrl == null) {
+ String errorMsg = "Invalid XML contents: JDBCConnection missing dbUrl attribute";
+ LOGGER.error(AS_CONF_ERROR + errorMsg);
+ throw new DBConfigException(errorMsg);
+ }
+
+ try {
+
+ PoolProperties p = new PoolProperties();
+ p.setDriverClassName(dbDriver);
+ p.setUrl(dbUrl);
+ p.setUsername(dbUserId);
+ p.setPassword(dbPasswd);
+ p.setJmxEnabled(true);
+ p.setTestWhileIdle(false);
+ p.setTestOnBorrow(true);
+ p.setValidationQuery("SELECT 1");
+ p.setTestOnReturn(false);
+ p.setValidationInterval(30000);
+ p.setTimeBetweenEvictionRunsMillis(30000);
+ p.setInitialSize(initialLimit);
+ p.setMaxActive(maxLimit);
+ p.setMaxIdle(maxLimit);
+ p.setMaxWait(10000);
+ p.setRemoveAbandonedTimeout(60);
+ p.setMinEvictableIdleTimeMillis(30000);
+ p.setMinIdle(minLimit);
+ p.setLogAbandoned(true);
+ p.setRemoveAbandoned(true);
+ p.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"
+ + "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
+
+ final DataSource dataSource = new DataSource(p);
+
+ synchronized (this) {
+ initialized = true;
+ }
+ LOGGER.info(String.format("JdbcDBCachedDataSource <%s> configured successfully. Using URL: %s",
+ dbConnectionName, dbUrl));
+ return dataSource;
+ } catch (Exception exc) {
+ initialized = false;
+ LOGGER.error(String.format("AS_CONF_ERROR: Failed to initialize MySQLCachedDataSource <%s>. Reason: %s",
+ dbConnectionName, exc.getMessage()));
+ return null;
+ }
+ }
+
+ public final String getDbUrl() {
+ return dbUrl;
+ }
+
+ public final String getDbUserId() {
+ return dbUserId;
+ }
+
+ public final String getDbPasswd() {
+ return dbPasswd;
+ }
+
+ public static JdbcDBCachedDataSource createInstance(BaseDBConfiguration config) /*throws Exception*/ {
+ return new JdbcDBCachedDataSource(config);
+ }
+
+ public String toString() {
+ return getDbConnectionName();
+ }
+
+ public java.util.logging.Logger getParentLogger()
+ throws SQLFeatureNotSupportedException {
+ return null;
+ }
+
+ @Override
+ public void cleanUp() {
+ DataSource dataSource = (DataSource) ds;
+ dataSource.getPool().purge();
+ dataSource.close(true);
+ super.cleanUp();
+ }
+
+ @Override
+ protected int getAvailableConnections() {
+ return org.apache.tomcat.jdbc.pool.DataSource.class.cast(ds).getSize();
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/PollingWorker.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/PollingWorker.java
new file mode 100644
index 000000000..f66250676
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/PollingWorker.java
@@ -0,0 +1,259 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Modifications Copyright (C) 2018 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib.pm;
+
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeSet;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @version $Revision: 1.15 $
+ * Change Log
+ * Author Date Comments
+ * ============== ======== ====================================================
+ * Rich Tabedzki
+ */
+public class PollingWorker implements Runnable {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PollingWorker.class);
+
+ private static PollingWorker self = null;
+
+ private LinkedBlockingQueue tasks = new LinkedBlockingQueue(100);
+ private long interval = 1000L;
+ private Thread worker = null;
+ private AtomicLong[] counters = null;
+ private int[] bucketUnit = null;
+ private static boolean enabled = false;
+ private Timer timer = null;
+
+ private PollingWorker(Properties ctxprops) {
+ if (ctxprops == null || ctxprops.getProperty("org.onap.ccsdk.dblib.pm") == null) {
+ enabled = false;
+ } else {
+ if ("true".equalsIgnoreCase((String) ctxprops.getProperty("org.onap.ccsdk.dblib.pm"))) {
+ enabled = true;
+ } else {
+ enabled = false;
+ }
+ }
+
+ interval = Long.parseLong((ctxprops == null || ctxprops.getProperty("org.onap.ccsdk.dblib.pm.interval") == null)
+ ? "60" : (String) ctxprops.getProperty("org.onap.ccsdk.dblib.pm.interval"));
+ // '0' bucket is to count exceptions
+ String[] sampling = ((ctxprops == null || ctxprops.getProperty("org.onap.ccsdk.dblib.pm.sampling") == null)
+ ? "0,2,5,10,20,50,100" : (String) ctxprops.getProperty("org.onap.ccsdk.dblib.pm.sampling")).split(",");
+
+ if (enabled) {
+ bucketUnit = new int[sampling.length];
+ for (int i = 0, max = bucketUnit.length; i < max; i++) {
+ bucketUnit[i] = Integer.parseInt(sampling[i].trim());
+ }
+ counters = new AtomicLong[bucketUnit.length + 1];
+ for (int i = 0, max = counters.length; i < max; i++) {
+ counters[i] = new AtomicLong();
+ }
+ worker = new Thread(this);
+ worker.setDaemon(true);
+ worker.start();
+ timer = new Timer(true);
+ timer.schedule(new MyTimerTask(), interval * 1000L, interval * 1000L);
+ }
+ }
+
+ public static void post(long starttime) {
+ PollingWorker temp = self;
+ if (temp != null && enabled) {
+ temp.register(new TestSample(starttime));
+ }
+ }
+
+ public static void createInistance(Properties props) {
+ self = new PollingWorker(props);
+ }
+
+ private void register(TestSample object) {
+ try {
+ tasks.add(object);
+ } catch (Throwable exc) {
+ // if cannot add an object to the queue, do nothing
+ }
+ }
+
+ private void deRegister(TestSample object) {
+ tasks.remove(object);
+ }
+
+ @Override
+ public void run() {
+ for (;;) {
+ Set data = new TreeSet();
+ tasks.drainTo(data);
+ for (Iterator it = data.iterator(); it.hasNext();) {
+ Object next = it.next();
+ if (next instanceof TestSample) {
+ consume((TestSample) next);
+ } else {
+ System.out.println(next.getClass().getName());
+ LOGGER.error(next.getClass().getName());
+ }
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+
+ }
+ }
+
+ }
+
+ public void clearReqister() {
+ AtomicLong[] tmp = new AtomicLong[counters.length];
+ for (int i = 0, max = tmp.length; i < max; i++) {
+ tmp[i] = new AtomicLong();
+ }
+ AtomicLong[] tmp2 = counters;
+ synchronized (tmp2) {
+ counters = tmp;
+ }
+ StringBuffer sb = new StringBuffer("CPM: ");
+ for (int i = 0, max = tmp2.length; i < max; i++) {
+ if (i == 0 && bucketUnit[0] == 0) {
+ sb.append("[Exc]=");
+ } else {
+ sb.append("[");
+ if (i == bucketUnit.length) {
+ sb.append("Other]=");
+ } else {
+ sb.append(bucketUnit[i]).append(" ms]=");
+ }
+ }
+ sb.append(tmp2[i].get()).append("\t");
+ }
+ LOGGER.info(sb.toString());
+ }
+
+ class MyTimerTask extends TimerTask {
+
+ @Override
+ public void run() {
+
+ clearReqister();
+ }
+
+ }
+
+ private void consume(TestSample probe) {
+ AtomicLong[] tmp = counters;
+ synchronized (tmp) {
+ counters[getBucket(probe.getDuration())].incrementAndGet();
+ }
+ }
+
+ /*
+ * This method is used to find the offset of the bucket in counters.
+ * 'counters' array is 1 size longer than bucketUnit, hence by default it
+ * returns 'bucketUnit.length'
+ */
+ private int getBucket(long difftime) {
+ for (int i = 0; i < bucketUnit.length; i++) {
+ if (difftime < bucketUnit[i]) {
+ return i;
+ }
+ }
+ return bucketUnit.length;
+ }
+
+ private static boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * @author Rich Tabedzki
+ * A helper class to pass measured parameter to the counter.
+ */
+ static class TestSample implements Comparable {
+ private long starttime;
+ private long endtime;
+
+ public TestSample(long starttime) {
+ this.endtime = System.currentTimeMillis();
+ this.starttime = starttime;
+ }
+
+ public long getDuration() {
+ return endtime - starttime;
+ }
+
+ @Override
+ public int compareTo(Object o) {
+ if (o instanceof TestSample) {
+ TestSample x = (TestSample) o;
+ if (starttime < x.starttime)
+ return 1;
+ if (endtime < x.endtime)
+ return 1;
+ if (starttime > x.starttime)
+ return -1;
+ if (endtime > x.endtime)
+ return -1;
+ return 0;
+ }
+ return 1;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (endtime ^ (endtime >>> 32));
+ result = prime * result + (int) (starttime ^ (starttime >>> 32));
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ TestSample other = (TestSample) obj;
+ if (endtime != other.endtime)
+ return false;
+ if (starttime != other.starttime)
+ return false;
+ return true;
+ }
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/SQLExecutionMonitor.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/SQLExecutionMonitor.java
new file mode 100644
index 000000000..b6664f3c3
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/SQLExecutionMonitor.java
@@ -0,0 +1,222 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib.pm;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.SortedSet;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicLong;
+import org.onap.ccsdk.sli.core.dblib.DBResourceObserver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SQLExecutionMonitor extends Observable {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SQLExecutionMonitor.class);
+
+ static final long MILISECOND = 1000000L;
+ static final long SECOND = 1000L * MILISECOND;
+
+ private final Timer timer;
+ // collection
+ private final SortedSet<TestObject> innerSet;
+ private SQLExecutionMonitorObserver parent = null;
+ private final AtomicLong completionCounter;
+ private boolean activeState = false;
+ private final long interval;
+ private final long initialDelay;
+ private final long EXPECTED_TIME_TO_COMPLETE;
+ private final long UNPROCESSED_FAILOVER_THRESHOLD;
+
+ private final class MonitoringTask extends TimerTask {
+
+ @Override
+ public void run() {
+ try {
+ TestObject testObj = new TestObject();
+ testObj.setStartTime(testObj.getStartTime() - EXPECTED_TIME_TO_COMPLETE);
+
+ // take a snapshot of the current task list
+ TestObject[] array = innerSet.toArray(new TestObject[0]);
+ SortedSet<TestObject> copyCurrent = new TreeSet<>(Arrays.asList(array));
+ // get the list of the tasks that are older than the specified
+ // interval.
+ SortedSet<TestObject> unprocessed = copyCurrent.headSet(testObj);
+
+ long successfulCount = completionCounter.get();
+ int unprocessedCount = unprocessed.size();
+
+ if (!unprocessed.isEmpty() && unprocessedCount > UNPROCESSED_FAILOVER_THRESHOLD
+ && successfulCount == 0) {
+ // switch the Connection Pool to passive
+ setChanged();
+ notifyObservers("Open JDBC requests=" + unprocessedCount + " in " + SQLExecutionMonitor.this.parent
+ .getDbConnectionName());
+ }
+ } catch (Exception exc) {
+ LOGGER.error("", exc);
+ } finally {
+ completionCounter.set(0L);
+ }
+ }
+ }
+
+ public static class TestObject implements Comparable<TestObject>, Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private long startTime;
+ private long randId;
+
+ public TestObject() {
+ startTime = System.nanoTime();
+ }
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(long newTime) {
+ startTime = newTime;
+ }
+
+ @Override
+ public int compareTo(TestObject o) {
+ if (this == o) {
+ return 0;
+ }
+ if (this.startTime > o.getStartTime()) {
+ return 1;
+ }
+ if (this.startTime < o.getStartTime()) {
+ return -1;
+ }
+
+ if (this.hashCode() > o.hashCode()) {
+ return 1;
+ }
+ if (this.hashCode() < o.hashCode()) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ public String toString() {
+ return Long.toString(startTime) + "#" + this.hashCode();
+ }
+
+ public int hashCode() {
+ return Objects.hash(startTime, randId);
+ }
+
+ public boolean equals(Object obj) {
+ return this == obj || (obj instanceof TestObject && startTime == ((TestObject) obj).getStartTime()
+ && hashCode() == obj.hashCode());
+ }
+ }
+
+ public SQLExecutionMonitor(SQLExecutionMonitorObserver parent) {
+ this.parent = parent;
+ completionCounter = new AtomicLong(0L);
+ interval = parent.getInterval();
+ initialDelay = parent.getInitialDelay();
+ this.UNPROCESSED_FAILOVER_THRESHOLD = parent.getUnprocessedFailoverThreshold();
+ this.EXPECTED_TIME_TO_COMPLETE = parent.getExpectedCompletionTime() * MILISECOND;
+
+ innerSet = Collections.synchronizedSortedSet(new TreeSet<TestObject>());
+ timer = new Timer();
+ }
+
+ public void cleanup() {
+ timer.cancel();
+ }
+
+ // registerRequest
+ public TestObject registerRequest() {
+ if (activeState) {
+ TestObject test = new TestObject();
+ if (innerSet.add(test)) {
+ return test;
+ }
+ }
+ return null;
+ }
+
+ // deregisterSuccessfulRequest
+ public boolean deregisterRequest(TestObject test) {
+ if (test == null) {
+ return false;
+ }
+ // remove from the collection
+ if (innerSet.remove(test) && activeState) {
+ completionCounter.incrementAndGet();
+ return true;
+ }
+ return false;
+ }
+
+ public void terminate() {
+ timer.cancel();
+ }
+
+ /**
+ * @return the parent
+ */
+ public final Object getParent() {
+ return parent;
+ }
+
+ @Override
+ public void addObserver(Observer observer) {
+ if (observer instanceof DBResourceObserver) {
+ DBResourceObserver dbObserver = (DBResourceObserver) observer;
+ if (dbObserver.isMonitorDbResponse() && countObservers() == 0) {
+ TimerTask remindTask = new MonitoringTask();
+ timer.schedule(remindTask, initialDelay, interval);
+ activeState = true;
+ }
+ }
+ super.addObserver(observer);
+ }
+
+ @Override
+ public void deleteObserver(Observer observer) {
+ super.deleteObserver(observer);
+ if (observer instanceof DBResourceObserver) {
+ DBResourceObserver dbObserver = (DBResourceObserver) observer;
+ if (dbObserver.isMonitorDbResponse() && countObservers() == 0) {
+ timer.cancel();
+ activeState = false;
+ }
+ }
+ }
+
+ public final int getProcessedConnectionsCount() {
+ return innerSet.size();
+ }
+}
diff --git a/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/SQLExecutionMonitorObserver.java b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/SQLExecutionMonitorObserver.java
new file mode 100644
index 000000000..2fdde1ee4
--- /dev/null
+++ b/core/dblib/provider/src/main/java/org/onap/ccsdk/sli/core/dblib/pm/SQLExecutionMonitorObserver.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * onap
+ * ================================================================================
+ * Copyright (C) 2016 - 2017 ONAP
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ccsdk.sli.core.dblib.pm;
+
+public interface SQLExecutionMonitorObserver {
+ String getDbConnectionName();
+
+ long getInterval();
+ void setInterval(long value);
+
+ long getInitialDelay();
+ void setInitialDelay(long value);
+
+ long getExpectedCompletionTime();
+ void setExpectedCompletionTime(long value);
+
+ long getUnprocessedFailoverThreshold();
+ void setUnprocessedFailoverThreshold(long value);
+}
diff --git a/core/dblib/provider/src/main/resources/OSGI-INF/blueprint/dblib-blueprint.xml b/core/dblib/provider/src/main/resources/OSGI-INF/blueprint/dblib-blueprint.xml
new file mode 100755
index 000000000..f1d5166db
--- /dev/null
+++ b/core/dblib/provider/src/main/resources/OSGI-INF/blueprint/dblib-blueprint.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ odl:use-default-for-reference-types="true">
+
+ <bean id="provider" class="org.onap.ccsdk.sli.core.dblib.DBLIBResourceProvider" />
+
+ <bean id="dbResourceManager" class="org.onap.ccsdk.sli.core.dblib.DBResourceManager">
+ <argument ref="provider" />
+ </bean>
+ <service ref="dbResourceManager">
+ <interfaces>
+ <value>javax.sql.DataSource</value>
+ <value>org.onap.ccsdk.sli.core.dblib.DbLibService</value>
+ </interfaces>
+ </service>
+
+</blueprint> \ No newline at end of file
diff --git a/core/dblib/provider/src/main/resources/dblib.properties b/core/dblib/provider/src/main/resources/dblib.properties
new file mode 100755
index 000000000..e0399cc31
--- /dev/null
+++ b/core/dblib/provider/src/main/resources/dblib.properties
@@ -0,0 +1,14 @@
+org.onap.ccsdk.sli.dbtype=jdbc
+org.onap.ccsdk.sli.jdbc.hosts=sdnctldb01,sdnctldb02
+org.onap.ccsdk.sli.jdbc.url=jdbc:derby:memory:sdnctl;create=true
+org.onap.ccsdk.sli.jdbc.driver=org.apache.derby.jdbc.EmbeddedDriver
+org.onap.ccsdk.sli.jdbc.database=sdnctl
+org.onap.ccsdk.sli.jdbc.user=test
+org.onap.ccsdk.sli.jdbc.password=test
+org.onap.ccsdk.sli.jdbc.connection.name=sdnctldb01
+
+org.onap.ccsdk.sli.jdbc.connection.timeout=50
+org.onap.ccsdk.sli.jdbc.request.timeout=100
+org.onap.ccsdk.sli.jdbc.limit.init=10
+org.onap.ccsdk.sli.jdbc.limit.min=10
+org.onap.ccsdk.sli.jdbc.limit.max=20
diff --git a/core/dblib/provider/src/main/resources/org/opendaylight/blueprint/dblib-blueprint.xml b/core/dblib/provider/src/main/resources/org/opendaylight/blueprint/dblib-blueprint.xml
new file mode 100755
index 000000000..d9bb99c07
--- /dev/null
+++ b/core/dblib/provider/src/main/resources/org/opendaylight/blueprint/dblib-blueprint.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ odl:use-default-for-reference-types="true">
+
+ <bean id="provider" class="org.onap.ccsdk.sli.core.dblib.DBLIBResourceProvider" />
+
+ <bean id="dbResourceManager" class="org.onap.ccsdk.sli.core.dblib.DBResourceManager">
+ <argument ref="provider" />
+ </bean>
+ <service ref="dbResourceManager">
+ <interfaces>
+ <value>javax.sql.DataSource</value>
+ <value>org.onap.ccsdk.sli.core.dblib.DbLibService</value>
+ </interfaces>
+ </service>
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.0.0">
+ <command name="dblib/encode">
+ <action class="org.onap.ccsdk.sli.core.dblib.EncShellCommand"/>
+ </command>
+ </command-bundle>
+
+</blueprint> \ No newline at end of file
diff --git a/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/CachedDataSourceTest.java b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/CachedDataSourceTest.java
new file mode 100644
index 000000000..45268107e
--- /dev/null
+++ b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/CachedDataSourceTest.java
@@ -0,0 +1,121 @@
+package org.onap.ccsdk.sli.core.dblib;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.Properties;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.ccsdk.sli.core.dblib.config.BaseDBConfiguration;
+import org.onap.ccsdk.sli.core.dblib.config.JDBCConfiguration;
+import org.onap.ccsdk.sli.core.dblib.jdbc.JdbcDBCachedDataSource;
+import org.slf4j.LoggerFactory;
+
+public class CachedDataSourceTest {
+
+ private static final Properties props = new Properties();
+ private static BaseDBConfiguration config;
+ private static CachedDataSource ds;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ props.setProperty("org.onap.ccsdk.sli.dbtype", "jdbc");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.hosts", "localhost");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.url", "jdbc:mysql://dbhost:3306/test");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.driver", "org.mariadb.jdbc.Driver");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.database", "test");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.user", "dbuser");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.password", "passw0rd");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.connection.name", "testdb01");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.connection.timeout", "50");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.request.timeout", "100");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.limit.init", "10");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.limit.min", "10");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.limit.max", "20");
+ props.setProperty("org.onap.dblib.connection.recovery", "false");
+
+ config = new JDBCConfiguration(props);
+ ds = new JdbcDBCachedDataSource(config);
+ }
+
+ @Test
+ public void testCachedDataSource() {
+ assertNotNull(ds);
+ }
+
+ @Test
+ public void testConfigure() {
+
+ assertNotNull(ds.configure(config));
+ }
+
+ @Test
+ public void testSetInitialDelay() {
+ ds.setInitialDelay(1000L);
+ assertTrue(ds.getInitialDelay() == 1000L);
+ }
+
+ @Test
+ public void testSetInterval() {
+ ds.setInterval(1000L);
+ assertTrue(ds.getInterval() == 1000L);
+ }
+
+ @Test
+ public void testSetExpectedCompletionTime() {
+ ds.setExpectedCompletionTime(100L);
+ assertTrue(ds.getExpectedCompletionTime() == 100L);
+ }
+
+ @Test
+ public void testSetUnprocessedFailoverThreshold() {
+ ds.setUnprocessedFailoverThreshold(100L);
+ assertTrue(ds.getUnprocessedFailoverThreshold() == 100L);
+ }
+
+ @Test
+ public void testGetParentLogger() {
+ try {
+ assertNull(ds.getParentLogger());
+ } catch (SQLFeatureNotSupportedException e) {
+ LoggerFactory.getLogger(CachedDataSourceTest.class).warn("Test Failure", e);
+ }
+ }
+
+ @Test
+ public void testGettersForJdbcDBCachedDataSource() {
+
+ assertEquals("jdbc:mysql://dbhost:3306/test", ((JdbcDBCachedDataSource) ds).getDbUrl());
+ assertEquals("dbuser", ((JdbcDBCachedDataSource) ds).getDbUserId());
+ assertEquals("passw0rd", ((JdbcDBCachedDataSource) ds).getDbPasswd());
+ assertEquals("testdb01", ((JdbcDBCachedDataSource) ds).toString());
+ }
+
+ @Test
+ public void testIsInitialised() {
+ assertTrue(ds.isInitialized());
+ }
+
+ @Test
+ public void testIsWrapperFor() throws SQLException {
+ assertFalse(ds.isWrapperFor(CachedDataSource.class));
+ }
+
+ @Test
+ public void testGetSetNextErrorReportTime() throws SQLException {
+ ds.setNextErrorReportTime(1L);
+ assertEquals(1L, ds.getNextErrorReportTime());
+ }
+
+ @Test
+ public void testGetSetGlobalHostName() throws SQLException {
+ ds.setGlobalHostName("hostName");
+ assertEquals("hostName", ds.getGlobalHostName());
+ }
+} \ No newline at end of file
diff --git a/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/DBConfigExceptionTest.java b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/DBConfigExceptionTest.java
new file mode 100644
index 000000000..2a5b65c7a
--- /dev/null
+++ b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/DBConfigExceptionTest.java
@@ -0,0 +1,19 @@
+package org.onap.ccsdk.sli.core.dblib;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+
+public class DBConfigExceptionTest {
+
+ @Test
+ public void testDBConfigExceptionException() {
+ assertNotNull(new DBConfigException("JUnit Test"));
+ }
+
+ @Test
+ public void testDBConfigExceptionString() {
+ assertNotNull(new DBConfigException(new Exception("JUnit Test")));
+ }
+
+}
diff --git a/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/DblibConfigurationExceptionTest.java b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/DblibConfigurationExceptionTest.java
new file mode 100644
index 000000000..7becd1ae4
--- /dev/null
+++ b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/DblibConfigurationExceptionTest.java
@@ -0,0 +1,24 @@
+package org.onap.ccsdk.sli.core.dblib;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class DblibConfigurationExceptionTest {
+
+ @Test
+ public void testDblibConfigurationException() {
+ assertNotNull(new DblibConfigurationException());
+ }
+
+ @Test
+ public void testDblibConfigurationExceptionString() {
+ assertNotNull(new DblibConfigurationException("JUnit Test"));
+ }
+
+ @Test
+ public void testDblibConfigurationExceptionStringThrowable() {
+ assertNotNull(new DblibConfigurationException("JUnit Test", new Exception("JUnit Test")));
+ }
+
+}
diff --git a/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/NoAvailableConnectionsExceptionTest.java b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/NoAvailableConnectionsExceptionTest.java
new file mode 100644
index 000000000..2fdacb922
--- /dev/null
+++ b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/NoAvailableConnectionsExceptionTest.java
@@ -0,0 +1,16 @@
+package org.onap.ccsdk.sli.core.dblib;
+
+import static org.junit.Assert.*;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class NoAvailableConnectionsExceptionTest {
+
+ @Test
+ public void testNoAvailableConnectionsException() {
+ assertNotNull(new NoAvailableConnectionsException(new Exception("test")));
+ }
+
+}
diff --git a/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TerminatingCachedDataSourceTest.java b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TerminatingCachedDataSourceTest.java
new file mode 100644
index 000000000..160a3d4ae
--- /dev/null
+++ b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TerminatingCachedDataSourceTest.java
@@ -0,0 +1,94 @@
+package org.onap.ccsdk.sli.core.dblib;
+
+import static org.junit.Assert.*;
+
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.Properties;
+
+import org.junit.Test;
+import org.onap.ccsdk.sli.core.dblib.config.BaseDBConfiguration;
+import org.onap.ccsdk.sli.core.dblib.config.JDBCConfiguration;
+import org.slf4j.LoggerFactory;
+
+public class TerminatingCachedDataSourceTest {
+
+ @Test
+ public void testTerminatingCachedDataSource() {
+ Properties props = new Properties();
+ BaseDBConfiguration config = new JDBCConfiguration(props);
+ CachedDataSource ds = new TerminatingCachedDataSource(config);
+ assertNotNull(ds);
+ }
+
+ @Test
+ public void testConfigure() {
+ Properties props = new Properties();
+ props.setProperty("org.onap.ccsdk.sli.dbtype", "jdbc");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.hosts", "localhost");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.url", "jdbc:mysql://dbhost:3306/test");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.driver", "org.mariadb.jdbc.Driver");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.database", "test");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.user", "dbuser");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.password", "passw0rd");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.connection.name", "testdb01");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.connection.timeout", "50");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.request.timeout", "100");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.limit.init", "10");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.limit.min", "10");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.limit.max", "20");
+ props.setProperty("org.onap.dblib.connection.recovery", "false");
+ BaseDBConfiguration config = new JDBCConfiguration(props);
+
+ CachedDataSource ds = new TerminatingCachedDataSource(config);
+ assertNull(ds.configure(config));
+ }
+
+ @Test
+ public void testSetInitialDelay() {
+ Properties props = new Properties();
+ BaseDBConfiguration config = new JDBCConfiguration(props);
+ CachedDataSource ds = new TerminatingCachedDataSource(config);
+ ds.setInitialDelay(1000L);
+ assertTrue(ds.getInitialDelay() == 1000L);
+ }
+
+ @Test
+ public void testSetInterval() {
+ Properties props = new Properties();
+ BaseDBConfiguration config = new JDBCConfiguration(props);
+ CachedDataSource ds = new TerminatingCachedDataSource(config);
+ ds.setInterval(1000L);
+ assertTrue(ds.getInterval() == 1000L);
+ }
+
+ @Test
+ public void testSetExpectedCompletionTime() {
+ Properties props = new Properties();
+ BaseDBConfiguration config = new JDBCConfiguration(props);
+ CachedDataSource ds = new TerminatingCachedDataSource(config);
+ ds.setExpectedCompletionTime(100L);
+ assertTrue(ds.getExpectedCompletionTime() == 100L);
+ }
+
+ @Test
+ public void testSetUnprocessedFailoverThreshold() {
+ Properties props = new Properties();
+ BaseDBConfiguration config = new JDBCConfiguration(props);
+ CachedDataSource ds = new TerminatingCachedDataSource(config);
+ ds.setUnprocessedFailoverThreshold(100L);
+ assertTrue(ds.getUnprocessedFailoverThreshold() == 100L);
+ }
+
+ @Test
+ public void testGetParentLogger() {
+ Properties props = new Properties();
+ BaseDBConfiguration config = new JDBCConfiguration(props);
+ CachedDataSource ds = new TerminatingCachedDataSource(config);
+ ds.setInterval(100L);
+ try {
+ assertNull(ds.getParentLogger());
+ } catch (SQLFeatureNotSupportedException e) {
+ LoggerFactory.getLogger(TerminatingCachedDataSourceTest.class).warn("Test Failure", e);
+ }
+ }
+} \ No newline at end of file
diff --git a/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TestDBResourceManager.java b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TestDBResourceManager.java
new file mode 100644
index 000000000..8d7d34324
--- /dev/null
+++ b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TestDBResourceManager.java
@@ -0,0 +1,70 @@
+package org.onap.ccsdk.sli.core.dblib;
+
+import static org.junit.Assert.*;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import ch.vorburger.mariadb4j.DB;
+import ch.vorburger.mariadb4j.DBConfigurationBuilder;
+
+public class TestDBResourceManager {
+
+ DbLibService dblibSvc;
+ DBResourceManager dbm;
+
+ @Before
+ public void setUp() throws Exception {
+ URL propUrl = getClass().getResource("/dblib.properties");
+
+ InputStream propStr = getClass().getResourceAsStream("/dblib.properties");
+
+ Properties props = new Properties();
+
+ props.load(propStr);
+
+ // Start MariaDB4j database
+ DBConfigurationBuilder config = DBConfigurationBuilder.newBuilder();
+ config.setPort(0); // 0 => autom. detect free port
+ DB db = DB.newEmbeddedDB(config.build());
+ db.start();
+
+ // Override jdbc URL and database name
+ props.setProperty("org.onap.ccsdk.sli.jdbc.database", "test");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.url", config.getURL("test"));
+
+ dblibSvc = new DBResourceManager(props);
+ dbm = new DBResourceManager(props);
+ dblibSvc.writeData("CREATE TABLE DBLIB_TEST (name varchar(20));", null, null);
+ dblibSvc.getData("SELECT * FROM DBLIB_TEST", null, null);
+
+ }
+
+ @Test
+ public void testForceRecovery() {
+ dbm.testForceRecovery();
+ }
+
+ @Test
+ public void testGetConnection() throws SQLException {
+ assertNotNull(dbm.getConnection());
+ assertNotNull(dbm.getConnection("testUser", "testPaswd"));
+ }
+
+ @Test
+ public void testCleanup() {
+ dbm.cleanUp();
+
+ }
+
+ @Test
+ public void testGetLogWriter() throws SQLException {
+ assertNull(dbm.getLogWriter());
+ }
+
+}
diff --git a/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TestDBResourceManager2.java b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TestDBResourceManager2.java
new file mode 100644
index 000000000..a3cb8d914
--- /dev/null
+++ b/core/dblib/provider/src/test/java/org/onap/ccsdk/sli/core/dblib/TestDBResourceManager2.java
@@ -0,0 +1,95 @@
+/*-
+ 2 * ============LICENSE_START=======================================================
+ 3 * ONAP CCSDK
+ 4 * ================================================================================
+ 5 * Copyright (C) 2019 AT&T Intellectual Property. All rights
+ 6 * 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.sli.core.dblib;
+
+import static org.junit.Assert.*;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import ch.vorburger.mariadb4j.DB;
+import ch.vorburger.mariadb4j.DBConfigurationBuilder;
+
+public class TestDBResourceManager2 {
+
+ DbLibService dblibSvc;
+ DBResourceManager dbm;
+
+ @Before
+ public void setUp() throws Exception {
+ URL propUrl = getClass().getResource("/dblib.properties");
+
+ InputStream propStr = getClass().getResourceAsStream("/dblib.properties");
+
+ Properties props = new Properties();
+
+ props.load(propStr);
+
+ // Start MariaDB4j database
+ DBConfigurationBuilder config = DBConfigurationBuilder.newBuilder();
+ config.setPort(0); // 0 => autom. detect free port
+ DB db = DB.newEmbeddedDB(config.build());
+ db.start();
+
+ // Override jdbc URL, database name, and recovery
+ props.setProperty("org.onap.ccsdk.sli.jdbc.database", "test");
+ props.setProperty("org.onap.ccsdk.sli.jdbc.url", config.getURL("test"));
+ props.setProperty("org.onap.dblib.connection.recovery", "true");
+
+
+ dblibSvc = new DBResourceManager(props);
+ dbm = new DBResourceManager(props);
+ dblibSvc.writeData("CREATE TABLE DBLIB_TEST2 (name varchar(20));", null, null);
+ dblibSvc.getData("SELECT * FROM DBLIB_TEST2", null, null);
+
+
+ }
+
+ @Test
+ public void testForceRecovery() {
+ dbm.testForceRecovery();
+ }
+
+ @Test
+ public void testGetConnection() throws SQLException {
+ assertNotNull(dbm.getConnection());
+ assertNotNull(dbm.getConnection("testUser", "testPaswd"));
+ }
+
+ @Test
+ public void testCleanup() {
+ dbm.cleanUp();
+
+ }
+
+ @Test
+ public void testGetLogWriter() throws SQLException {
+ assertNull(dbm.getLogWriter());
+ }
+
+}
diff --git a/core/dblib/provider/src/test/resources/dblib.properties b/core/dblib/provider/src/test/resources/dblib.properties
new file mode 100644
index 000000000..9506ac8d1
--- /dev/null
+++ b/core/dblib/provider/src/test/resources/dblib.properties
@@ -0,0 +1,38 @@
+###
+# ============LICENSE_START=======================================================
+# openECOMP : SDN-C
+# ================================================================================
+# Copyright (C) 2017 AT&T Intellectual Property. All rights
+# reserved.
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=========================================================
+###
+
+# dblib.properrties
+org.onap.ccsdk.sli.dbtype=jdbc
+
+org.onap.ccsdk.sli.jdbc.hosts=sdnctldb01
+org.onap.ccsdk.sli.jdbc.url=jdbc:mysql://dbhost:3306/sdnctl
+org.onap.ccsdk.sli.jdbc.driver=org.mariadb.jdbc.Driver
+org.onap.ccsdk.sli.jdbc.database=sdnctl
+org.onap.ccsdk.sli.jdbc.user=sdnctl
+org.onap.ccsdk.sli.jdbc.password=gamma
+org.onap.ccsdk.sli.jdbc.connection.name=sdnctldb01
+org.onap.ccsdk.sli.jdbc.connection.timeout=50
+org.onap.ccsdk.sli.jdbc.request.timeout=100
+org.onap.ccsdk.sli.jdbc.limit.init=10
+org.onap.ccsdk.sli.jdbc.limit.min=10
+org.onap.ccsdk.sli.jdbc.limit.max=20
+org.onap.dblib.connection.recovery=false
+