WQMTools.java [src/java/wqm/utils] Revision: 417be6c639399ec07be5f35956b01a04e49e2fc5 Date: Mon Nov 09 14:29:40 MST 2015
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package wqm.utils;
import csip.Config;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
/**
*
* @author Shaun Case
*/
public class WQMTools {
// Only access these from a 'synchronized' member function...
private static ConcurrentHashMap<String, DataSource> dataSources = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, String> connectionStrings = new ConcurrentHashMap<>();
public static String getR2GISFileURL(String path, String name) throws MalformedURLException {
return ((("http://" + wqm.utils.WQMTools.getDatabaseHostname("r2gis") + "/r2/" + path + "/" + name + ".xml")).replace(" ", "%20")).replace("\\", "/");
}
//If at some point it becomes a performance issue, this method could be made to not be 'synchronized', as this syncs on the actual
// WQMTools as a whole. A speedier implementation would be to synchronize() on the HashMaps, and use synchronized blocks of code.
// this could potentially allow for other threads looking for already built connections to continue with their work without having
// to wait on the threads looking for connections that have not yet been built and pooled, which is what happens now when the function
// in its entirety is synchronized. If multiple hits per second are happening to the WQM service, then this might need to be done, as
// in testing the initial db connections prove to take around 1 sec, and once pooled take only milliseconds to return.
public static synchronized Connection getConnection(String className, Logger logger) throws csip.ServiceException {
Connection ret_val = null;
boolean debugging = Boolean.parseBoolean(Config.getString("debugging", "false"));
if (null != className) {
//Don't need to synchronize() around these calls because they are already in a synchronized member
if (!connectionStrings.containsKey(className)) {
connectionStrings.put(className, Config.getString((debugging ? className.toLowerCase() + ".debug.db" : className.toLowerCase() + ".db")));
}
if (connectionStrings.get(className) == null || connectionStrings.get(className).isEmpty()) {
if (logger != null) {
logger.log(Level.SEVERE, "Connection string not provided. Please configure wqm.db configuration parameter.");
}
throw new csip.ServiceException("Unable to connect to WQM database. Please check the " + (debugging ? className.toLowerCase() + ".debug.db" : className.toLowerCase() + ".db") + " configuration parameter");
}
try {
if (!dataSources.containsKey(className)) {
createDataSource(className, Config.getString(className + ".db.driver", "org.postgresql.Driver"), logger);
}
ret_val = dataSources.get(className).getConnection();
} catch (SQLException ex) {
if (logger != null) {
logger.log(Level.SEVERE, null, ex);
}
throw new csip.ServiceException("Failed to connect to WQM database. Please check the " + className + " configuration parameter");
}
}
return ret_val;
}
private static String getDatabaseHostname(String className) throws MalformedURLException {
String ret_val;
boolean debugging = Boolean.parseBoolean(Config.getString("debugging", "false"));
ret_val = new URL(Config.getString((debugging ? className + ".debug.db" : className + ".db")).replace("jdbc:postgresql", "http")).getHost();
return ret_val;
}
//Doesn't 'need' to be synchronized because it should only be called by getConnection() which is synchronized,
// but just in case someone tries calling it elsewhere within this class at a later date...leave it synchronized.
private static synchronized void createDataSource(String className, String DriverClassName, Logger logger) throws SQLException {
PoolProperties p = new PoolProperties();
p.setUrl(connectionStrings.get(className));
//p.setDefaultReadOnly( true );
p.setDriverClassName(DriverClassName);
p.setJmxEnabled(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(30); //Experimentally find an optimal value for this...
p.setRemoveAbandoned(true);
p.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"
+ "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer;"
+ "org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer");
dataSources.put(className, new org.apache.tomcat.jdbc.pool.DataSource(p));
if (logger != null) {
logger.log(Level.INFO, "\nCreated datasource {0}", dataSources.get(className).toString());
}
}
/*
public static void shutdownDataSource() {
if (datasourceR2GIS != null && datasourceR2GIS instanceof DataSourceProxy) {
DataSourceProxy proxy = (DataSourceProxy) datasourceR2GIS;
proxy.close(true);
datasourceR2GIS = null;
}
if (datasourceSSURGO != null && datasourceSSURGO instanceof DataSourceProxy) {
DataSourceProxy proxy = (DataSourceProxy) datasourceSSURGO;
proxy.close(true);
datasourceSSURGO = null;
}
if (datasourceWQM != null && datasourceWQM instanceof DataSourceProxy) {
DataSourceProxy proxy = (DataSourceProxy) datasourceWQM;
proxy.close(true);
datasourceWQM = null;
}
}
*/
public static class PolygonLatLon {
private ArrayList<WQMTools.PolygonLatLon.PointLatLon> points;
private Boolean bValid;
public PolygonLatLon() {
this.bValid = true;
this.points = new ArrayList<>();
}
public PolygonLatLon(ArrayList<WQMTools.PolygonLatLon.PointLatLon> tPoints) {
if (null != tPoints) {
this.bValid = true;
this.points = new ArrayList<>(tPoints);
for (WQMTools.PolygonLatLon.PointLatLon tPoint : tPoints) {
if (!tPoint.isValid()) {
this.bValid = false;
break;
}
}
}
}
public void add(WQMTools.PolygonLatLon.PointLatLon tPoint) {
this.points.add(tPoint);
if (!tPoint.isValid()) {
this.bValid = false;
}
}
public void add(Double Lat, Double Lon) {
WQMTools.PolygonLatLon.PointLatLon tPoint = new WQMTools.PolygonLatLon.PointLatLon(Lat, Lon);
this.points.add(tPoint);
if (!tPoint.isValid()) {
this.bValid = false;
}
}
public String toWKT() {
String ret_val = "";
int currentPoint = 0;
StringBuilder buff = new StringBuilder();
if (this.bValid) {
buff.append("'POLYGON((");
for (WQMTools.PolygonLatLon.PointLatLon tPoint : this.points) {
if (currentPoint > 0) {
buff.append(", ");
}
// Remember Lon is X and Lat is Y...
buff.append(tPoint.getLon().toString());
buff.append(" ");
buff.append(tPoint.getLat().toString());
//ret_val += tPoint.getLon().toString() + " " + tPoint.getLat().toString();
currentPoint++;
}
buff.append("))'");
ret_val = buff.toString();
}
return ret_val;
}
public Boolean isValid() {
return this.bValid;
}
public static class PointLatLon {
private Double Lat;
private Double Lon;
private Boolean bValid;
public PointLatLon(Double Lat, Double Lon) {
this.Lat = Lat;
this.Lon = Lon;
this.bValid = true;
if (Math.abs(Lon) > 180) {
this.bValid = false;
}
if (Math.abs(Lat) > 90) {
this.bValid = false;
}
}
public Boolean isValid() {
return this.bValid;
}
public Double getLat() {
return this.Lat;
}
public Double getLon() {
return this.Lon;
}
public void setLon(Double Lon) {
this.Lon = Lon;
if (Math.abs(Lon) > 180) {
this.bValid = false;
}
}
public void setLat(Double Lat) {
this.Lat = Lat;
if (Math.abs(Lat) > 90) {
this.bValid = false;
}
}
}
}
}