From 10a4aa4e6bcf3eddc1cfce7f104ec19f8f8c50f7 Mon Sep 17 00:00:00 2001
From: Nikolaus Krismer <niko@krismer.de>
Date: Thu, 27 Mar 2014 15:29:19 +0100
Subject: [PATCH] reworked connection management (minimized open connections)

---
 .../it/unibz/inf/isochrone/db/Database.java   | 23 +++++++++++--------
 .../it/unibz/inf/isoga/db/DatabaseWeb.java    | 15 +++++++-----
 .../java/it/unibz/inf/isoga/db/DbUtility.java | 17 ++++----------
 .../unibz/inf/isoga/network/BBoxOutput.java   |  6 ++---
 .../isoga/service/ServiceConfiguration.java   |  4 ++++
 .../inf/isoga/service/ServiceFeatureInfo.java |  8 ++++++-
 .../inf/isoga/service/ServiceIsochrone.java   | 13 ++++-------
 .../inf/isoga/service/ServicePoiFeature.java  |  8 ++++++-
 .../unibz/inf/isoga/service/ServiceWps.java   | 14 ++++++++---
 .../it/unibz/inf/isoga/web/JsonWebsocket.java |  6 +++--
 .../unibz/inf/isoga/web/StartupListener.java  |  3 +++
 11 files changed, 72 insertions(+), 45 deletions(-)

diff --git a/src/main/java/it/unibz/inf/isochrone/db/Database.java b/src/main/java/it/unibz/inf/isochrone/db/Database.java
index 006f0e1a..de4cd7c4 100644
--- a/src/main/java/it/unibz/inf/isochrone/db/Database.java
+++ b/src/main/java/it/unibz/inf/isochrone/db/Database.java
@@ -43,8 +43,6 @@ import org.apache.commons.dbutils.DbUtils;
 public class Database {
 	protected static final byte NW_MODE_CONTINUOUS = 0;
 	protected static final byte NW_MODE_DISCRETE = 1;
-
-	private final Map<String, PreparedStatement> pstmtsCacheMap;
 	private final String queryEarliestArrivalTimeHomo;
 	private final String queryLatestDepartureTimeHomo;
 	private final String queryGetAllEdges;
@@ -63,8 +61,8 @@ public class Database {
 	protected final ConfigDataset config;
 	protected Mode mode;
 	protected boolean isIncoming;
-	protected Connection connection;
-
+	protected Connection connection; /** The connection the the database. It is lazy-loaded by {@link #getPstmt(String)} method. */
+	private Map<String, PreparedStatement> pstmtsCacheMap;
 	// Constructor
 
 	public Database(final ConfigDataset config, final Mode mode, final Direction direction) {
@@ -72,9 +70,6 @@ public class Database {
 		this.isIncoming = (direction == Direction.INCOMING);
 		this.mode = mode;
 
-		connection = ConfigIsochrone.getInstance().getConnection();
-		pstmtsCacheMap = new HashMap<String, PreparedStatement>();
-
 		final String configDatecodes = config.getTableDatecodes();
 		final String configEdges = config.getTableEdges();
 		final String configSchedule = config.getTableSchedule();
@@ -185,8 +180,10 @@ public class Database {
 	 * This should be called once you're done with the database.
 	 */
 	public void close() {
-		for (final PreparedStatement pstmt : pstmtsCacheMap.values()) {
-			DbUtils.closeQuietly(pstmt);
+		if (pstmtsCacheMap != null) {
+			for (final PreparedStatement pstmt : pstmtsCacheMap.values()) {
+				DbUtils.closeQuietly(pstmt);
+			}
 		}
 
 		DbUtils.closeQuietly(connection);
@@ -636,6 +633,14 @@ public class Database {
 	// FIXME: Find a fix for prepared statements with parameters
 	// (we should NOT set strings in preparedStatemens just because we want to cache something)
 	protected PreparedStatement getPstmt(final String query) {
+		if (connection == null) {
+			// lazy-loading of the database connection (so it is not created on object construction)
+			connection = ConfigIsochrone.getInstance().getConnection();
+		}
+		if (pstmtsCacheMap == null) {
+			pstmtsCacheMap = new HashMap<String, PreparedStatement>();
+		}
+
 		try {
 			if (pstmtsCacheMap.containsKey(query) && !pstmtsCacheMap.get(query).isClosed()) {
 				return pstmtsCacheMap.get(query);
diff --git a/src/main/java/it/unibz/inf/isoga/db/DatabaseWeb.java b/src/main/java/it/unibz/inf/isoga/db/DatabaseWeb.java
index 8457d483..994ea7f6 100644
--- a/src/main/java/it/unibz/inf/isoga/db/DatabaseWeb.java
+++ b/src/main/java/it/unibz/inf/isoga/db/DatabaseWeb.java
@@ -428,7 +428,7 @@ public class DatabaseWeb extends Database {
 
 	public Point transform(final Point p) {
 		final String query = "SELECT ST_X(P.GEO) X, ST_Y(P.GEO) Y FROM (SELECT ST_Transform(ST_PointFromText(?,?),?) GEO ) P";
-		return DatabaseWeb.transform(config, p, getPstmt(query));
+		return DatabaseWeb.transform(connection, config, p, getPstmt(query));
 	}
 
 	public void updateVertexTable() {
@@ -447,15 +447,18 @@ public class DatabaseWeb extends Database {
 	// Public static methods
 
 	public static Point transform(final ConfigDataset config, final Point p) {
-		return transform(config, p, null);
+		final Connection connection = ConfigIsochrone.getInstance().getConnection();
+		final Point result = transform(connection, config, p, null);
+		DbUtils.closeQuietly(connection);
+
+		return result;
 	}
 
 	// Private static methods
 
-	private static Point transform(final ConfigDataset config, final Point p, final PreparedStatement preparedStatement) {
-		final ConfigIsochrone configIso = ConfigIsochrone.getInstance();
+	private static Point transform(final Connection connection, final ConfigDataset config, final Point p, final PreparedStatement preparedStatement) {
 		final boolean openNewStatement = (preparedStatement == null);
-		final int clientSRID = configIso.getClientSRID();
+		final int clientSRID = ConfigIsochrone.getInstance().getClientSRID();
 		if (clientSRID == config.getServerSRID()) {
 			return p;
 		}
@@ -468,7 +471,7 @@ public class DatabaseWeb extends Database {
 		try {
 			if (openNewStatement) {
 				final String query = "SELECT ST_X(P.GEO) X, ST_Y(P.GEO) Y FROM (SELECT ST_Transform(ST_PointFromText(?,?),?) GEO ) P";
-				createdStatement = configIso.getConnection().prepareStatement(query);
+				createdStatement = connection.prepareStatement(query);
 				stmt = createdStatement;
 			} else {
 				stmt = preparedStatement;
diff --git a/src/main/java/it/unibz/inf/isoga/db/DbUtility.java b/src/main/java/it/unibz/inf/isoga/db/DbUtility.java
index e7de60cc..c5de3e3a 100644
--- a/src/main/java/it/unibz/inf/isoga/db/DbUtility.java
+++ b/src/main/java/it/unibz/inf/isoga/db/DbUtility.java
@@ -1,7 +1,6 @@
 package it.unibz.inf.isoga.db;
 
 import it.unibz.inf.isochrone.config.ConfigDataset;
-import it.unibz.inf.isochrone.config.ConfigIsochrone;
 import it.unibz.inf.isochrone.util.EnumContainer.Direction;
 import it.unibz.inf.isochrone.util.EnumContainer.QueryType;
 import it.unibz.inf.isoga.config.ConfigClient;
@@ -144,15 +143,13 @@ public final class DbUtility {
 		return false;
 	}
 
-	public static ResponsePoiFeature getPoiFeatures(final String sqlExpr, final ConfigDataset config) {
+	public static ResponsePoiFeature getPoiFeatures(final Connection connection, final String sqlExpr, final ConfigDataset config) {
 		final ResponsePoiFeature result = new ResponsePoiFeature();
 		if ("noResult".equals(sqlExpr)) {
 			result.setType("invalid");
 			return result;
 		}
 
-		final Connection connection = ConfigIsochrone.getInstance().getConnection();
-
 		Statement stmt = null;
 		ResultSet rSet = null;
 		try {
@@ -178,11 +175,10 @@ public final class DbUtility {
 		return result;
 	}
 
-	public static WpsStatistic getStatistics(final String sqlExpr, final boolean withSum, final ConfigDataset config) {
-		final Connection connection = ConfigIsochrone.getInstance().getConnection();
-
+	public static WpsStatistic getStatistics(final Connection connection,  final String sqlExpr, final boolean withSum, final ConfigDataset config) {
 		int sum = 0;
 		int count = 0;
+
 		Statement stmt = null;
 		ResultSet rSet = null;
 		try {
@@ -210,10 +206,9 @@ public final class DbUtility {
 		return new WpsStatistic(count, sum);
 	}
 
-	public static SortedMap<Calendar, RouteEntity> getVertexAnnotation(final ConfigClient config, final int vertexId, final Direction direction) {
+	public static SortedMap<Calendar, RouteEntity> getVertexAnnotation(final Connection connection, final ConfigClient config, final int vertexId, final Direction direction) {
 		final Comparator<Object> comp = (direction == Direction.INCOMING) ? Collections.reverseOrder() : null;
 		final SortedMap<Calendar, RouteEntity> schedules =  new TreeMap<Calendar, RouteEntity>(comp);
-		final Connection connection = ConfigIsochrone.getInstance().getConnection();
 
 		final String arrivalTimeStr = "to_char(to_timestamp(CAST(\"TIME_A\" AS text),'SSSSS'),'HH24:MI') \"ARRIVAL_TIME\"";
 		final String departureTimeStr = "to_char(to_timestamp(CAST(\"TIME_D\" AS text),'SSSSS'),'HH24:MI') \"DEPARTURE_TIME\"";
@@ -291,15 +286,13 @@ public final class DbUtility {
 		}
 	}
 
-	public static ResponseWps spatialIntersection2JSON(final String sqlExpr, final SqlQuery query, final ConfigDataset config) {
+	public static ResponseWps spatialIntersection2JSON(final Connection connection, final String sqlExpr, final SqlQuery query, final ConfigDataset config) {
 		final ResponseWps result = new ResponseWps(query.getQueryType().toString().toLowerCase());
 		if (sqlExpr.equals("noResult")) {
 			result.setType("invalid");
 			return result;
 		}
 
-		final Connection connection = ConfigIsochrone.getInstance().getConnection();
-
 		Statement stmt = null;
 		ResultSet rSet = null;
 		try {
diff --git a/src/main/java/it/unibz/inf/isoga/network/BBoxOutput.java b/src/main/java/it/unibz/inf/isoga/network/BBoxOutput.java
index f7375287..edb8534a 100644
--- a/src/main/java/it/unibz/inf/isoga/network/BBoxOutput.java
+++ b/src/main/java/it/unibz/inf/isoga/network/BBoxOutput.java
@@ -83,15 +83,15 @@ public class BBoxOutput extends MemoryOutput {
 	// Private methods
 
 	private void calculateBoundingBox() {
-		final ConfigIsochrone configIso = ConfigIsochrone.getInstance();
-		final Connection connection = configIso.getConnection();
+		final int clientSRID = ConfigIsochrone.getInstance().getClientSRID();
+		final Connection connection = db.getConnection();
 
 		PreparedStatement statement = null;
 		ResultSet rSet = null;
 		try {
 			statement = connection.prepareStatement(query);
 			statement.setInt(1, serverSRID);
-			statement.setInt(2, configIso.getClientSRID());
+			statement.setInt(2, clientSRID);
 			rSet = statement.executeQuery();
 			if (rSet.next()) {
 				long minX = Math.round(rSet.getDouble("min_x"));
diff --git a/src/main/java/it/unibz/inf/isoga/service/ServiceConfiguration.java b/src/main/java/it/unibz/inf/isoga/service/ServiceConfiguration.java
index 1b3542e0..823e5512 100644
--- a/src/main/java/it/unibz/inf/isoga/service/ServiceConfiguration.java
+++ b/src/main/java/it/unibz/inf/isoga/service/ServiceConfiguration.java
@@ -14,6 +14,8 @@ import java.util.Set;
 
 import javax.websocket.Session;
 
+import org.apache.commons.dbutils.DbUtils;
+
 /**
  * @author Nikolaus Krismer
  */
@@ -66,6 +68,8 @@ public class ServiceConfiguration extends AbstractService<RequestConfiguration,
 			final ConfigClient configClient = ConfigClient.getInstance(clientId, dataset);
 			registerTableAndLayers(connection, configClient);
 		}
+
+		DbUtils.closeQuietly(connection);
 	}
 
 }
diff --git a/src/main/java/it/unibz/inf/isoga/service/ServiceFeatureInfo.java b/src/main/java/it/unibz/inf/isoga/service/ServiceFeatureInfo.java
index b98c0297..8507e58b 100644
--- a/src/main/java/it/unibz/inf/isoga/service/ServiceFeatureInfo.java
+++ b/src/main/java/it/unibz/inf/isoga/service/ServiceFeatureInfo.java
@@ -1,6 +1,7 @@
 package it.unibz.inf.isoga.service;
 
 
+import it.unibz.inf.isochrone.config.ConfigIsochrone;
 import it.unibz.inf.isochrone.util.EnumContainer.Direction;
 import it.unibz.inf.isoga.config.ConfigClient;
 import it.unibz.inf.isoga.db.DbUtility;
@@ -9,11 +10,14 @@ import it.unibz.inf.isoga.service.dto.RequestFeatureInfo;
 import it.unibz.inf.isoga.service.dto.ResponseFeatureInfo;
 import it.unibz.inf.isoga.service.dto.ResponseFeatureInfo.Entry;
 
+import java.sql.Connection;
 import java.util.Calendar;
 import java.util.SortedMap;
 
 import javax.websocket.Session;
 
+import org.apache.commons.dbutils.DbUtils;
+
 /**
  * @author Nikolaus Krismer
  */
@@ -35,11 +39,13 @@ public class ServiceFeatureInfo extends AbstractService<RequestFeatureInfo, Resp
 		final ConfigClient config = ConfigClient.getInstance(clientId, ds);
 		final ResponseFeatureInfo result = new ResponseFeatureInfo();
 
-		final SortedMap<Calendar, RouteEntity> schedules = DbUtility.getVertexAnnotation(config, stopId, direction);
+		final Connection connection = ConfigIsochrone.getInstance().getConnection();
+		final SortedMap<Calendar, RouteEntity> schedules = DbUtility.getVertexAnnotation(connection, config, stopId, direction);
 		for (final RouteEntity routeEntity : schedules.values()) {
 			result.addEntry(new Entry(routeEntity));
 		}
 
+		DbUtils.closeQuietly(connection);
 		return result;
 	}
 
diff --git a/src/main/java/it/unibz/inf/isoga/service/ServiceIsochrone.java b/src/main/java/it/unibz/inf/isoga/service/ServiceIsochrone.java
index 299fcc35..5f36e40b 100644
--- a/src/main/java/it/unibz/inf/isoga/service/ServiceIsochrone.java
+++ b/src/main/java/it/unibz/inf/isoga/service/ServiceIsochrone.java
@@ -7,7 +7,6 @@ import it.unibz.inf.isochrone.algorithm.MDijkstra;
 import it.unibz.inf.isochrone.algorithm.MineX;
 import it.unibz.inf.isochrone.algorithm.MrneX;
 import it.unibz.inf.isochrone.config.ConfigDataset;
-import it.unibz.inf.isochrone.config.ConfigIsochrone;
 import it.unibz.inf.isochrone.network.Location;
 import it.unibz.inf.isochrone.util.EnumContainer.Algorithm;
 import it.unibz.inf.isochrone.util.EnumContainer.CoverageMode;
@@ -57,15 +56,15 @@ public class ServiceIsochrone extends AbstractService<RequestIsochrone, Response
 		final ConfigClient config = ConfigClient.getInstance(clientId, dataset);
 
 		// register layers which have not been pre-registered in ServiceConfiguration class
+		final long startComputationTiming = System.currentTimeMillis();
+		final DatabaseWeb db = new DatabaseWeb(config, request.getMode(), direction);
 		if (!config.isRegistered()) {
-			registerTableAndLayers(ConfigIsochrone.getInstance().getConnection(), config);
+			registerTableAndLayers(db.getConnection(), config);
 		}
 
 		// Timing start
 
-		final long startComputationTiming = System.currentTimeMillis();
-		final DatabaseWeb db = new DatabaseWeb(config, request.getMode(), direction);
-		truncateTables(config);
+		truncateTables(db.getConnection(), config);
 		final Query query = new Query(direction, request.getSpeed(), request.getDmax(), request.getDate(), true, request.getMode());
 		query.setStartLocations(locationsFromQueryNodes(request.getQueryNodes(), db));
 		final Isochrone isochrone = getAlgorithm(request.getAlgorithm(), config, db, query);
@@ -126,9 +125,7 @@ public class ServiceIsochrone extends AbstractService<RequestIsochrone, Response
 		return locations;
 	}
 
-	private void truncateTables(final ConfigDataset config) {
-		final Connection conn = ConfigIsochrone.getInstance().getConnection();
-
+	private void truncateTables(final Connection conn, final ConfigDataset config) {
 		DbUtility.truncateTable(conn, config.getEdgeTableEntry().getTableName());
 		DbUtility.truncateTable(conn, config.getVertexTableEntry().getTableName());
 		DbUtility.dropIndex(conn, config.getVertexTableEntry().getIndexName());
diff --git a/src/main/java/it/unibz/inf/isoga/service/ServicePoiFeature.java b/src/main/java/it/unibz/inf/isoga/service/ServicePoiFeature.java
index 06701581..63f7705d 100644
--- a/src/main/java/it/unibz/inf/isoga/service/ServicePoiFeature.java
+++ b/src/main/java/it/unibz/inf/isoga/service/ServicePoiFeature.java
@@ -7,8 +7,12 @@ import it.unibz.inf.isoga.db.DbUtility;
 import it.unibz.inf.isoga.service.dto.RequestPoiFeature;
 import it.unibz.inf.isoga.service.dto.ResponsePoiFeature;
 
+import java.sql.Connection;
+
 import javax.websocket.Session;
 
+import org.apache.commons.dbutils.DbUtils;
+
 /**
  * @author Nikolaus Krismer
  */
@@ -35,7 +39,9 @@ public class ServicePoiFeature extends AbstractService<RequestPoiFeature, Respon
 		if (!sqlMessage.toUpperCase().startsWith("SELECT")) {
 			poiFeatures.setType("No result");
 		} else {
-			poiFeatures = DbUtility.getPoiFeatures(sqlMessage, config);
+			final Connection connection = ConfigIsochrone.getInstance().getConnection();
+			poiFeatures = DbUtility.getPoiFeatures(connection, sqlMessage, config);
+			DbUtils.closeQuietly(connection);
 		}
 
 		return poiFeatures;
diff --git a/src/main/java/it/unibz/inf/isoga/service/ServiceWps.java b/src/main/java/it/unibz/inf/isoga/service/ServiceWps.java
index f2d7b08e..eb32cb56 100644
--- a/src/main/java/it/unibz/inf/isoga/service/ServiceWps.java
+++ b/src/main/java/it/unibz/inf/isoga/service/ServiceWps.java
@@ -1,6 +1,7 @@
 package it.unibz.inf.isoga.service;
 
 
+import it.unibz.inf.isochrone.config.ConfigIsochrone;
 import it.unibz.inf.isochrone.util.EnumContainer.QueryType;
 import it.unibz.inf.isoga.config.ConfigClient;
 import it.unibz.inf.isoga.db.DbUtility;
@@ -8,8 +9,12 @@ import it.unibz.inf.isoga.db.SqlQuery;
 import it.unibz.inf.isoga.service.dto.RequestWps;
 import it.unibz.inf.isoga.service.dto.ResponseWps;
 
+import java.sql.Connection;
+
 import javax.websocket.Session;
 
+import org.apache.commons.dbutils.DbUtils;
+
 /**
  * @author Nikolaus Krismer
  */
@@ -36,13 +41,16 @@ public class ServiceWps extends AbstractService<RequestWps, ResponseWps> {
 		final String clientId = session.getId();
 		final ConfigClient config = ConfigClient.getInstance(clientId, ds);
 		final String spatialQuery = query.getSpatialQueryString(config);
-		response = DbUtility.spatialIntersection2JSON(spatialQuery, query, config);
+		final Connection connection = ConfigIsochrone.getInstance().getConnection();
+
+		response = DbUtility.spatialIntersection2JSON(connection, spatialQuery, query, config);
 		if (query.getQueryType().equals(QueryType.SELECT)) {
-			response.setStatistic(DbUtility.getStatistics(query.getStatistics(false), false, config));
+			response.setStatistic(DbUtility.getStatistics(connection, query.getStatistics(false), false, config));
 		} else if (query.getQueryType().equals(QueryType.SUM)) {
-			response.setStatistic(DbUtility.getStatistics(query.getStatisticsWithSum(false), true, config));
+			response.setStatistic(DbUtility.getStatistics(connection, query.getStatisticsWithSum(false), true, config));
 		}
 
+		DbUtils.closeQuietly(connection);
 		return response;
 	}
 
diff --git a/src/main/java/it/unibz/inf/isoga/web/JsonWebsocket.java b/src/main/java/it/unibz/inf/isoga/web/JsonWebsocket.java
index 23f7135f..dce87f7d 100644
--- a/src/main/java/it/unibz/inf/isoga/web/JsonWebsocket.java
+++ b/src/main/java/it/unibz/inf/isoga/web/JsonWebsocket.java
@@ -41,6 +41,7 @@ import javax.websocket.OnOpen;
 import javax.websocket.Session;
 import javax.websocket.server.ServerEndpoint;
 
+import org.apache.commons.dbutils.DbUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -62,14 +63,14 @@ public class JsonWebsocket {
 
 	@OnClose
 	public void onClose(final Session session, final CloseReason closeReason) {
-		final Connection connection = ConfigIsochrone.getInstance().getConnection();
-
 		final ConfigGeoserver config = ConfigGeoserver.getInstance();
 		final String ws = config.getRenderServerWorkspace();
 		final GeoServerRESTPublisher publisher = config.getGeoServerPublisher();
 
 		final String clientId = session.getId();
 		final Collection<ConfigClient> allClientDatasetConfigs = ConfigClient.getAllConfigs(clientId);
+
+		final Connection connection = ConfigIsochrone.getInstance().getConnection();
 		for (final ConfigClient clientDSetConfig : allClientDatasetConfigs) {
 			if (clientDSetConfig.isRegistered()) {
 				// unregister layers
@@ -86,6 +87,7 @@ public class JsonWebsocket {
 				}
 			}
 		}
+		DbUtils.closeQuietly(connection);
 
 		publisher.reload();
 		LOGGER.debug("Websocket: Session \"" + clientId + "\" closed: " + closeReason);
diff --git a/src/main/java/it/unibz/inf/isoga/web/StartupListener.java b/src/main/java/it/unibz/inf/isoga/web/StartupListener.java
index 88465c08..108341d0 100644
--- a/src/main/java/it/unibz/inf/isoga/web/StartupListener.java
+++ b/src/main/java/it/unibz/inf/isoga/web/StartupListener.java
@@ -11,6 +11,7 @@ import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
 import javax.servlet.annotation.WebListener;
 
+import org.apache.commons.dbutils.DbUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -68,6 +69,8 @@ public class StartupListener implements ServletContextListener {
 				LOGGER.warn(" - table(s) \"" + regexp + "\" could not be dropped");
 			}
 		}
+
+		DbUtils.closeQuietly(connection);
 	}
 
 	/**
-- 
GitLab