/
DriverDataSource.scala
131 lines (111 loc) · 5.36 KB
/
DriverDataSource.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package slick.jdbc
import java.io.{Closeable, PrintWriter}
import java.sql.*
import java.util.Properties
import java.util.logging.Logger
import scala.beans.BeanProperty
import scala.jdk.CollectionConverters.*
import scala.util.control.NonFatal
import slick.SlickException
import slick.util.{ignoreFollowOnError, ClassLoaderUtil, Logging}
import javax.sql.DataSource
/** A DataSource that wraps the DriverManager API. It can be configured as a Java Bean and used
* both stand-alone and as a source for a connection pool. */
class DriverDataSource(
/** The JDBC URL (required) */
@BeanProperty @volatile var url: String,
/** Optional user name */
@BeanProperty @volatile var user: String = null,
/** Optional password */
@BeanProperty @volatile var password: String = null,
/** Optional connection properties */
@BeanProperty @volatile var properties: Properties = null,
/** Name of the `java.sql.Driver` class. This must be set unless a `driverObject` is set
* directly or the driver is already registered with the DriverManager. */
@BeanProperty @volatile var driverClassName: String = null,
/** When `close()` is called, try to deregister a driver that was registered by this instance. */
@BeanProperty @volatile var deregisterDriver: Boolean = false,
/** The JDBC driver to use. If this is set, `driverClassName` will be ignored. */
@volatile var driverObject: Driver = null,
/** The ClassLoader that is used to load `driverClassName` */
@volatile var classLoader: ClassLoader = ClassLoaderUtil.defaultClassLoader
) extends DataSource with Closeable with Logging {
def this() = this(null)
// Alias for `driverClassName`
def getDriver: String = driverClassName
def setDriver(s: String): Unit = driverClassName = s
// State that gets initialized by `init`
@volatile private[this] var registered: Boolean = false
@volatile private[this] var initialized = false
@volatile private[this] var driver: Driver = _
@volatile private[this] var connectionProps: Properties = _
def init: Unit = if(!initialized) {
this.synchronized {
if(!initialized) {
try {
if(url eq null) throw new SQLException("Required parameter \"url\" missing in DriverDataSource")
driver = if(driverObject eq null) {
if(driverClassName ne null) {
DriverManager.getDrivers.asScala.find(_.getClass.getName == driverClassName).getOrElse {
logger.debug(s"Driver $driverClassName not already registered; trying to load it")
val cl = classLoader.loadClass(driverClassName)
registered = true
DriverManager.getDrivers.asScala.find(_.getClass.getName == driverClassName).getOrElse {
logger.debug(s"Loaded driver $driverClassName but it did not register with DriverManager; trying to instantiate directly")
try cl.getConstructor().newInstance().asInstanceOf[Driver] catch { case ex: Exception =>
logger.debug(s"Instantiating driver class $driverClassName failed; asking DriverManager to handle URL $url", ex)
try DriverManager.getDriver(url) catch { case ex: Exception =>
throw new SlickException(s"Driver $driverClassName does not know how to handle URL $url", ex)
}
}
}
}
} else try DriverManager.getDriver(url) catch { case ex: Exception =>
throw new SlickException(s"No driver specified and DriverManager does not know how to handle URL $url", ex)
}
} else driverObject
if(!driver.acceptsURL(url)) {
close()
throw new SlickException(s"Driver ${driver.getClass.getName} does not know how to handle URL $url")
}
connectionProps = propsWithUserAndPassword(properties, user, password)
} catch { case NonFatal(ex) =>
try close() catch ignoreFollowOnError
throw ex
} finally initialized = true
}
}
}
private[this] def propsWithUserAndPassword(p: Properties, user: String, password: String): Properties = {
if((p ne null) && (user eq null) && (password eq null)) p else {
val p2 = new Properties(p)
if(user ne null) p2.setProperty("user", user)
if(password ne null) p2.setProperty("password", password)
p2
}
}
def getConnection: Connection = {
init
driver.connect(url, connectionProps)
}
def getConnection(username: String, password: String): Connection = {
init
driver.connect(url, propsWithUserAndPassword(connectionProps, username, password))
}
def close(): Unit = if(registered && deregisterDriver && (driver ne null)) {
DriverManager.deregisterDriver(driver)
registered = false
}
def getLoginTimeout: Int = DriverManager.getLoginTimeout
def setLoginTimeout(seconds: Int): Unit = DriverManager.setLoginTimeout(seconds)
def getLogWriter: PrintWriter = throw new SQLFeatureNotSupportedException()
def setLogWriter(out: PrintWriter): Unit = throw new SQLFeatureNotSupportedException()
def getParentLogger: Logger = {
init
driver.getParentLogger
}
def isWrapperFor(iface: Class[_]): Boolean = iface.isInstance(this)
def unwrap[T](iface: Class[T]): T =
if(iface.isInstance(this)) this.asInstanceOf[T]
else throw new SQLException(getClass.getName+" is not a wrapper for "+iface)
}