How to run ScalaTest with Guice DI and Slick? - scala

I don't know how to configure GuiceApplicationBuilder in such a way, that I am able to load controllers that require a DatabaseConfigProvider to be injected.
I'd like to specify an alternative postgres database for testing, or an in memory database (if that is possible).
Code
class User
extends MySpecs
with OneAppPerTest
{
override def newAppForTest( testData: TestData ) = new GuiceApplicationBuilder()
// Somehow bind a database here, I guess?
.build()
"A test" should "test" in
{
val result = Application.instanceCache[api.controller.User]
.apply( app )
.list()( FakeRequest() )
...
}
}
Stacktrace
[info] - should return an entity *** FAILED ***
[info] com.google.inject.ConfigurationException: Guice configuration errors:
[info]
[info] 1) No implementation for play.api.db.slick.DatabaseConfigProvider was bound.
[info] while locating play.api.db.slick.DatabaseConfigProvider
[info] for parameter 1 at api.controller.User.<init>(User.scala:22)
[info] while locating api.controller.User
[info]
[info] 1 error
[info] at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1042)
[info] at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1001)
[info] at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
[info] at play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:321)
[info] at play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:316)
[info] at play.api.Application$$anonfun$instanceCache$1.apply(Application.scala:234)
[info] at play.api.Application$$anonfun$instanceCache$1.apply(Application.scala:234)
[info] at play.utils.InlineCache.fresh(InlineCache.scala:69)
[info] at play.utils.InlineCache.apply(InlineCache.scala:55)
[info] ...

You need to add a configuration to your GuiceApplicationBuilder(), then everything should be handled automatically by play framework. Something like this should help:
val app = new GuiceApplicationBuilder()
.configure(
Configuration.from(
Map(
"slick.dbs.YOURDBNAME.driver" -> "slick.driver.H2Driver$",
"slick.dbs.YOURDBNAME.db.driver" -> "org.h2.Driver",
"slick.dbs.YOURDBNAME.db.url" -> "jdbc:h2:mem:",
"slick.dbs.default.driver" -> "slick.driver.MySQLDriver$",
"slick.dbs.default.db.driver" -> "com.mysql.jdbc.Driver"
)
)
)
.in(Mode.Test)
.build()

There is a little bit of setting up in this approach but the final result is fair. First of all start with implementing your own GuiceApplicationLoader by extending it. See my answer how to implement it. Why your own application loader? You can specify different configs/modules per Prod/Dev/Test modes - as well as different data sources. Your main application.conf wouldn't have data source configured. Instead, you would move it to environment specific configurations which would be merged with main configuration by application loader anyway. Your dev.conf would look as follows:
slick.dbs {
default {
driver = "slick.driver.PostgresDriver$",
db {
driver = "org.postgresql.Driver",
url = "jdbc:postgresql://localhost:5432/dev",
user = "postgres"
password = "postgres"
}
}
}
And the trick now is to use same data source name, in this case default, for all other configurations (the database url, drivers, credentials etc. would be different). With such setup, your evolutions will be applied to your test and dev database. Your test.conf could look as follows:
slick.dbs {
default {
// in memory configuration
}
}
In your tests, use WithApplicationLoader with your custom application loader instead and thats it.
#RunWith(classOf[JUnitRunner])
class ApplicationSpec extends Specification {
"Application" should {
"return text/html ok for home" in new WithApplicationLoader(new CustomApplicationLoader) {
val home = route(FakeRequest(routes.ApplicationController.home())).get
status(home) must equalTo(OK)
contentType(home) must beSome.which(_ == "text/html")
}
}
}
Within the test itself, you have an access to app: Application value:
val service = app.injector.instanceOf(classOf[IService])

Related

display source directories from sbt task for current project as well as source directories of project it dependsOn

sbt sourceDirectories
only displays source directories of current project, but doesn’t display source directories of projects that it depends using
dependsOn(ProjectRef)
Below is the simplified task.
lazy val showAllSourceDirs = taskKey[Unit]("show source directories of all projects")
showAllSourceDirs := {
val projectRefs = loadedBuild.value.allProjectRefs.map(_._1)
projectRefs foreach { projectRef =>
/*
Below line is giving IllegalArgumentException exception :-
[error] java.lang.IllegalArgumentException: Could not find proxy for projectRef: sbt.ProjectRef in
List(value projectRef, value $anonfun, method sbtdef$1, method $sbtdef, object $7fb70afe92bc9a6fedc3,
package <empty>, package <root>) (currentOwner= method $sbtdef )
*/
val sources = (projectRef / Compile / sourceDirectories).value
sources.foreach( println )
}
}
Link for simplified project to reproduce problem :-
https://github.com/moglideveloper/Example
Steps :

Go to ApiSpec directory from command line and run below command :-
sbt showAllSourceDirs
Expected output : print all source directories of Api and ApiSpec project
Actual output : throws java.lang.IllegalArgumentException
I believe you cannot do that, because sbt works with macros. What you can do here, is adding sourceDirectories of Api into sourceDirectories of ApiSpec. This means, that if you add the following into your sbt:
Compile / sourceDirectories ++= (apiModule / Compile / sourceDirectories).value
Then, when running:
sbt sourceDirectories
You get the output:
[info] * /workspace/games/Example/ApiSpec/src/main/scala-2.12
[info] * /workspace/games/Example/ApiSpec/src/main/scala
[info] * /workspace/games/Example/ApiSpec/src/main/java
[info] * /workspace/games/Example/ApiSpec/target/scala-2.12/src_managed/main
[info] * /workspace/games/Example/Api/src/main/scala-2.12
[info] * /workspace/games/Example/Api/src/main/scala
[info] * /workspace/games/Example/Api/src/main/java
[info] * /workspace/games/Example/Api/target/scala-2.12/src_managed/main
There is one thing you need to notice - you are overriding the current sourceDirectories, so be sure you are not using it anywhere else.
Another note, is that you need to add this line on every dependency you have. So I am not sure how big is your project, and how feasible that is.
If you'd like to have a different task, you can do that, bur use the modules themselves, and not thru reflection, again due to macros.
lazy val showAllSourceDirs = taskKey[Unit]("show source directories of all projects")
showAllSourceDirs := {
println((apiSpecProject / Compile / sourceDirectories).value)
println((apiModule / Compile / sourceDirectories).value)
}

How to overcome Scalatra initialization issue: NoSuchMethodError: javax.servlet.ServletContext.getFilterRegistration?

This is my first time using Scalatra, and I'm using it outside of SBT (building and running using mill). I get the following error which seems to be about a missing dependency.
2018.05.23 18:26:30 [main] INFO org.scalatra.servlet.ScalatraListener - The cycle class name from the config: ScalatraBootstrap
2018.05.23 18:26:30 [main] INFO org.scalatra.servlet.ScalatraListener - Initializing life cycle class: ScalatraBootstrap
2018.05.23 18:26:30 [main] ERROR org.scalatra.servlet.ScalatraListener - Failed to initialize scalatra application at
java.lang.NoSuchMethodError: javax.servlet.ServletContext.getFilterRegistration(Ljava/lang/String;)Ljavax/servlet/FilterRegistration;
at org.scalatra.servlet.RichServletContext.mountFilter(RichServletContext.scala:162)
at org.scalatra.servlet.RichServletContext.mount(RichServletContext.scala:85)
at org.scalatra.servlet.RichServletContext.mount(RichServletContext.scala:93)
at org.scalatra.servlet.RichServletContext.mount(RichServletContext.scala:90)
at ScalatraBootstrap.init(ScalatraBootstrap.scala:8)
at org.scalatra.servlet.ScalatraListener.configureCycleClass(ScalatraListener.scala:66)
at org.scalatra.servlet.ScalatraListener.contextInitialized(ScalatraListener.scala:22)
at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:890)
at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:558)
at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:853)
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:370)
at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1497)
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1459)
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:785)
at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:287)
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:545)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:138)
at org.eclipse.jetty.server.Server.start(Server.java:419)
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:108)
at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:113)
at org.eclipse.jetty.server.Server.doStart(Server.java:386)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at com.example.JettyLauncher$.main(JettyLauncher.scala:20)
at com.example.JettyLauncher.main(JettyLauncher.scala)
Here are the dependencies I'm using:
val jettyVersion = "9.4.10.v20180503"
def ivyDeps = Agg(
ivy"org.scalatra::scalatra:2.6.3",
ivy"javax.servlet:servlet-api:2.5",
ivy"org.eclipse.jetty:jetty-server:$jettyVersion",
ivy"org.eclipse.jetty:jetty-servlet:$jettyVersion",
ivy"org.eclipse.jetty:jetty-webapp:$jettyVersion",
)
My JettyLauncher is a striaght copy from the web site, so far, except I changed the resourceBase to be something that actually exists (but it didn't help):
object JettyLauncher { // this is my entry object as specified in sbt project definition
def main(args: Array[String]) {
val port = if(System.getenv("PORT") != null) System.getenv("PORT").toInt else 5001
val server = new Server(port)
val context = new WebAppContext()
context setContextPath "/"
context.setResourceBase("repeater")
context.addEventListener(new ScalatraListener)
context.addServlet(classOf[DefaultServlet], "/")
server.setHandler(context)
server.start
server.join
}
}
My LifeCycle class is also fairly minimal:
class ScalatraBootstrap extends LifeCycle {
override def init(context: ServletContext) {
context mount (new RepeatAll, "/*")
}
}
UPDATE
I changed to using ScalatraServlet instead of ScalatraServlet, but get a similar issue:
2018.05.23 18:39:24 [main] ERROR org.scalatra.servlet.ScalatraListener - Failed to initialize scalatra application at
java.lang.NoSuchMethodError: javax.servlet.ServletContext.getServletRegistration(Ljava/lang/String;)Ljavax/servlet/ServletRegistration;
at org.scalatra.servlet.RichServletContext.mountServlet(RichServletContext.scala:127)
at org.scalatra.servlet.RichServletContext.mount(RichServletContext.scala:84)
at org.scalatra.servlet.RichServletContext.mount(RichServletContext.scala:93)
at org.scalatra.servlet.RichServletContext.mount(RichServletContext.scala:90)
at ScalatraBootstrap.init(ScalatraBootstrap.scala:8)
Update 2
Another important part of the stacktrace I missed posting earlier:
2018.05.23 18:39:24 [main] WARN org.eclipse.jetty.webapp.WebAppContext - Failed startup of context o.e.j.w.WebAppContext#3d74bf60{/,file:///home/brandon/workspace/sbh/repeater,UNAVAILABLE}
java.lang.NoSuchMethodError: javax.servlet.ServletContext.getServletRegistration(Ljava/lang/String;)Ljavax/servlet/ServletRegistration;
at org.scalatra.servlet.RichServletContext.mountServlet(RichServletContext.scala:127)
at org.scalatra.servlet.RichServletContext.mount(RichServletContext.scala:84)
at org.scalatra.servlet.RichServletContext.mount(RichServletContext.scala:93)
I tried putting a WEB-INF/web.xml under repeater as specified above, but same result.

Play error: Unable to create injector

I'm trying to create a simple restful api using play and scala. I've been able to create some basic routes, get it to send back json responses, and validate json sent in a POST. Now I want to add persistence to a postgres database, which is where it's starting to break down.
I started off by adding the db info to my application.conf, creating an evolution to create a table. I then attempted to hit my GET endpoint to see if it runs the evolution, but I instead get an error. Here is the error output from the html (I was using my browser to hit the endpoint):
CreationException: Unable to create injector, see the following errors:
1) A binding to play.api.db.DBApi was already configured at play.api.db.DBModule.bindings(DBModule.scala:25):
Binding(interface play.api.db.DBApi to ProviderConstructionTarget(class play.api.db.DBApiProvider)) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1).
at play.api.db.slick.evolutions.EvolutionsModule.bindings(EvolutionsModule.scala:15):
Binding(interface play.api.db.DBApi to ConstructionTarget(class play.api.db.slick.evolutions.internal.DBApiAdapter) in interface javax.inject.Singleton) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1)
com.google.inject.CreationException: Unable to create injector, see the following errors:
com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:466)
com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:155)
com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:107)
com.google.inject.Guice.createInjector(Guice.java:96)
com.google.inject.Guice.createInjector(Guice.java:84)
play.api.inject.guice.GuiceBuilder.injector(GuiceInjectorBuilder.scala:181)
play.api.inject.guice.GuiceApplicationBuilder.build(GuiceApplicationBuilder.scala:123)
play.api.inject.guice.GuiceApplicationLoader.load(GuiceApplicationLoader.scala:21)
play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1$$anonfun$2.apply(DevServerStart.scala:168)
play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1$$anonfun$2.apply(DevServerStart.scala:164)
play.utils.Threads$.withContextClassLoader(Threads.scala:21)
play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:164)
play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:131)
scala.Option.map(Option.scala:146)
play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1.apply(DevServerStart.scala:131)
play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1.apply(DevServerStart.scala:129)
scala.util.Success.flatMap(Try.scala:231)
play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1.apply(DevServerStart.scala:129)
play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1.apply(DevServerStart.scala:121)
scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1423)
java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:902)
java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1689)
java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1644)
java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Here is my route (please note that I am not actually attempting to connect to the database yet):
GET /cities controllers.CityController.index
Which connects to this controller method:
def index = Action {
Ok(Json.obj("status" ->"OK", "message" -> "You did the thing!"))
}
And here is my application.conf, comments removed for brevity:
akka {
}
play.crypto.secret = "changeme"
play.modules {
}
play.i18n {
langs = [ "en" ]
}
play.http {
session {
}
flash {
}
}
play.server.netty {
}
play.ws {
ssl {
}
}
play.cache {
}
play.filters {
cors {
}
csrf {
}
headers {
}
hosts {
}
}
play.evolutions {
}
play.db {
prototype {
}
}
db {
db.default.driver=org.postgresql.Driver
db.default.jdbcUrl="jdbc:postgresql://localhost:5432/transit_service"
db.default.username="redacted"
db.default.password="redacted"
db.default.logSql=true
}
My evolution is a fairly simple create table statement that lives under conf/evolutions/default/1.sql.
I would appreciate any insight into why this error is occurring and how I can get the evolution to actually run.
As Matthias Eckhart pointed out in a comment: Removing the jdbc entry from libraryDependencies in build.sbt was the solution to the problem.

Using forked JVM when using runner in SBT

My goal is to avoid FileNotFound exception when loading resource from inside JAR and running application from JAR in SBT.
I have a following task:
val generateSource = TaskKey[Unit]("generateSource")
val generateSourceImpl : Def.Initialize[Task[Unit]] = {
Def.task {
val localGeneratorJar = file(s"${baseDirectory.value.getParentFile.toString}/generator/repo/.sbt/generator.jar")
if (!localGeneratorJar.exists()) {
sys.error(s"Generator not found, please execute 'sbt publishLocal' in 'generator' project first.")
}
val r = (runner in Compile).value
val opts = Seq( /* options here */ )
toError(r.run(“com.example.AllMightyCompiler", Seq(localGeneratorJar), opts, streams.value.log))
}
}
Issue happens when AllMightyCompiler loads e.g. XSD schema from ‘localGeneratorJar’ itself and JAR is modified between the runs of the sbt task.
Typical workflow is:
$ sbt
> generateSource
# all good, but
# when in meantime ‘generator.jar is modified’ task invocation will be
> generateSource
root > generateSource
[info] Running AllMightyCompiler —opts=dummy
com.example.GeneratorException: org.xml.sax.SAXParseException; schema_reference.4: Failed to read schema document 'jar:file:/Users/Me/Development/Work/generator/repo/.sbt/generator.jar!/module.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
at com.example.ModuleLoader.<init>(ModuleLoader.java:127)
at com.example.AllMightyCompiler.loadModel(Compiler.java:394)
at com.example.AllMightyCompiler.generateImpl(Compiler.java:264)
at com.example.AllMightyCompiler.generate(Compiler.java:144)
at com.example.AllMightyCompiler.main(Compiler.java:134)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at sbt.Run.invokeMain(Run.scala:72)
at sbt.Run.run0(Run.scala:65)
at sbt.Run.sbt$Run$$execute$1(Run.scala:54)
at sbt.Run$$anonfun$run$1.apply$mcV$sp(Run.scala:58)
at sbt.Run$$anonfun$run$1.apply(Run.scala:58)
at sbt.Run$$anonfun$run$1.apply(Run.scala:58)
at sbt.Logger$$anon$4.apply(Logger.scala:90)
at sbt.TrapExit$App.run(TrapExit.scala:244)
at java.lang.Thread.run(Thread.java:744)
Caused by: org.xml.sax.SAXParseException; schema_reference.4: Failed to read schema document 'jar:file:/Users/Me/Development/Work/generator/repo/.sbt/generator.jar!/module.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:437)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:347)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaErr(XSDHandler.java:4166)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaError(XSDHandler.java:4149)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getSchemaDocument1(XSDHandler.java:2479)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getSchemaDocument(XSDHandler.java:2187)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(XSDHandler.java:573)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(XMLSchemaLoader.java:616)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:574)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:540)
at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:255)
at javax.xml.validation.SchemaFactory.newSchema(SchemaFactory.java:638)
at javax.xml.validation.SchemaFactory.newSchema(SchemaFactory.java:670)
at n4.generator.util.ModuleLoader.<init>(ModuleLoader.java:121)
... 17 more
As I’ve figured out there are 2 possible solutions:
Disabling caching by JAR URLs directly in com.example.ModuleLoader
Somehow fork runner in my sbt task ‘generateSources'
Option 1 can be accomplished with:
new URLConnection(schemaURL) {
#Override
public void connect() throws IOException {
// Do nothing
}
}.setDefaultUseCaches(false);
But, option 1 is not what I want while it requires modification of ‘generator.jar’, which I can do now, but maybe not in future.
Thus only remaining option is using forked JVM for runner in task, but when I set in build.sbt:
fork in Compile := true
obtained runner stil use same JVM as sbt itself.
Does anybody have an idea how can I force runner to start in separate JVM?
P.S. I only suppose that forking will help base on http://www.scala-sbt.org/0.13/docs/Running-Project-Code.html because when I run my task first time I see:
Exception: sbt.TrapExitSecurityException thrown from the UncaughtExceptionHandler in thread "run-main-0"
This is due to fact that AllMightyCompiler currently ends with following line:
public static void main(String... args) {
… lot of code ...
System.exit(exit);
}
After a short discussion on sbt/sbt gitter channel I was told to use:
fork in (Compile, run) := true
but at the same time acquire runner as:
val r = (runner in (Compile, run)).value
That worked :)

slick 3.0.0 with HikariCP driver not loaded - IllegalAccessException: AbstractHikariConfig can not access a member with modifiers "private"

I am trying to use tminglei/slick-pg v9.0.0 with slick 3.0.0 and am getting an IllegalAccessException:
akka.actor.ActorInitializationException: exception during creation
at akka.actor.ActorInitializationException$.apply(Actor.scala:166) ~[akka-actor_2.11-2.3.11.jar:na]
...
Caused by: java.lang.RuntimeException: driverClassName specified class 'com.github.tminglei.MyPostgresDriver$' could not be loaded
at com.zaxxer.hikari.AbstractHikariConfig.setDriverClassName(AbstractHikariConfig.java:370) ~[HikariCP-java6-2.3.8.jar:na]
at slick.jdbc.HikariCPJdbcDataSource$$anonfun$forConfig$18.apply(JdbcDataSource.scala:145) ~[slick_2.11-3.0.0.jar:na]
at slick.jdbc.HikariCPJdbcDataSource$$anonfun$forConfig$18.apply(JdbcDataSource.scala:145) ~[slick_2.11-3.0.0.jar:na]
at scala.Option.map(Option.scala:146) ~[scala-library-2.11.7.jar:na]
at slick.jdbc.HikariCPJdbcDataSource$.forConfig(JdbcDataSource.scala:145) ~[slick_2.11-3.0.0.jar:na]
at slick.jdbc.HikariCPJdbcDataSource$.forConfig(JdbcDataSource.scala:135) ~[slick_2.11-3.0.0.jar:na]
at slick.jdbc.JdbcDataSource$.forConfig(JdbcDataSource.scala:35) ~[slick_2.11-3.0.0.jar:na]
at slick.jdbc.JdbcBackend$DatabaseFactoryDef$class.forConfig(JdbcBackend.scala:223) ~[slick_2.11-3.0.0.jar:na]
at slick.jdbc.JdbcBackend$$anon$3.forConfig(JdbcBackend.scala:33) ~[slick_2.11-3.0.0.jar:na]
...
Caused by: java.lang.IllegalAccessException: Class com.zaxxer.hikari.AbstractHikariConfig can not access a member of class com.github.tminglei.MyPostgresDriver$ with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:109) ~[na:1.7.0_79]
at java.lang.Class.newInstance(Class.java:373) ~[na:1.7.0_79]
at com.zaxxer.hikari.AbstractHikariConfig.setDriverClassName(AbstractHikariConfig.java:366) ~[HikariCP-java6-2.3.8.jar:na]
... 43 common frames omitted
HikariCP is the default connection pool in slick 3.0.0
I have defined the driver class much like in the example:
trait MyPostgresDriver extends ExPostgresDriver with PgArraySupport
with PgEnumSupport
with PgRangeSupport
with PgHStoreSupport
with PgSearchSupport{
override val api = new MyAPI {}
//////
trait MyAPI extends API
with ArrayImplicits
with RangeImplicits
with HStoreImplicits
with SearchImplicits
with SearchAssistants
}
object MyPostgresDriver extends MyPostgresDriver
My database config is pretty straightforward [excerpt of typesafe config follows]:
slick.dbs.default {
driver="com.github.tminglei.MyPostgresDriver$"
db {
driver="org.postgresql.Driver"
url="jdbc:postgresql://hostname:port/dbname"
user=user
password="pass"
}
}
It does not seem as if it should not work, and yet...
Should I change my driver class somehow? Is it something else?
Note: as can be seen in the stacktrace I am using
Java 1.7.0_79
Scala 2.11.7
akka 2.3.11 (I share the config instance for slick and akka)
slick 3.0.0
HikariCP-java6 2.3.8
tminglei's slick-pg_core 0.9.0
Lastly, when debugging thru the jdk code at Class.class (decompiled line 143)
Constructor tmpConstructor1 = this.cachedConstructor;
I get the following (toString'ed) value (as shown by intellij):
private com.github.tminglei.MyPostgresDriver$()
Could this be indicative of the problem? If so how should I fix it?
EDIT
I have replaced the custom driver configuration with the stock PostgresDriver like so:
slick.dbs.default {
driver="slick.driver.PostgresDriver$"
db {
driver="org.postgresql.Driver"
url="jdbc:postgresql://hostname:port/dbname"
user=user
password="pass"
}
}
The error is the same:
akka.actor.ActorInitializationException: exception during creation
...
Caused by: java.lang.RuntimeException: driverClassName specified class 'slick.driver.PostgresDriver$' could not be loaded
...
Caused by: java.lang.IllegalAccessException: Class com.zaxxer.hikari.AbstractHikariConfig can not access a member of class slick.driver.PostgresDriver$ with modifiers "private"
I had a similar problem.
I think you are using Database.forConfig("slick.dbs.default") but your config file is in DatabaseConfig format.
Instead, try using:
val dbConfig: DatabaseConfig[PostgresDriver] = DatabaseConfig.forConfig("slick.dbs.default")
val db = dbConfig.db