package com.revolsys.jdbc.io; import java.nio.file.Path; import java.sql.SQLException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Pattern; import javax.sql.DataSource; import com.revolsys.collection.map.Maps; import com.revolsys.io.IoFactory; import com.revolsys.jdbc.JdbcUtils; import com.revolsys.logging.Logs; import com.revolsys.record.io.RecordStoreFactory; import com.revolsys.record.schema.FieldDefinition; import com.revolsys.record.schema.RecordStore; import com.revolsys.util.PasswordUtil; import com.revolsys.util.Property; public interface JdbcDatabaseFactory extends RecordStoreFactory { String URL_FIELD = "urlField"; static DataSource closeDataSource(final DataSource dataSource) { if (dataSource instanceof DataSourceImpl) { final DataSourceImpl basicDataSource = (DataSourceImpl)dataSource; try { basicDataSource.close(); } catch (final SQLException e) { } } return null; } static List<JdbcDatabaseFactory> databaseFactories() { return IoFactory.factories(JdbcDatabaseFactory.class); } static JdbcDatabaseFactory databaseFactory(final DataSource dataSource) { final String productName = JdbcUtils.getProductName(dataSource); return databaseFactory(productName); } static JdbcDatabaseFactory databaseFactory(final Map<String, ? extends Object> config) { final String url = (String)config.get("url"); if (url == null) { throw new IllegalArgumentException("The url parameter must be specified"); } else { for (final JdbcDatabaseFactory databaseFactory : databaseFactories()) { if (databaseFactory.canOpenUrl(url)) { return databaseFactory; } } throw new IllegalArgumentException("Database factory not found for " + url); } } static JdbcDatabaseFactory databaseFactory(final String productName) { for (final JdbcDatabaseFactory databaseFactory : databaseFactories()) { if (databaseFactory.getProductName().equals(productName)) { return databaseFactory; } } return null; } static DataSource dataSource(final Map<String, Object> config) { final JdbcDatabaseFactory databaseFactory = JdbcDatabaseFactory.databaseFactory(config); return databaseFactory.newDataSource(config); } static DataSource dataSource(final String url, final String username, final String password) { final Map<String, Object> config = new HashMap<>(); config.put("url", url); config.put("user", username); config.put("password", password); return dataSource(config); } @Override default boolean canOpenPath(final Path path) { return false; } @Override default boolean canOpenUrl(final String url) { if (url.startsWith("jdbc:" + getVendorName() + ":")) { return true; } else { return false; } } List<FieldDefinition> getConnectionFieldDefinitions(); /** * Get the map from connection name to JDBC URL for the database driver. For * example in Oracle this will be connections loaded from the TNSNAMES.ora file. * @return */ default Map<String, String> getConnectionUrlMap() { return Collections.emptyMap(); } default String getConnectionValidationQuery() { return "SELECT 1"; } String getDriverClassName(); String getProductName(); @Override Class<? extends RecordStore> getRecordStoreInterfaceClass( Map<String, ? extends Object> connectionProperties); @Override default List<Pattern> getUrlPatterns() { return Collections.singletonList(Pattern.compile("jdbc:" + getVendorName() + ":.+")); } String getVendorName(); @Override default boolean isAvailable() { return true; } default DataSource newDataSource(final Map<String, ? extends Object> config) { try { final Map<String, Object> newConfig = new HashMap<>(config); final String url = (String)newConfig.remove("url"); final String user = (String)newConfig.remove("user"); String password = (String)newConfig.remove("password"); if (Property.hasValue(password)) { password = PasswordUtil.decrypt(password); } final DataSourceImpl dataSource = new DataSourceImpl(); dataSource.setAccessToUnderlyingConnectionAllowed(true); dataSource.setDriverClassName(getDriverClassName()); dataSource.setUsername(user); dataSource.setPassword(password); dataSource.setUrl(url); dataSource.setValidationQuery(getConnectionValidationQuery()); final int minPoolSize = Maps.getInteger(config, "minPoolSize", -1); newConfig.remove("minPoolSize"); dataSource.setMinIdle(minPoolSize); dataSource.setMaxIdle(-1); final int maxPoolSize = Maps.getInteger(config, "maxPoolSize", 10); newConfig.remove("maxPoolSize"); dataSource.setMaxTotal(maxPoolSize); final int maxWaitMillis = Maps.getInteger(config, "waitTimeout", 10); newConfig.remove("waitTimeout"); dataSource.setMaxWaitMillis(maxWaitMillis); final boolean validateConnection = Maps.getBool(config, "validateConnection", true); newConfig.remove("validateConnection"); dataSource.setTestOnCreate(validateConnection); // dataSource.setTestOnBorrow(validateConnection); final int inactivityTimeout = Maps.getInteger(config, "inactivityTimeout", 60); newConfig.remove("inactivityTimeout"); dataSource.setMinEvictableIdleTimeMillis(inactivityTimeout * 1000); dataSource.setTimeBetweenEvictionRunsMillis(inactivityTimeout * 1000); for (final Entry<String, Object> property : newConfig.entrySet()) { final String name = property.getKey(); final Object value = property.getValue(); try { Property.setSimple(dataSource, name, value); } catch (final Throwable t) { Logs.debug(this, "Unable to set data source property " + name + " = " + value + " for " + url, t); } } return dataSource; } catch (final Throwable e) { throw new IllegalArgumentException("Unable to create data source for " + config, e); } } JdbcRecordStore newRecordStore(DataSource dataSource); @Override JdbcRecordStore newRecordStore(Map<String, ? extends Object> connectionProperties); }