2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2016 - 2017 AT&T
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.openecomp.sdnc.sli.resource.dblib;
23 import java.io.PrintWriter;
24 import java.sql.Connection;
25 import java.sql.SQLDataException;
26 import java.sql.SQLException;
27 import java.sql.SQLFeatureNotSupportedException;
28 import java.sql.SQLIntegrityConstraintViolationException;
29 import java.sql.SQLSyntaxErrorException;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Comparator;
33 import java.util.HashSet;
34 import java.util.Iterator;
35 import java.util.LinkedList;
36 import java.util.Observable;
37 import java.util.PriorityQueue;
38 import java.util.Properties;
39 import java.util.Queue;
41 import java.util.concurrent.ConcurrentLinkedQueue;
42 import java.util.concurrent.atomic.AtomicBoolean;
44 import javax.sql.DataSource;
45 import javax.sql.rowset.CachedRowSet;
47 import org.openecomp.sdnc.sli.resource.dblib.config.DbConfigPool;
48 import org.openecomp.sdnc.sli.resource.dblib.factory.AbstractDBResourceManagerFactory;
49 import org.openecomp.sdnc.sli.resource.dblib.factory.AbstractResourceManagerFactory;
50 import org.openecomp.sdnc.sli.resource.dblib.factory.DBConfigFactory;
51 import org.openecomp.sdnc.sli.resource.dblib.pm.PollingWorker;
52 import org.openecomp.sdnc.sli.resource.dblib.pm.SQLExecutionMonitor;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
56 import com.sun.rowset.providers.RIOptimisticProvider;
59 * @version $Revision: 1.15 $
61 * Author Date Comments
62 * ============== ======== ====================================================
65 public class DBResourceManager implements DataSource, DataAccessor, DBResourceObserver, DbLibService {
66 private static Logger LOGGER = LoggerFactory.getLogger(DBResourceManager.class);
68 transient boolean terminating = false;
69 transient protected long retryInterval = 10000L;
70 transient boolean recoveryMode = true;
72 protected final AtomicBoolean dsSelector = new AtomicBoolean();
74 // Queue<CachedDataSource> dsQueue = new ConcurrentLinkedQueue<CachedDataSource>();
75 Queue<CachedDataSource> dsQueue = new PriorityQueue<CachedDataSource>(4, new Comparator<CachedDataSource>(){
78 public int compare(CachedDataSource left, CachedDataSource right) {
85 } catch (Throwable e) {
92 protected final Set<CachedDataSource> broken = Collections.synchronizedSet(new HashSet<CachedDataSource>());
93 protected final Object monitor = new Object();
94 protected final Properties configProps;
95 protected final Thread worker;
97 protected final long terminationTimeOut;
98 protected final boolean monitorDbResponse;
99 protected final long monitoringInterval;
100 protected final long monitoringInitialDelay;
101 protected final long expectedCompletionTime;
102 protected final long unprocessedFailoverThreshold;
104 public DBResourceManager(Properties props){
105 this.configProps = props;
107 // get retry interval value
108 retryInterval = getLongFromProperties(props, "org.openecomp.dblib.connection.retry", 10000L);
110 // get recovery mode flag
111 recoveryMode = getBooleanFromProperties(props, "org.openecomp.dblib.connection.recovery", true);
114 recoveryMode = false;
115 LOGGER.info("Recovery Mode disabled");
117 // get time out value for thread cleanup
118 terminationTimeOut = getLongFromProperties(props, "org.openecomp.dblib.termination.timeout", 300000L);
119 // get properties for monitoring
120 monitorDbResponse = getBooleanFromProperties(props, "org.openecomp.dblib.connection.monitor", false);
121 monitoringInterval = getLongFromProperties(props, "org.openecomp.dblib.connection.monitor.interval", 1000L);
122 monitoringInitialDelay = getLongFromProperties(props, "org.openecomp.dblib.connection.monitor.startdelay", 5000L);
123 expectedCompletionTime = getLongFromProperties(props, "org.openecomp.dblib.connection.monitor.expectedcompletiontime", 5000L);
124 unprocessedFailoverThreshold = getLongFromProperties(props, "org.openecomp.dblib.connection.monitor.unprocessedfailoverthreshold", 3L);
126 // initialize performance monitor
127 PollingWorker.createInistance(props);
129 // initialize recovery thread
130 worker = new RecoveryMgr();
131 worker.setName("DBResourcemanagerWatchThread");
132 worker.setDaemon(true);
136 RIOptimisticProvider rio = new RIOptimisticProvider();
137 LOGGER.info("Class " + rio.getClass().getName() + " found.");
138 Class clas = this.getClass().getClassLoader().loadClass("com.sun.rowset.providers.RIOptimisticProvider");
140 LOGGER.info("Class " + rio.getClass().getName() + " found by classloader.");
142 } catch(Exception exc) {
143 LOGGER.info("Failed resolving RIOptimisticProvider class", exc);
147 private void config(Properties ctx) throws Exception {
149 DbConfigPool dbConfig = DBConfigFactory.createConfig(this.configProps);
152 AbstractResourceManagerFactory factory = AbstractDBResourceManagerFactory.getFactory(dbConfig.getType());
153 if(LOGGER.isInfoEnabled()){
154 LOGGER.info("Default DB config is : " + dbConfig.getType());
155 LOGGER.info("Using factory : " + factory.getClass().getName());
157 CachedDataSource[] cachedDS = factory.initDBResourceManager(dbConfig, this);
158 if(cachedDS == null || cachedDS.length == 0) {
159 LOGGER.error("Initialization of CachedDataSources failed. No instance was created.");
160 throw new Exception("Failed to initialize DB Library. No data source was created.");
163 for(int i=0; i<cachedDS.length; i++){
164 if(cachedDS[i] != null && cachedDS[i].isInitialized()){
165 setDataSource(cachedDS[i]);
166 cachedDS[i].setInterval(monitoringInterval);
167 cachedDS[i].setInitialDelay(monitoringInitialDelay);
168 cachedDS[i].setExpectedCompletionTime(expectedCompletionTime);
169 cachedDS[i].setUnprocessedFailoverThreshold(unprocessedFailoverThreshold);
170 cachedDS[i].addObserver(this);
174 } catch(Exception exc){
179 private long getLongFromProperties(Properties props, String property, long defaultValue)
182 long tmpLongValue = defaultValue;
184 value = (String)props.getProperty(property);
186 tmpLongValue = Long.parseLong(value);
188 } catch(NumberFormatException exc) {
189 if(LOGGER.isWarnEnabled()){
190 LOGGER.warn("'"+property+"'=" + value+" is invalid. It should be a numeric value");
192 } catch(Exception exc) {
198 private boolean getBooleanFromProperties(Properties props, String property, boolean defaultValue)
200 boolean tmpValue = defaultValue;
204 value = (String)props.getProperty(property);
206 tmpValue = Boolean.parseBoolean(value);
208 } catch(NumberFormatException exc) {
209 if(LOGGER.isWarnEnabled()){
210 LOGGER.warn("'"+property+"'=" + value+" is invalid. It should be a boolean value");
212 } catch(Exception exc) {
219 public void update(Observable observable, Object data) {
220 // if observable is active and there is a standby available, switch
221 if(observable instanceof SQLExecutionMonitor)
223 SQLExecutionMonitor monitor = (SQLExecutionMonitor)observable;
224 if(monitor.getParent() instanceof CachedDataSource)
226 CachedDataSource dataSource = (CachedDataSource)monitor.getParent();
227 if(dataSource == dsQueue.peek())
229 if(recoveryMode && dsQueue.size() > 1){
230 handleGetConnectionException(dataSource, new Exception(data.toString()));
237 public void testForceRecovery()
239 CachedDataSource active = (CachedDataSource) this.dsQueue.peek();
240 handleGetConnectionException(active, new Exception("test"));
243 class RecoveryMgr extends Thread {
249 Thread.sleep(retryInterval);
250 } catch (InterruptedException e1) { }
251 CachedDataSource brokenSource = null;
253 if (!broken.isEmpty()) {
254 CachedDataSource[] sourceArray = broken.toArray(new CachedDataSource[0]);
255 for (int i = 0; i < sourceArray.length; i++)
257 brokenSource = sourceArray[i];
258 if (brokenSource instanceof TerminatingCachedDataSource)
260 if (resetConnectionPool(brokenSource)) {
261 broken.remove(brokenSource);
262 brokenSource.blockImmediateOffLine();
263 dsQueue.add(brokenSource);
264 LOGGER.info("DataSource <"
265 + brokenSource.getDbConnectionName()
271 } catch (Exception exc) {
272 LOGGER.warn(exc.getMessage());
273 if(brokenSource != null){
275 if(!broken.contains(brokenSource))
276 broken.add(brokenSource);
278 } catch (Exception e1) { }
282 LOGGER.info("DBResourceManager.RecoveryMgr <"+this.toString() +"> terminated." );
285 private boolean resetConnectionPool(CachedDataSource dataSource){
287 return dataSource.testConnection();
288 } catch (Exception exc) {
289 LOGGER.info("DataSource <" + dataSource.getDbConnectionName() + "> resetCache failed with error: "+ exc.getMessage());
296 * @see org.openecomp.dblib.DataAccessor#getData(java.lang.String, java.util.ArrayList)
299 * @see org.openecomp.sdnc.sli.resource.dblib.DbLibService#getData(java.lang.String, java.util.ArrayList, java.lang.String)
302 public CachedRowSet getData(String statement, ArrayList<String> arguments, String preferredDS) throws SQLException {
304 return requestDataWithRecovery(statement, arguments, preferredDS);
306 return requestDataNoRecovery(statement, arguments, preferredDS);
309 private CachedRowSet requestDataWithRecovery(String statement, ArrayList<String> arguments, String preferredDS) throws SQLException {
310 Throwable lastException = null;
311 CachedDataSource active = null;
313 // test if there are any connection pools available
314 LinkedList<CachedDataSource> sources = new LinkedList<CachedDataSource>(this.dsQueue);
315 if(sources.isEmpty()){
316 LOGGER.error("Generated alarm: DBResourceManager.getData - No active DB connection pools are available.");
317 throw new DBLibException("No active DB connection pools are available in RequestDataWithRecovery call.");
319 if(preferredDS != null && !sources.peek().getDbConnectionName().equals(preferredDS)) {
320 Collections.reverse(sources);
324 // loop through available data sources to retrieve data.
325 while(!sources.isEmpty())
327 active = sources.peek();
329 long time = System.currentTimeMillis();
331 if(!active.isFabric()) {
332 CachedDataSource master = findMaster();
338 sources.remove(active);
339 return active.getData(statement, arguments);
340 } catch(SQLDataException exc){
342 } catch(SQLSyntaxErrorException exc){
344 } catch(SQLIntegrityConstraintViolationException exc){
346 } catch(Throwable exc){
348 String message = exc.getMessage();
349 if(message == null) {
350 if(exc.getCause() != null) {
351 message = exc.getCause().getMessage();
354 message = exc.getClass().getName();
356 LOGGER.error("Generated alarm: "+active.getDbConnectionName()+" - "+message);
357 handleGetConnectionException(active, exc);
359 if(LOGGER.isDebugEnabled()){
360 time = (System.currentTimeMillis() - time);
361 LOGGER.debug(">> getData : "+ active.getDbConnectionName()+" "+time+" miliseconds.");
365 if(lastException instanceof SQLException){
366 throw (SQLException)lastException;
368 // repackage the exception
369 // you are here because either you run out of available data sources
370 // or the last exception was not of SQLException type.
371 // repackage the exception
372 if(lastException == null) {
373 throw new DBLibException("The operation timed out while waiting to acquire a new connection." );
375 SQLException exception = new DBLibException(lastException.getMessage());
376 exception.setStackTrace(lastException.getStackTrace());
377 if(lastException.getCause() instanceof SQLException) {
378 throw (SQLException)lastException.getCause();
384 private CachedRowSet requestDataNoRecovery(String statement, ArrayList<String> arguments, String preferredDS) throws SQLException {
385 if(dsQueue.isEmpty()){
386 LOGGER.error("Generated alarm: DBResourceManager.getData - No active DB connection pools are available.");
387 throw new DBLibException("No active DB connection pools are available in RequestDataNoRecovery call.");
389 CachedDataSource active = (CachedDataSource) this.dsQueue.peek();
390 long time = System.currentTimeMillis();
392 if(!active.isFabric()) {
393 CachedDataSource master = findMaster();
397 return active.getData(statement, arguments);
398 // } catch(SQLDataException exc){
400 } catch(Throwable exc){
401 String message = exc.getMessage();
403 message = exc.getClass().getName();
404 LOGGER.error("Generated alarm: "+active.getDbConnectionName()+" - "+message);
405 if(exc instanceof SQLException)
406 throw (SQLException)exc;
408 DBLibException excptn = new DBLibException(exc.getMessage());
409 excptn.setStackTrace(exc.getStackTrace());
413 if(LOGGER.isDebugEnabled()){
414 time = (System.currentTimeMillis() - time);
415 LOGGER.debug(">> getData : "+ active.getDbConnectionName()+" "+time+" miliseconds.");
422 * @see org.openecomp.dblib.DataAccessor#writeData(java.lang.String, java.util.ArrayList)
425 * @see org.openecomp.sdnc.sli.resource.dblib.DbLibService#writeData(java.lang.String, java.util.ArrayList, java.lang.String)
428 public boolean writeData(String statement, ArrayList<String> arguments, String preferredDS) throws SQLException {
429 return writeDataNoRecovery(statement, arguments, preferredDS);
432 CachedDataSource findMaster() {
433 CachedDataSource master = null;
434 CachedDataSource[] dss = this.dsQueue.toArray(new CachedDataSource[0]);
435 for(int i=0; i<dss.length; i++) {
436 if(!dss[i].isSlave()) {
439 dsQueue.remove(master);
446 LOGGER.warn("MASTER not found.");
452 private boolean writeDataNoRecovery(String statement, ArrayList<String> arguments, String preferredDS) throws SQLException {
453 if(dsQueue.isEmpty()){
454 LOGGER.error("Generated alarm: DBResourceManager.getData - No active DB connection pools are available.");
455 throw new DBLibException("No active DB connection pools are available in RequestDataNoRecovery call.");
458 boolean initialRequest = true;
459 boolean retryAllowed = true;
460 CachedDataSource active = (CachedDataSource) this.dsQueue.peek();
461 long time = System.currentTimeMillis();
462 while(initialRequest) {
463 initialRequest = false;
465 if(!active.isFabric()) {
466 CachedDataSource master = findMaster();
472 return active.writeData(statement, arguments);
473 } catch(Throwable exc){
474 String message = exc.getMessage();
476 message = exc.getClass().getName();
477 LOGGER.error("Generated alarm: "+active.getDbConnectionName()+" - "+message);
478 if(exc instanceof SQLException) {
479 SQLException sqlExc = SQLException.class.cast(exc);
480 // handle read-only exception
481 if(sqlExc.getErrorCode() == 1290 && "HY000".equals(sqlExc.getSQLState())) {
482 LOGGER.warn("retrying due to: " + sqlExc.getMessage());
483 dsQueue.remove(active);
486 retryAllowed = false;
487 initialRequest = true;
491 throw (SQLException)exc;
493 DBLibException excptn = new DBLibException(exc.getMessage());
494 excptn.setStackTrace(exc.getStackTrace());
498 if(LOGGER.isDebugEnabled()){
499 time = (System.currentTimeMillis() - time);
500 LOGGER.debug(">> getData : "+ active.getDbConnectionName()+" "+time+" miliseconds.");
507 private void setDataSource(CachedDataSource dataSource) {
508 if(dataSource.testConnection(true)){
509 this.dsQueue.add(dataSource);
511 this.broken.add(dataSource);
515 public Connection getConnection() throws SQLException {
516 Throwable lastException = null;
517 CachedDataSource active = null;
519 if(dsQueue.isEmpty()){
520 throw new DBLibException("No active DB connection pools are available in GetConnection call.");
524 active = dsQueue.peek();
525 CachedDataSource tmpActive = findMaster();
526 if(tmpActive != null) {
529 return active.getConnection();
530 } catch(javax.sql.rowset.spi.SyncFactoryException exc){
531 LOGGER.debug("Free memory (bytes): " + Runtime.getRuntime().freeMemory());
532 LOGGER.warn("CLASSPATH issue. Allowing retry", exc);
534 } catch(Exception exc){
537 handleGetConnectionException(active, exc);
539 if(exc instanceof SQLException)
540 throw (SQLException)exc;
542 DBLibException excptn = new DBLibException(exc.getMessage());
543 excptn.setStackTrace(exc.getStackTrace());
547 } catch (Throwable trwb) {
548 DBLibException excptn = new DBLibException(trwb.getMessage());
549 excptn.setStackTrace(trwb.getStackTrace());
552 if(LOGGER.isDebugEnabled()){
557 if(lastException instanceof SQLException){
558 throw (SQLException)lastException;
560 // repackage the exception
561 if(lastException == null) {
562 throw new DBLibException("The operation timed out while waiting to acquire a new connection." );
564 SQLException exception = new DBLibException(lastException.getMessage());
565 exception.setStackTrace(lastException.getStackTrace());
566 if(lastException.getCause() instanceof SQLException) {
567 // exception.setNextException((SQLException)lastException.getCause());
568 throw (SQLException)lastException.getCause();
574 public Connection getConnection(String username, String password)
575 throws SQLException {
576 CachedDataSource active = null;
578 if(dsQueue.isEmpty()){
579 throw new DBLibException("No active DB connection pools are available in GetConnection call.");
584 active = dsQueue.peek();
585 CachedDataSource tmpActive = findMaster();
586 if(tmpActive != null) {
589 return active.getConnection(username, password);
590 } catch(Throwable exc){
592 handleGetConnectionException(active, exc);
594 if(exc instanceof SQLException)
595 throw (SQLException)exc;
597 DBLibException excptn = new DBLibException(exc.getMessage());
598 excptn.setStackTrace(exc.getStackTrace());
605 throw new DBLibException("No connections available in DBResourceManager in GetConnection call.");
608 private void handleGetConnectionException(CachedDataSource source, Throwable exc) {
610 if(!source.canTakeOffLine())
612 LOGGER.error("Could not switch due to blocking");
616 boolean removed = dsQueue.remove(source);
617 if(!broken.contains(source))
619 if(broken.add(source))
621 LOGGER.warn("DB Recovery: DataSource <" + source.getDbConnectionName() + "> put in the recovery mode. Reason : " + exc.getMessage());
623 LOGGER.warn("Error putting DataSource <" +source.getDbConnectionName()+ "> in recovery mode.");
626 LOGGER.info("DB Recovery: DataSource <" + source.getDbConnectionName() + "> already in recovery queue");
630 if(!dsQueue.isEmpty())
632 LOGGER.warn("DB DataSource <" + dsQueue.peek().getDbConnectionName() + "> became active");
635 } catch (Exception e) {
640 public void cleanUp() {
641 for(Iterator<CachedDataSource> it=dsQueue.iterator();it.hasNext();){
642 CachedDataSource cds = (CachedDataSource)it.next();
648 this.terminating = true;
652 broken.add( new TerminatingCachedDataSource(null));
653 } catch(Exception exc){
654 LOGGER.error("Waiting for Worker to stop", exc);
657 worker.join(terminationTimeOut);
658 LOGGER.info("DBResourceManager.RecoveryMgr <"+worker.toString() +"> termination was successful: " + worker.getState());
659 } catch(Exception exc){
660 LOGGER.error("Waiting for Worker thread to terminate ", exc);
664 public static DBResourceManager create(Properties props) throws Exception {
665 DBResourceManager dbmanager = new DBResourceManager(props);
666 dbmanager.config(props);
670 public PrintWriter getLogWriter() throws SQLException {
671 return ((CachedDataSource)this.dsQueue.peek()).getLogWriter();
674 public int getLoginTimeout() throws SQLException {
675 return ((CachedDataSource)this.dsQueue.peek()).getLoginTimeout();
678 public void setLogWriter(PrintWriter out) throws SQLException {
679 ((CachedDataSource)this.dsQueue.peek()).setLogWriter(out);
682 public void setLoginTimeout(int seconds) throws SQLException {
683 ((CachedDataSource)this.dsQueue.peek()).setLoginTimeout(seconds);
686 public void displayState(){
687 if(LOGGER.isDebugEnabled()){
688 LOGGER.debug("POOLS : Active = "+dsQueue.size() + ";\t Broken = "+broken.size());
689 CachedDataSource current = (CachedDataSource)dsQueue.peek();
690 if(current != null) {
691 LOGGER.debug("POOL : Active name = \'"+current.getDbConnectionName()+ "\'");
697 * @see org.openecomp.sdnc.sli.resource.dblib.DbLibService#isActive()
700 public boolean isActive() {
701 return this.dsQueue.size()>0;
704 public String getActiveStatus(){
705 return "Connected: " + dsQueue.size()+"\tIn-recovery: "+broken.size();
708 public String getDBStatus(boolean htmlFormat) {
709 StringBuilder buffer = new StringBuilder();
711 ArrayList<CachedDataSource> list = new ArrayList<CachedDataSource>();
712 list.addAll(dsQueue);
716 buffer.append("<tr class=\"headerRow\"><th id=\"header1\">")
717 .append("Name:").append("</th>");
718 for (int i = 0; i < list.size(); i++) {
719 buffer.append("<th id=\"header").append(2 + i).append("\">");
720 buffer.append(((CachedDataSource) list.get(i)).getDbConnectionName()).append("</th>");
722 buffer.append("</tr>");
724 buffer.append("<tr><td>State:</td>");
725 for (int i = 0; i < list.size(); i++) {
726 if (broken.contains(list.get(i))) {
727 buffer.append("<td>in recovery</td>");
729 if (dsQueue.contains(list.get(i))) {
730 if (dsQueue.peek() == list.get(i))
731 buffer.append("<td>active</td>");
733 buffer.append("<td>standby</td>");
736 buffer.append("</tr>");
739 for (int i = 0; i < list.size(); i++) {
740 buffer.append("Name: ").append(((CachedDataSource) list.get(i)).getDbConnectionName());
741 buffer.append("\tState: ");
742 if (broken.contains(list.get(i))) {
743 buffer.append("in recovery");
745 if (dsQueue.contains(list.get(i))) {
746 if (dsQueue.peek() == list.get(i))
747 buffer.append("active");
749 buffer.append("standby");
756 return buffer.toString();
759 public boolean isWrapperFor(Class<?> iface) throws SQLException {
763 public <T> T unwrap(Class<T> iface) throws SQLException {
768 * @return the monitorDbResponse
770 public final boolean isMonitorDbResponse() {
771 return recoveryMode && monitorDbResponse;
775 CachedDataSource obj = dsQueue.peek();
776 Exception ption = new Exception();
778 for(int i=0; i<5; i++)
780 handleGetConnectionException(obj, ption);
782 } catch(Throwable exc){
783 LOGGER.warn("", exc);
787 public String getPreferredDSName(){
789 return getPreferredDataSourceName(dsSelector);
794 private String getPreferredDataSourceName(AtomicBoolean flipper) {
796 LinkedList<CachedDataSource> snapshot = new LinkedList<CachedDataSource>(dsQueue);
797 if(snapshot.size() > 1){
798 CachedDataSource first = snapshot.getFirst();
799 CachedDataSource last = snapshot.getLast();
801 int delta = first.getMonitor().getPorcessedConnectionsCount() - last.getMonitor().getPorcessedConnectionsCount();
804 } else if(delta > 0) {
807 // check the last value and return !last
808 flipper.getAndSet(!flipper.get());
812 Collections.reverse(snapshot);
814 return snapshot.peek().getDbConnectionName();
817 public java.util.logging.Logger getParentLogger()
818 throws SQLFeatureNotSupportedException {
819 // TODO Auto-generated method stub
823 public String getMasterName() {
825 return getMasterDataSourceName(dsSelector);
831 private String getMasterDataSourceName(AtomicBoolean flipper) {
833 LinkedList<CachedDataSource> snapshot = new LinkedList<CachedDataSource>(dsQueue);
834 if(snapshot.size() > 1){
835 CachedDataSource first = snapshot.getFirst();
836 CachedDataSource last = snapshot.getLast();
838 int delta = first.getMonitor().getPorcessedConnectionsCount() - last.getMonitor().getPorcessedConnectionsCount();
841 } else if(delta > 0) {
844 // check the last value and return !last
845 flipper.getAndSet(!flipper.get());
849 Collections.reverse(snapshot);
851 return snapshot.peek().getDbConnectionName();
855 private void runTest(){
856 Thread producer = null;
858 producer = new ProducerThread("Prod1");
859 producer.setDaemon(true);
862 producer = new ProducerThread("Prod2");
863 producer.setDaemon(true);
866 producer = new ProducerThread("Prod3");
867 producer.setDaemon(true);
870 producer = new ProducerThread("Prod4");
871 producer.setDaemon(true);
874 producer = new ProducerThread("Prod5");
875 producer.setDaemon(true);
878 producer = new ProducerThread("Prod6");
879 producer.setDaemon(true);
882 producer = new ProducerThread("Prod7");
883 producer.setDaemon(true);
886 producer = new ProducerThread("Prod8");
887 producer.setDaemon(true);
890 producer = new ProducerThread("Prod9");
891 producer.setDaemon(true);
894 producer = new ProducerThread("Pro10");
895 producer.setDaemon(true);
900 private final class ProducerThread extends Thread {
901 private ProducerThread(String threadName) {
908 for(int i=0; i<(Integer.MAX_VALUE-1); i++)
911 name = getPreferredDataSourceName(dsSelector);
912 if(name.contains("BACK")){
913 LOGGER.error(this.getName()+": <====== ");
915 LOGGER.error(this.getName()+": ======>");
917 CachedRowSet rs = null;
918 rs = getData("select 1 from dual", new ArrayList<String>(), name);
920 rs = getData("select 1 from dual", new ArrayList<String>(), name);
922 rs = getData("select 1 from dual", new ArrayList<String>(), name);
924 rs = getData("select 1 from dual", new ArrayList<String>(), name);
926 } catch (Exception e) {
932 } catch (InterruptedException e) {