# HG changeset patch # User Sascha L. Teichmann # Date 1264421551 0 # Node ID 5b536542ef569a6653eb2727f324b00a6369ce44 # Parent 7615ee5d134519663d457bee137f2e7f8e148051 Another attempt to fix gnv/issue34. Implemented an internal idle time checking. geo-backend/trunk@619 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r 7615ee5d1345 -r 5b536542ef56 geo-backend/ChangeLog --- a/geo-backend/ChangeLog Fri Jan 15 20:57:13 2010 +0000 +++ b/geo-backend/ChangeLog Mon Jan 25 12:12:31 2010 +0000 @@ -1,3 +1,27 @@ +2009-01-25 Sascha L. Teichmann + + Another attempt to fix gnv/issue34 + + * src/main/java/de/intevation/gnv/geobackend/base/DefaultResultDescriptor.java: + Add a toString() method. + + * src/main/java/de/intevation/gnv/geobackend/sde/datasources/ArcSDEConnection.java: + Added an idle time detection mechanism. You can call touch() on + a connection to refresh the internal timestamp and ask with isActive() + if a constructor given time interval in milliseconds is exceeded. + + * src/main/java/de/intevation/gnv/geobackend/sde/connectionpool/ArcSDEPoolableObjectFactory.java: + Added a check of inactivity to validateObject() before relying on SDE logic. + This is the last line of defence if the testServer() call is going not to respond. + This duplicates the eviction policy of Apache Commons pool a bit but I found + this way to be more trustworthy. + + The idle time is configure with the property 'serverInactiveInterval'. + units: seconds. Defaults to 5 minutes. + + The unit of 'serverRoundtripInterval' (parameter of testServer()) is now + in seconds, too. Default: 5 seconds. This is the value used in GeoTools. + 2009-01-09 Sascha L. Teichmann * src/main/java/de/intevation/gnv/geobackend/sde/datasources/SDEQuery.java, diff -r 7615ee5d1345 -r 5b536542ef56 geo-backend/src/main/java/de/intevation/gnv/geobackend/base/DefaultResultDescriptor.java --- a/geo-backend/src/main/java/de/intevation/gnv/geobackend/base/DefaultResultDescriptor.java Fri Jan 15 20:57:13 2010 +0000 +++ b/geo-backend/src/main/java/de/intevation/gnv/geobackend/base/DefaultResultDescriptor.java Mon Jan 25 12:12:31 2010 +0000 @@ -98,4 +98,19 @@ Integer value = this.columnIndexLookup.get(columnName); return value != null ? value.intValue() : -1; } + + public String toString() { + StringBuilder sb = new StringBuilder("["); + int N = Math.min(columnNames.size(), columnClassNames.size()); + for (int i = 0; i < N; ++i) { + if (i > 0) { + sb.append(", "); + } + sb.append(columnNames.get(i)) + .append(": ") + .append(columnClassNames.get(i)); + } + sb.append(']'); + return sb.toString(); + } } diff -r 7615ee5d1345 -r 5b536542ef56 geo-backend/src/main/java/de/intevation/gnv/geobackend/sde/connectionpool/ArcSDEPoolableObjectFactory.java --- a/geo-backend/src/main/java/de/intevation/gnv/geobackend/sde/connectionpool/ArcSDEPoolableObjectFactory.java Fri Jan 15 20:57:13 2010 +0000 +++ b/geo-backend/src/main/java/de/intevation/gnv/geobackend/sde/connectionpool/ArcSDEPoolableObjectFactory.java Mon Jan 25 12:12:31 2010 +0000 @@ -1,30 +1,34 @@ -/** - * - */ package de.intevation.gnv.geobackend.sde.connectionpool; +import de.intevation.gnv.geobackend.base.connectionpool.exception.ConnectionException; + +import de.intevation.gnv.geobackend.sde.datasources.ArcSDEConnection; + import java.sql.Connection; import java.sql.SQLException; + import java.util.Properties; import org.apache.commons.pool.PoolableObjectFactory; + import org.apache.log4j.Logger; -import de.intevation.gnv.geobackend.base.connectionpool.exception.ConnectionException; -import de.intevation.gnv.geobackend.sde.datasources.ArcSDEConnection; - /** - * @author Tim Englich - * + * @author Tim Englich (tim.englich@intevation.de) + * @author Sascha L. Teichmann (sascha.teichmann@intevation.de) */ public class ArcSDEPoolableObjectFactory implements PoolableObjectFactory { /** * the logger, used to log exceptions and additonaly information */ - private static Logger log = Logger.getLogger(ArcSDEPoolableObjectFactory.class); - - private int serverRoundtripInterval = 1000 * 30;// 30 Sekunden + private static Logger log = Logger.getLogger( + ArcSDEPoolableObjectFactory.class); + + // The 5 seconds are inspired by GeoTools's testServer() usage. + private int serverRoundtripInterval = 5; + + private long serverInactiveInterval = 5L*60L*1000L; // 5 minutes /** * The URL to the ArcSDE Server */ @@ -51,30 +55,48 @@ * @param properties the Properties which includes the ConnectionParams to the Database */ public ArcSDEPoolableObjectFactory(Properties properties) { + log.debug("ArcSDEPoolableObjectFactory.Constructor"); - this.server = properties.getProperty("server"); - this.port = properties.getProperty("port"); - this.database = properties.getProperty("database"); - this.username = properties.getProperty("username"); - this.credentials = properties.getProperty("credentials"); + + server = properties.getProperty("server"); + port = properties.getProperty("port"); + database = properties.getProperty("database"); + username = properties.getProperty("username"); + credentials = properties.getProperty("credentials"); + + String serverRoundtripIntervalValue = + properties.getProperty("serverRoundtripInterval"); + String serverInactiveIntervalValue = + properties.getProperty("serverInactiveInterval"); try { - String serverRoundtripIntervalValue = properties.getProperty("serverRoundtripInterval"); - if (serverRoundtripIntervalValue != null){ - this.serverRoundtripInterval = Integer.parseInt(serverRoundtripIntervalValue); + if (serverRoundtripIntervalValue != null) { + serverRoundtripInterval = + Integer.parseInt(serverRoundtripIntervalValue); } - } catch (NumberFormatException e) { + } + catch (NumberFormatException e) { + log.error(e,e); + } + + try { + if (serverInactiveIntervalValue != null) { + serverInactiveInterval = 1000L * // input in seconds! + Long.parseLong(serverInactiveIntervalValue); + } + } + catch (NumberFormatException e) { log.error(e,e); } log.info("ArcSDEPoolableObjectFactory initialized"); - log.info("Server: "+this.server); - log.info("Port: "+this.port); - log.info("Database: "+this.database); - log.info("User: "+this.username); - log.info("Testtimeout: "+this.serverRoundtripInterval); - - } + log.info("Server: " + server); + log.info("Port: " + port); + log.info("Database: " + database); + log.info("User: " + username); + log.info("Roundtrip check interval: " + serverRoundtripInterval); + log.info("Inactive check interval: " + serverInactiveInterval); + } /** * @see org.apache.commons.pool.PoolableObjectFactory#activateObject(java.lang.Object) @@ -102,10 +124,19 @@ log.debug("ArcSDEPoolableObjectFactory.makeObject"); Connection con; try { - con = new ArcSDEConnection(this.server, this.port, this.database, this.username, this.credentials, this.serverRoundtripInterval); + con = new ArcSDEConnection( + server, + port, + database, + username, + credentials, + serverRoundtripInterval, + serverInactiveInterval); } catch (ConnectionException e) { - throw new ConnectionException("Establishing a connection to database failed: " + e.toString(), e); + throw new ConnectionException( + "Establishing a connection to database failed: " + + e.toString(), e); } return con; } @@ -114,22 +145,52 @@ * @see org.apache.commons.pool.PoolableObjectFactory#passivateObject(java.lang.Object) */ public void passivateObject(Object arg0) throws Exception { - log.debug("ArcSDEPoolableObjectFactory.passivateObject"); + + boolean debug = log.isDebugEnabled(); + + if (debug) { + log.debug("ArcSDEPoolableObjectFactory.passivateObject"); + } + + if (arg0 instanceof ArcSDEConnection) { + if (debug) { + log.debug(" touching connection"); + } + ((ArcSDEConnection)arg0).touch(); + } } /** * @see org.apache.commons.pool.PoolableObjectFactory#validateObject(java.lang.Object) */ public boolean validateObject(Object arg0) { - log.debug("ArcSDEPoolableObjectFactory.validateObject"); - - boolean returnValue = false; + + boolean debug = log.isDebugEnabled(); + + if (debug) { + log.debug("ArcSDEPoolableObjectFactory.validateObject"); + } + + if (!(arg0 instanceof ArcSDEConnection)) { + return false; + } + try { - returnValue = arg0 instanceof ArcSDEConnection - ? ((ArcSDEConnection)arg0).isValid(this.serverRoundtripInterval) - : false; + ArcSDEConnection con = (ArcSDEConnection)arg0; + + boolean isValid = + con.isActive() && con.isValid(serverRoundtripInterval); + + if (!isValid && debug) { + log.debug("connection is invalid!"); + } + + return isValid; } - catch (SQLException sqle) {} - return returnValue; + catch (SQLException sqle) { + log.error(sqle, sqle); + } + + return false; } } diff -r 7615ee5d1345 -r 5b536542ef56 geo-backend/src/main/java/de/intevation/gnv/geobackend/sde/datasources/ArcSDEConnection.java --- a/geo-backend/src/main/java/de/intevation/gnv/geobackend/sde/datasources/ArcSDEConnection.java Fri Jan 15 20:57:13 2010 +0000 +++ b/geo-backend/src/main/java/de/intevation/gnv/geobackend/sde/datasources/ArcSDEConnection.java Mon Jan 25 12:12:31 2010 +0000 @@ -43,22 +43,55 @@ private SeConnection seConnection = null; private long serverRoundtripInterval; + + private long inactiveInterval; + + private long lastTouch; - /** * Constructor */ - public ArcSDEConnection(String server,String port,String database,String username,String credentials, long serverRoundtripInterval) throws ConnectionException { + public ArcSDEConnection( + String server, + String port, + String database, + String username, + String credentials, + long serverRoundtripInterval, + long inactiveInterval + ) + throws ConnectionException + { + this.serverRoundtripInterval = serverRoundtripInterval; + this.inactiveInterval = inactiveInterval; + lastTouch = System.currentTimeMillis(); + try { - seConnection = new SeConnection(server,port,database,username,credentials); - this.serverRoundtripInterval = serverRoundtripInterval; - } catch (SeException e) { + seConnection = new SeConnection( server, port, database, username, credentials); + } + catch (SeException e) { log.error(e,e); throw new ConnectionException(e); } } + public boolean isActive() { + long current = System.currentTimeMillis(); + long last; + synchronized (this) { + last = lastTouch; + } + return Math.abs(current - last) < inactiveInterval; + } + + public void touch() { + long time = System.currentTimeMillis(); + synchronized (this) { + lastTouch = time; + } + } + /** * @see java.sql.Connection#clearWarnings() */