java/jdbc-dk-driver/src/info/globalcode/sql/dk/jdbc/Driver.java
author František Kučera <franta-hg@frantovo.cz>
Tue, 26 Feb 2019 18:19:49 +0100
branchv_0
changeset 236 a3ec71fa8e17
parent 235 8ce612cca4d8
permissions -rw-r--r--
Avoid reusing/rewriting the DB connection properties.
There was weird random errors while testing connection to multiple DB in parallel when one of them was meta connection to same DB connection.
Two kinds of exception: 1) missing password 2) „Passing DB password as CLI parameter is insecure!“
franta-hg@171
     1
/**
franta-hg@171
     2
 * SQL-DK
franta-hg@192
     3
 * Copyright © 2015 František Kučera (frantovo.cz)
franta-hg@171
     4
 *
franta-hg@171
     5
 * This program is free software: you can redistribute it and/or modify
franta-hg@171
     6
 * it under the terms of the GNU General Public License as published by
franta-hg@171
     7
 * the Free Software Foundation, either version 3 of the License, or
franta-hg@171
     8
 * (at your option) any later version.
franta-hg@171
     9
 *
franta-hg@171
    10
 * This program is distributed in the hope that it will be useful,
franta-hg@171
    11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
franta-hg@171
    12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
franta-hg@171
    13
 * GNU General Public License for more details.
franta-hg@171
    14
 *
franta-hg@171
    15
 * You should have received a copy of the GNU General Public License
franta-hg@171
    16
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
franta-hg@171
    17
 */
franta-hg@192
    18
package info.globalcode.sql.dk.jdbc;
franta-hg@171
    19
franta-hg@192
    20
import info.globalcode.sql.dk.Constants;
franta-hg@192
    21
import info.globalcode.sql.dk.configuration.Configuration;
franta-hg@192
    22
import info.globalcode.sql.dk.configuration.ConfigurationException;
franta-hg@192
    23
import info.globalcode.sql.dk.configuration.DatabaseDefinition;
franta-hg@192
    24
import info.globalcode.sql.dk.configuration.Loader;
franta-hg@192
    25
import info.globalcode.sql.dk.configuration.Properties;
franta-hg@192
    26
import info.globalcode.sql.dk.configuration.Property;
franta-hg@192
    27
import java.sql.Connection;
franta-hg@171
    28
import java.sql.DriverManager;
franta-hg@171
    29
import java.sql.DriverPropertyInfo;
franta-hg@171
    30
import java.sql.SQLException;
franta-hg@171
    31
import java.sql.SQLFeatureNotSupportedException;
franta-hg@171
    32
import java.util.logging.Level;
franta-hg@171
    33
import java.util.logging.Logger;
franta-hg@171
    34
franta-hg@171
    35
/**
franta-hg@192
    36
 * <p>
franta-hg@192
    37
 * Meta JDBC driver that works as redirector/router. It loads SQL-DK's configuration file and
franta-hg@192
    38
 * instantiates a real JDBC connection (PostgreSQL, MariDB etc.) of given name.
franta-hg@192
    39
 * </p>
franta-hg@192
    40
 *
franta-hg@192
    41
 * <p>
franta-hg@192
    42
 * Raison d'être: user can define his/her database connections just once (in SQL-DK's configuration)
franta-hg@192
    43
 * and use them in many other programs – there is no need to define the connection (hostname,
franta-hg@192
    44
 * username, password, properties…) in each application again and again. User will simply connect to
franta-hg@192
    45
 * e.g. <code>jdbc:sql-dk://my-connection</code> and this driver reads parameters of
franta-hg@192
    46
 * <code>my-connection</code> from SQL-DK's configuration and returns real driver implementation.
franta-hg@192
    47
 * </p>
franta-hg@192
    48
 *
franta-hg@192
    49
 * <p>
franta-hg@192
    50
 * Of course, the real JDBC driver for given database is still needed on the class path.
franta-hg@192
    51
 * </p>
franta-hg@192
    52
 *
franta-hg@192
    53
 * <p>
franta-hg@192
    54
 * TODO: current version is quite heavy-weight, because it includes whole SQL-DK source tree. Some
franta-hg@195
    55
 * refactoring and separation is desired to provide more light-weight JDBC driver. However the
franta-hg@195
    56
 * public interface and behavior of this driver should remain unchanged.
franta-hg@192
    57
 * </p>
franta-hg@171
    58
 *
franta-hg@171
    59
 * @author Ing. František Kučera (frantovo.cz)
franta-hg@171
    60
 */
franta-hg@171
    61
public class Driver implements java.sql.Driver {
franta-hg@171
    62
franta-hg@171
    63
	private static final Logger log = Logger.getLogger(Driver.class.getName());
franta-hg@171
    64
franta-hg@192
    65
	private final Loader loader = new Loader();
franta-hg@192
    66
franta-hg@171
    67
	static {
franta-hg@171
    68
		try {
franta-hg@171
    69
			DriverManager.registerDriver(new Driver());
franta-hg@171
    70
		} catch (SQLException e) {
franta-hg@171
    71
			log.log(Level.SEVERE, "Unable to register JDBC driver", e);
franta-hg@171
    72
		}
franta-hg@171
    73
	}
franta-hg@171
    74
franta-hg@171
    75
	@Override
franta-hg@192
    76
	public Connection connect(String url, java.util.Properties info) throws SQLException {
franta-hg@177
    77
		if (acceptsURL(url)) {
franta-hg@192
    78
			log.log(Level.FINER, "Loading SQL-DK configuration for URL: {0}", url);
franta-hg@192
    79
			String name = extractDatabaseName(url);
franta-hg@192
    80
			log.log(Level.FINE, "Loading SQL-DK configuration for name: {0}", name);
franta-hg@192
    81
franta-hg@192
    82
			try {
franta-hg@192
    83
				return getConnection(name, info);
franta-hg@192
    84
			} catch (ConfigurationException e) {
franta-hg@192
    85
				log.log(Level.SEVERE, "Unable to load SQL-DK configuration for name: {0}. Is it defined in {1}?", new Object[]{name, Constants.CONFIG_FILE});
franta-hg@192
    86
				throw new SQLException("Unable to load SQL-DK configuration for name: " + name, e);
franta-hg@192
    87
			}
franta-hg@177
    88
		} else {
franta-hg@235
    89
			// The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given URL.
franta-hg@235
    90
			return null;
franta-hg@177
    91
		}
franta-hg@171
    92
	}
franta-hg@171
    93
franta-hg@192
    94
	private Connection getConnection(String connectionName, java.util.Properties info) throws SQLException, ConfigurationException {
franta-hg@192
    95
		Configuration c = loader.loadConfiguration();
franta-hg@192
    96
		DatabaseDefinition dd = c.getDatabase(connectionName);
franta-hg@192
    97
franta-hg@192
    98
		if (acceptsURL(dd.getUrl())) {
franta-hg@192
    99
			log.log(Level.SEVERE, "SQL-DK meta JDBC driver loops to itself: {0} → {1} Please check {2}", new Object[]{connectionName, dd.getUrl(), Constants.CONFIG_FILE});
franta-hg@192
   100
			throw new ConfigurationException("SQL-DK meta JDBC driver loops to itself.");
franta-hg@192
   101
		} else {
franta-hg@192
   102
			return Loader.jdbcConnect(dd, translate(info));
franta-hg@192
   103
		}
franta-hg@192
   104
	}
franta-hg@192
   105
franta-hg@192
   106
	private String extractDatabaseName(String url) {
franta-hg@192
   107
		return url.split("//", 2)[1];
franta-hg@192
   108
	}
franta-hg@192
   109
franta-hg@192
   110
	/**
franta-hg@192
   111
	 * TODO: refactor/move, reuse
franta-hg@192
   112
	 *
franta-hg@192
   113
	 * @param info
franta-hg@192
   114
	 * @return
franta-hg@192
   115
	 */
franta-hg@192
   116
	private Properties translate(java.util.Properties info) {
franta-hg@192
   117
		Properties properties = new Properties();
franta-hg@192
   118
franta-hg@192
   119
		for (String name : info.stringPropertyNames()) {
franta-hg@192
   120
			String value = info.getProperty(name);
franta-hg@192
   121
			properties.add(new Property(name, value));
franta-hg@192
   122
		}
franta-hg@192
   123
franta-hg@192
   124
		return properties;
franta-hg@171
   125
	}
franta-hg@171
   126
franta-hg@171
   127
	@Override
franta-hg@192
   128
	public boolean acceptsURL(String url) throws SQLException {
franta-hg@192
   129
		return url != null && url.startsWith("jdbc:sql-dk://");
franta-hg@192
   130
	}
franta-hg@192
   131
franta-hg@192
   132
	@Override
franta-hg@192
   133
	public DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException {
franta-hg@171
   134
		return new DriverPropertyInfo[0];
franta-hg@171
   135
	}
franta-hg@171
   136
franta-hg@171
   137
	@Override
franta-hg@171
   138
	public int getMajorVersion() {
franta-hg@171
   139
		return 0;
franta-hg@171
   140
	}
franta-hg@171
   141
franta-hg@171
   142
	@Override
franta-hg@171
   143
	public int getMinorVersion() {
franta-hg@171
   144
		return 1;
franta-hg@171
   145
	}
franta-hg@171
   146
franta-hg@171
   147
	@Override
franta-hg@171
   148
	public boolean jdbcCompliant() {
franta-hg@171
   149
		return false;
franta-hg@171
   150
	}
franta-hg@171
   151
franta-hg@171
   152
	@Override
franta-hg@171
   153
	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
franta-hg@171
   154
		throw new SQLFeatureNotSupportedException("Not supported yet.");
franta-hg@171
   155
	}
franta-hg@171
   156
franta-hg@171
   157
}