JDBCFeatureSourceHelper.java
/*
* Copyright (C) 2022 B3Partners B.V.
*
* SPDX-License-Identifier: MIT
*/
package org.tailormap.api.geotools.featuresources;
import static org.geotools.data.sqlserver.SQLServerDataStoreFactory.GEOMETRY_METADATA_TABLE;
import static org.geotools.jdbc.JDBCDataStoreFactory.DATABASE;
import static org.geotools.jdbc.JDBCDataStoreFactory.DBTYPE;
import static org.geotools.jdbc.JDBCDataStoreFactory.EXPOSE_PK;
import static org.geotools.jdbc.JDBCDataStoreFactory.FETCHSIZE;
import static org.geotools.jdbc.JDBCDataStoreFactory.HOST;
import static org.geotools.jdbc.JDBCDataStoreFactory.MAXWAIT;
import static org.geotools.jdbc.JDBCDataStoreFactory.PASSWD;
import static org.geotools.jdbc.JDBCDataStoreFactory.PK_METADATA_TABLE;
import static org.geotools.jdbc.JDBCDataStoreFactory.PORT;
import static org.geotools.jdbc.JDBCDataStoreFactory.SCHEMA;
import static org.geotools.jdbc.JDBCDataStoreFactory.USER;
import static org.tailormap.api.persistence.json.JDBCConnectionProperties.DbtypeEnum.ORACLE;
import static org.tailormap.api.persistence.json.JDBCConnectionProperties.DbtypeEnum.POSTGIS;
import static org.tailormap.api.persistence.json.JDBCConnectionProperties.DbtypeEnum.SQLSERVER;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.geotools.api.data.DataStore;
import org.geotools.data.postgis.PostgisNGDataStoreFactory;
import org.geotools.data.sqlserver.SQLServerDataStoreFactory;
import org.tailormap.api.persistence.TMFeatureSource;
import org.tailormap.api.persistence.json.JDBCConnectionProperties;
import org.tailormap.api.persistence.json.ServiceAuthentication;
public class JDBCFeatureSourceHelper extends FeatureSourceHelper {
private static final Map<JDBCConnectionProperties.DbtypeEnum, Integer> defaultPorts =
Map.of(POSTGIS, 5432, ORACLE, 1521, SQLSERVER, 1433);
@Override
public DataStore createDataStore(TMFeatureSource tmfs, Integer timeout) throws IOException {
if (tmfs.getProtocol() != TMFeatureSource.Protocol.JDBC) {
throw new IllegalArgumentException(tmfs.getProtocol().getValue());
}
Objects.requireNonNull(tmfs.getJdbcConnection());
Objects.requireNonNull(tmfs.getAuthentication());
if (tmfs.getAuthentication().getMethod() != ServiceAuthentication.MethodEnum.PASSWORD) {
throw new IllegalArgumentException(
tmfs.getAuthentication().getMethod().getValue());
}
JDBCConnectionProperties c = tmfs.getJdbcConnection();
Objects.requireNonNull(c.getDbtype());
String connectionOpts = Optional.ofNullable(c.getAdditionalProperties().get("connectionOptions"))
.orElse("");
Map<String, Object> params = new HashMap<>();
// database specific settings
switch (c.getDbtype()) {
case POSTGIS -> {
// use spatial index to estimate the extents
params.put(PostgisNGDataStoreFactory.ESTIMATED_EXTENTS.key, true);
if (!connectionOpts.contains("ApplicationName")) {
connectionOpts = connectionOpts
+ (connectionOpts.contains("?") ? "&" : "?")
+ "ApplicationName=tailormap-api";
}
}
case SQLSERVER -> {
// use spatial index to estimate the extents
params.put(SQLServerDataStoreFactory.ESTIMATED_EXTENTS.key, true);
// we need this for mssql to determine a feature type on an empty table
params.put(GEOMETRY_METADATA_TABLE.key, "geometry_columns");
if (!connectionOpts.contains("applicationName")) {
// see
// https://learn.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver16
connectionOpts = connectionOpts + ";applicationName=tailormap-api";
}
}
case ORACLE -> {
// No specific settings for Oracle
}
}
params.put(DBTYPE.key, c.getDbtype().getValue());
params.put(HOST.key, c.getHost());
params.put(PORT.key, c.getPort() != null ? c.getPort() : defaultPorts.get(c.getDbtype()));
params.put(DATABASE.key, c.getDatabase() + connectionOpts);
params.put(SCHEMA.key, c.getSchema());
params.put(USER.key, tmfs.getAuthentication().getUsername());
params.put(PASSWD.key, tmfs.getAuthentication().getPassword());
params.put(FETCHSIZE.key, c.getFetchSize());
params.put(EXPOSE_PK.key, true);
params.put(PK_METADATA_TABLE.key, c.getPrimaryKeyMetadataTable());
params.put(MAXWAIT.key, timeout);
return openDatastore(params, PASSWD.key);
}
}