changeset 553:5b536542ef56

Another attempt to fix gnv/issue34. Implemented an internal idle time checking. geo-backend/trunk@619 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 25 Jan 2010 12:12:31 +0000
parents 7615ee5d1345
children 0ee3c0ed40e4
files geo-backend/ChangeLog geo-backend/src/main/java/de/intevation/gnv/geobackend/base/DefaultResultDescriptor.java geo-backend/src/main/java/de/intevation/gnv/geobackend/sde/connectionpool/ArcSDEPoolableObjectFactory.java geo-backend/src/main/java/de/intevation/gnv/geobackend/sde/datasources/ArcSDEConnection.java
diffstat 4 files changed, 176 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- 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	<sascha.teichmann@intevation.de>
+
+	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	<sascha.teichmann@intevation.de>
 
 	* src/main/java/de/intevation/gnv/geobackend/sde/datasources/SDEQuery.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();
+    }
 }
--- 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 <tim.englich@intevation.de>
- *
+ * @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;
     }
 }
--- 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()
 	 */

http://dive4elements.wald.intevation.org