2015-06-08 12:26:19 +02:00
|
|
|
/*
|
2017-01-04 17:37:10 +01:00
|
|
|
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
2015-06-08 12:26:19 +02:00
|
|
|
*/
|
|
|
|
|
package akka.persistence.query
|
|
|
|
|
|
|
|
|
|
import java.util.concurrent.atomic.AtomicReference
|
|
|
|
|
import akka.actor._
|
|
|
|
|
import akka.event.Logging
|
2015-06-08 12:26:19 +02:00
|
|
|
import scala.annotation.tailrec
|
2015-06-08 12:26:19 +02:00
|
|
|
import scala.util.Failure
|
2015-08-19 11:14:43 +02:00
|
|
|
import com.typesafe.config.Config
|
2015-06-08 12:26:19 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Persistence extension for queries.
|
|
|
|
|
*/
|
|
|
|
|
object PersistenceQuery extends ExtensionId[PersistenceQuery] with ExtensionIdProvider {
|
|
|
|
|
/**
|
|
|
|
|
* Java API.
|
|
|
|
|
*/
|
|
|
|
|
override def get(system: ActorSystem): PersistenceQuery = super.get(system)
|
|
|
|
|
|
|
|
|
|
def createExtension(system: ExtendedActorSystem): PersistenceQuery = new PersistenceQuery(system)
|
|
|
|
|
|
|
|
|
|
def lookup() = PersistenceQuery
|
|
|
|
|
|
|
|
|
|
/** INTERNAL API. */
|
2015-09-14 11:08:22 +02:00
|
|
|
private[persistence] case class PluginHolder(
|
|
|
|
|
scaladslPlugin: scaladsl.ReadJournal, javadslPlugin: akka.persistence.query.javadsl.ReadJournal)
|
|
|
|
|
extends Extension
|
2015-06-08 12:26:19 +02:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class PersistenceQuery(system: ExtendedActorSystem) extends Extension {
|
|
|
|
|
import PersistenceQuery._
|
|
|
|
|
|
|
|
|
|
private val log = Logging(system, getClass)
|
|
|
|
|
|
|
|
|
|
/** Discovered query plugins. */
|
|
|
|
|
private val readJournalPluginExtensionIds = new AtomicReference[Map[String, ExtensionId[PluginHolder]]](Map.empty)
|
|
|
|
|
|
|
|
|
|
/**
|
2015-09-14 11:08:22 +02:00
|
|
|
* Scala API: Returns the [[akka.persistence.query.scaladsl.ReadJournal]] specified by the given
|
|
|
|
|
* read journal configuration entry.
|
2015-06-08 12:26:19 +02:00
|
|
|
*/
|
2015-09-14 11:08:22 +02:00
|
|
|
final def readJournalFor[T <: scaladsl.ReadJournal](readJournalPluginId: String): T =
|
|
|
|
|
readJournalPluginFor(readJournalPluginId).scaladslPlugin.asInstanceOf[T]
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Java API: Returns the [[akka.persistence.query.javadsl.ReadJournal]] specified by the given
|
|
|
|
|
* read journal configuration entry.
|
|
|
|
|
*/
|
|
|
|
|
final def getReadJournalFor[T <: javadsl.ReadJournal](clazz: Class[T], readJournalPluginId: String): T =
|
|
|
|
|
readJournalPluginFor(readJournalPluginId).javadslPlugin.asInstanceOf[T]
|
|
|
|
|
|
|
|
|
|
@tailrec private def readJournalPluginFor(readJournalPluginId: String): PluginHolder = {
|
2015-06-08 12:26:19 +02:00
|
|
|
val configPath = readJournalPluginId
|
|
|
|
|
val extensionIdMap = readJournalPluginExtensionIds.get
|
|
|
|
|
extensionIdMap.get(configPath) match {
|
|
|
|
|
case Some(extensionId) ⇒
|
2015-09-14 11:08:22 +02:00
|
|
|
extensionId(system)
|
2015-06-08 12:26:19 +02:00
|
|
|
case None ⇒
|
|
|
|
|
val extensionId = new ExtensionId[PluginHolder] {
|
2015-09-14 11:08:22 +02:00
|
|
|
override def createExtension(system: ExtendedActorSystem): PluginHolder = {
|
|
|
|
|
val provider = createPlugin(configPath)
|
|
|
|
|
PluginHolder(provider.scaladslReadJournal(), provider.javadslReadJournal())
|
|
|
|
|
}
|
2015-06-08 12:26:19 +02:00
|
|
|
}
|
|
|
|
|
readJournalPluginExtensionIds.compareAndSet(extensionIdMap, extensionIdMap.updated(configPath, extensionId))
|
2015-09-14 11:08:22 +02:00
|
|
|
readJournalPluginFor(readJournalPluginId) // Recursive invocation.
|
2015-06-08 12:26:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-14 11:08:22 +02:00
|
|
|
private def createPlugin(configPath: String): ReadJournalProvider = {
|
2016-06-02 14:06:57 +02:00
|
|
|
require(
|
|
|
|
|
!isEmpty(configPath) && system.settings.config.hasPath(configPath),
|
2015-06-08 12:26:19 +02:00
|
|
|
s"'reference.conf' is missing persistence read journal plugin config path: '${configPath}'")
|
|
|
|
|
val pluginConfig = system.settings.config.getConfig(configPath)
|
|
|
|
|
val pluginClassName = pluginConfig.getString("class")
|
2015-06-08 12:26:19 +02:00
|
|
|
log.debug(s"Create plugin: ${configPath} ${pluginClassName}")
|
2015-06-08 12:26:19 +02:00
|
|
|
val pluginClass = system.dynamicAccess.getClassFor[AnyRef](pluginClassName).get
|
|
|
|
|
|
2015-10-12 09:32:23 +02:00
|
|
|
def instantiate(args: collection.immutable.Seq[(Class[_], AnyRef)]) =
|
|
|
|
|
system.dynamicAccess.createInstanceFor[ReadJournalProvider](pluginClass, args)
|
|
|
|
|
|
2016-12-13 19:37:03 +01:00
|
|
|
instantiate((classOf[ExtendedActorSystem], system) :: (classOf[Config], pluginConfig) ::
|
|
|
|
|
(classOf[String], configPath) :: Nil)
|
|
|
|
|
.recoverWith {
|
|
|
|
|
case x: NoSuchMethodException ⇒ instantiate(
|
|
|
|
|
(classOf[ExtendedActorSystem], system) :: (classOf[Config], pluginConfig) :: Nil)
|
|
|
|
|
}
|
2015-10-12 09:32:23 +02:00
|
|
|
.recoverWith { case x: NoSuchMethodException ⇒ instantiate((classOf[ExtendedActorSystem], system) :: Nil) }
|
|
|
|
|
.recoverWith { case x: NoSuchMethodException ⇒ instantiate(Nil) }
|
2015-09-14 11:08:22 +02:00
|
|
|
.recoverWith {
|
2016-12-13 19:37:03 +01:00
|
|
|
case ex: Exception ⇒ Failure.apply(
|
|
|
|
|
new IllegalArgumentException("Unable to create read journal plugin instance for path " +
|
|
|
|
|
s"[$configPath], class [$pluginClassName]!", ex))
|
2015-09-14 11:08:22 +02:00
|
|
|
}.get
|
2015-06-08 12:26:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Check for default or missing identity. */
|
|
|
|
|
private def isEmpty(text: String) = text == null || text.length == 0
|
|
|
|
|
}
|
|
|
|
|
|