Issues with initializing service class that has dependency on running application on startup (play 2.4 / scala) - scala

I am currently digging into using Quartz in our play 2.4 application.
Initially, I tried initializing everything through Global object, and everything worked perfectly.
Now, I an trying to move away from Global utilize modules infrastructure.
Here is what I have until now.
JobSchedulingService
#Singleton
class JobSchedulingService #Inject()(lifecycle: ApplicationLifecycle) extends ClassLogger{
lazy val schedulerFactory = current.injector.instanceOf[StdSchedulerFactory]
lazy val scheduler = schedulerFactory.getScheduler
/**
* Let's make sure that scheduler shuts down properly
*/
lifecycle.addStopHook{ () =>
Future.successful{
if (scheduler.isStarted) {
scheduler.shutdown(true)
}
}
}
protected def init() : Unit = {
logger.info("Initializing scheduler...")
scheduler.start()
}
init()
}
SchedulerModule - here for initialization of the service above.
class SchedulerModule extends AbstractModule{
override def configure(): Unit = {
bind(classOf[JobSchedulingService]).asEagerSingleton
}
}
And in my application.conf I added:
play.modules.enabled += "scheduling.modules.SchedulerModule"
It looks pretty strait forward. However, when the app starts I am getting an exception:
2016-03-23 00:07:42,173 INFO s.JobSchedulingService - Initializing
scheduler... 2016-03-23 00:07:42,213 ERROR application -
! #6pfp72mh6 - Internal server error, for (GET) [/] ->
play.api.UnexpectedException: Unexpected exception[CreationException:
Unable to create injector, see the following errors:
1) Error injecting constructor, java.lang.RuntimeException: There is
no started application at
scheduling.JobSchedulingService.(JobSchedulingService.scala:15)
at
scheduling.modules.SchedulerModule.configure(SchedulerModule.scala:11)
(via modules: com.google.inject.util.Modules$OverrideModule ->
scheduling.modules.SchedulerModule) while locating
scheduling.JobSchedulingService
...
The thing is, in our app, the scheduler is based of off persistence job storage and should restart when the application restarts. Again, when I did it through Global, it worked perfectly.
How do I get around this problem? What is the correct way to initialize an instance on startup?
Thanks,

You should probably be using the dependency injection for everything. Inject the scheduler like..
class JobSchedulingService #Inject()(lifecycle: ApplicationLifecycle, schedulerFactory: StdSchedulerFactory) extends ClassLogger{
lazy val scheduler = schedulerFactory.getScheduler

Hmmm...
I think I just resolved it.
It seems like the problem was not the actual initialization of a class, but this line:
lazy val schedulerFactory = current.injector.instanceOf[StdSchedulerFactory]
once I changed it to:
lazy val schedulerFactory = new StdSchedulerFactory
it started working.
May that will help somebody

Related

Silhouette Unexpected exception playframework 2.6

I have a problem using Silhouette library in play framework,I am using guice for runtime dependency injection, there is an error when creating DelegableAuthInfoRepository which accepts a DelegableAuthInfoDAO as argument, it says DelegableAuthInfoDAO is not bound to a concrete instance even though I have done a binding in the configure method,here is the error I get when enabling the module
play.api.UnexpectedException: Unexpected exception[CreationException: Unable
to create injector, see the following errors:
1) No implementation for
com.mohiva.play.silhouette.persistence.
daos.DelegableAuthInfoDAO<com.mohiva.pla
y.silhouette.api.util.PasswordInfo> was bound.
while locating
com.mohiva.play.silhouette.persistence.daos
.DelegableAuthInfoDAO<com.mohiva.play
.silhouette.api.util.PasswordInfo>
for the 1st parameter of
modules.authModule.provideAuthInfoRepository(authModule.scala:112)
at modules.authModule.provideAuthInfoRepository(authModule.scala:112) (via
modules: com.google.inject.util.Modules$OverrideModule ->
modules.authModule)
Here is some of the configuration done in the module:
#Provides
def provideAuthInfoRepository(passwordInfoDAO: DelegableAuthInfoDAO[PasswordInfo]): AuthInfoRepository = {
new DelegableAuthInfoRepository(passwordInfoDAO)
}
override def configure() = {
bind(classOf[UserDAO]).asEagerSingleton()
bind(classOf[IdentityService[User]]).to(classOf[UserService])
bind(classOf[CacheLayer]).to(classOf[PlayCacheLayer])
bind(classOf[PasswordHasher]).toInstance(new BCryptPasswordHasher())
bind(classOf[EventBus]).toInstance(new EventBus)
bind(classOf[DelegableAuthInfoDAO[PasswordInfo]]).toInstance(new InMemoryAuthInfoDAO[PasswordInfo]())
bind(classOf[IDGenerator]).toInstance(new SecureRandomIDGenerator())
bind(classOf[FingerprintGenerator]).toInstance(new DefaultFingerprintGenerator(false))
bind(classOf[Clock]).toInstance(Clock())
}
this works:
bind(new TypeLiteral[DelegableAuthInfoDAO[PasswordInfo]]{}).toInstance(new InMemoryAuthInfoDAO[PasswordInfo])

Session doesn't work after upgrade to Play Framework 2.5.4, Scala

After upgrading my project to Play Framework 2.5.4 from 2.4.8, I'm having issue while working with session. There are to methods in my controller to test the issue:
def test = Action { implicit request =>
Redirect(controllers.routes.Carts.test2()).withSession(new Session(Map("user" -> "customer")))
}
def test2 = Action { implicit request =>
Ok(request.session.data.toString())
}
Basically test method adds session and redirects to test2 method. When I open page with test method url after redirect I'm getting what I need in the browser:
Map(user -> customer)
But if I refresh page after that I'm getting:
Map(csrfToken ->
ce1a6222484f378d38ab3534c2b400191270395d-1470238791014-c988ce3fe47259173166949a)
So, seems like session works for one request only and then overwrites with csrfToken. I have disabled all the filters. My class with filters looks like this:
class Filters #Inject() () extends HttpFilters {
val filters = Seq.empty
}
Can't understand what is wrong with my code. The same code was working fine before upgrade.
Check in ur application.conf if session.secure is true bring it to
play.http.session.secure=false

fail to load a native library using activator (Play Framework)

I'm trying to load a native library in my Play 2.4.x application. I have written a simple test that works fine both in the IDE (IntelliJ) and in SBT. In both case I'm setting the java.library.path to get the tests to run.
In the IDE, I set -Djava.library.path=$USER_HOME$/dev/lindoapi/bin/linux64 in the test run configuration.
As per the sbt documentation, my build.sbt is forking the JVM and setting the java.library.path.
javaOptions += "-Djava.library.path=/home/aczerwon/dev/lindoapi/bin/linux64"
fork := true
The following test passes just fine in both the IDE and from activator test.
class LindoApiSpec extends Specification {
System.loadLibrary("lindojni")
"The Lindo API" should {
"have a valid license" in {
val lindo = new LindoEnvironment()
lindo.apiVerion() must beSuccessfulTry.withValue("LINDO API Version 9.0.2120.225")
}
}
When outside of the testing context, I load the native library in Play's startup lifecycle.
object Global extends GlobalSettings {
override def beforeStart(app: Application) = {
System.loadLibrary("lindojni")
}
}
When I call that same method from the webapi (activator ~run), I'm getting an UnsatisfiedLinkError error.
1) Error injecting constructor, java.lang.UnsatisfiedLinkError: no lindojni in java.library.path
at play.api.GlobalPlugin.<init>(GlobalSettings.scala:262)
at play.api.GlobalPlugin.class(GlobalSettings.scala:262)
while locating play.api.GlobalPlugin
The web api looks like this:
class OptimizationApi extends Controller {
def version() = Action {
val lindo = new LindoEnvironment()
lindo.apiVerion() match {
case Success(version) => Ok(version)
case Failure(e) => BadRequest(e.getMessage)
}
}
}
I assumed that my build.sbt would fork the JVM and set the java.library.path for both test and run contexts. Any clues as to what I'm doing wrong?
New Information
When I start activator -Djava.library.path=$USER_HOME$/dev/lindoapi/bin/linux64 or set JAVA_OPTS, the call to System.loadLibrary(...) in the startup lifecycle passes. I still get the UnsatisfiedLinkError, but it happens later when I make a call to the native library via JNI. Very strange.
I found a solution to the issue here.
The native library and its java counterpart must be in the same class loader.
Create a class similar to:
public final class PlayNativeLibraryLoader {
public static void load(String libraryPath) {
System.load(libraryPath);
}
}
And now you can use it in the Play startup lifecycle.
object Global extends GlobalSettings {
override def beforeStart(app: Application) = {
PlayNativeLibraryLoader.load(app.getFile("./lib/lindoapi/linux64/liblindojni.so").getPath)
Logger.info("Lindo native library loaded")
}
}

Why does OSGI bundle with ActorSystemActivator fail with Logger timeout upon start?

I wrote an OSGI "Hello World" app in Scala using the akka-osgi_2.10-2.3.4 library and all the needed dependencies. Actually here are all the dependencies maven reports:
+- com.typesafe.akka:akka-actor_2.10:jar:2.3.4:compile
| \- com.typesafe:config:jar:1.2.1:compile
+- com.typesafe.akka:akka-slf4j_2.10:jar:2.3.4:compile
| \- org.slf4j:slf4j-api:jar:1.7.5:compile
+- org.apache.felix:org.osgi.core:jar:1.4.0:compile
+- com.typesafe.akka:akka-osgi_2.10:jar:2.3.4:compile
| +- org.osgi:org.osgi.core:jar:4.3.1:compile
| \- org.osgi:org.osgi.compendium:jar:4.3.1:compile
+- org.scala-lang:scala-library:jar:2.10.4:compile
The app uses the simplest possible Activator class that takes advantage of the default start and stop methods offered by ActorSystemActivator. The Activator class code looks as follows:
import akka.actor.ActorSystem
import org.osgi.framework.BundleContext
import akka.osgi.ActorSystemActivator
class Activator extends ActorSystemActivator {
def configure(bundleContext: BundleContext, system: ActorSystem): Unit = {
}
}
I use IBM JDK 1.7 and in the eclipse juno-64 (on ubuntu 12.04) environment I set bootdelegation in config.ini as described in the official documentation:
org.osgi.framework.bootdelegation=sun.misc
I started Eclipse with the -console argument and I deployed the bundled app in the OSGi framework. This is what I'm getting when I run ss in the OSGi console:
426 <<LAZY>> test-osgi_0.0.1.SNAPSHOT
The issue I have is with starting the bundle. When I try to start it:
osgi> start 426
I get an Akka timeout exception:
[WARN] [01/06/2015 15:04:02.530] [Gogo shell] [EventStream(akka://bundle-426-ActorSystem)] Logger log1-Logging$DefaultLogger did not respond within Timeout(25000 milliseconds) to InitializeLogger(bus)
error while starting up loggers
akka.ConfigurationException: Logger specified in config can't be loaded [akka.event.Logging$DefaultLogger] due to [akka.event.Logging$LoggerInitializationException: Logger log1-Logging$DefaultLogger did not respond with LoggerInitialized, sent instead [TIMEOUT]]
at akka.event.LoggingBus$$anonfun$4$$anonfun$apply$1.applyOrElse(Logging.scala:114)
at akka.event.LoggingBus$$anonfun$4$$anonfun$apply$1.applyOrElse(Logging.scala:113)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:33)
...
I checked online and I found issues similar to mine but none of the suggested solutions worked for me. I tried modifying the default logger response timeout to different values, I changed the DefaultLogger with other versions (DefaultOSGiLogger), I added the org.osgi.framework.system.packages.extra=sun.misc configuration, but nothing helped.
I also tried the akka-sample-osgi-dining-hakkers example but I ran in an OSGi nightmare...
What am I missing?
The information from the link attached by sschaef (thank you) helped solve the issue. It's a workaround that makes possible start the Akka system without hacking the dependency libraries. As mentioned in that thread, apparently is a class loader issue, and the solution (that I followed) uses a listener to activate the system.
I still have extended ActorSystemActivator (although this can be avoided as pointed out by sschaef) and I reused the code from the extended class in the overridden start and stop methods. Here is the code that worked for me:
class Activator extends ActorSystemActivator {
private var system: Option[ActorSystem] = None
#volatile var bundleListener: org.osgi.framework.BundleListener = null
def configure(bundleContext: BundleContext, system: ActorSystem): Unit = {}
override def getActorSystemName(context: BundleContext): String = "my-actor-system"
override def start(context: BundleContext) = {
bundleListener = new org.osgi.framework.BundleListener() {
override def bundleChanged(event: org.osgi.framework.BundleEvent) {
if (event.getBundle == context.getBundle) {
if (event.getType == org.osgi.framework.BundleEvent.STARTED) {
system = Some(OsgiActorSystemFactory(context, getActorSystemConfiguration(context)).createActorSystem(Option(getActorSystemName(context))))
system foreach (addLogServiceListener(context, _))
system foreach (configure(context, _))
}
}
}
}
context.addBundleListener(bundleListener)
}
override def stop(context: BundleContext) = {
if (bundleListener != null)
context.removeBundleListener(bundleListener)
system foreach (_.shutdown())
}
}

Custom event listener example in Grails documentation

I'm trying to add a custom GORM event listener class in Bootstrap.groovy, as described in the Grails documentation but its not working for me. Here is the code straight from the docs:
def init = {
application.mainContext.eventTriggeringInterceptor.datastores.each { k, datastore ->
applicationContext.addApplicationListener new MyPersistenceListener(datastore)
}
}
When I run it, the compiler complains that application and applicationContext are null. I've tried adding them as class level members but they don't get magically wired up service-style. The closest I've got so far is:
def grailsApplication
def init = { servletContext ->
def applicationContext = servletContext.getAttribute(ApplicationAttributes.APPLICATION_CONTEXT)
grailsApplication.mainContext.eventTriggeringInterceptor.datastores.each { k, datastore ->
applicationContext.addApplicationListener new GormEventListener(datastore)
}
}
But I still get errors: java.lang.NullPointerException: Cannot get property 'datastores' on null object.
Thanks for reading...
EDIT: version 2.2.1
If you do:
ctx.getBeansOfType(Datastore).values().each { Datastore d ->
ctx.addApplicationListener new MyPersistenceListener(d)
}
This should work without needing the Hibernate plugin installed
That looks like it should work, although I'd do it a bit differently. BootStrap.groovy does support dependency injection, so you can inject the grailsApplication bean, but you can also inject eventTriggeringInterceptor directly:
class BootStrap {
def grailsApplication
def eventTriggeringInterceptor
def init = { servletContext ->
def ctx = grailsApplication.mainContext
eventTriggeringInterceptor.datastores.values().each { datastore ->
ctx.addApplicationListener new MyPersistenceListener(datastore)
}
}
}
Here I still inject grailsApplication but only because I need access to the ApplicationContext to register listeners. Here's my listener (simpler than what the docs claim the simplest implementation would be btw ;)
import org.grails.datastore.mapping.core.Datastore
import org.grails.datastore.mapping.engine.event.AbstractPersistenceEvent
import org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener
class MyPersistenceListener extends AbstractPersistenceEventListener {
MyPersistenceListener(Datastore datastore) {
super(datastore)
}
protected void onPersistenceEvent(AbstractPersistenceEvent event) {
println "Event $event.eventType $event.entityObject"
}
boolean supportsEventType(Class eventType) { true }
}
Finally stumbled onto a working Bootstrap.groovy, thanks to this post but I don't think its the best way to do it, rather its a work around.
def init = { servletContext ->
def applicationContext = servletContext.getAttribute(ApplicationAttributes.APPLICATION_CONTEXT)
applicationContext.addApplicationListener new GormEventListener(applicationContext.mongoDatastore)
}
So basically I'm hard-coding the MongoDB datastore directly as opposed to iterating over the available ones, as the docs suggest.
To save you reading the comments to the first answer, the adapted version I provided in the Question (as well as Burt's answer) only works if the Hibernate plugin is installed but in my case I was using the MongoDB plugin so had no need for the Hibernate plugin (it in fact broke my app in other ways).