All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.cypher.testing.impl.FeatureDatabaseManagementService.scala Maven / Gradle / Ivy

/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.cypher.testing.impl

import org.neo4j.configuration.Config
import org.neo4j.configuration.GraphDatabaseSettings.DEFAULT_DATABASE_NAME
import org.neo4j.configuration.connectors.BoltConnector
import org.neo4j.configuration.connectors.HttpConnector
import org.neo4j.configuration.helpers.SocketAddress
import org.neo4j.cypher.testing.api.CypherExecutor
import org.neo4j.cypher.testing.api.CypherExecutor.TransactionConfig
import org.neo4j.cypher.testing.api.CypherExecutorFactory
import org.neo4j.cypher.testing.api.CypherExecutorTransaction
import org.neo4j.cypher.testing.api.StatementResult
import org.neo4j.cypher.testing.impl.driver.DriverCypherExecutorFactory
import org.neo4j.cypher.testing.impl.embedded.EmbeddedCypherExecutorFactory
import org.neo4j.cypher.testing.impl.http.HttpCypherExecutorFactory
import org.neo4j.dbms.api.DatabaseManagementService
import org.neo4j.driver.NotificationConfig
import org.neo4j.fabric.executor.FabricExecutor
import org.neo4j.graphdb.schema.IndexDefinition
import org.neo4j.graphdb.schema.IndexType
import org.neo4j.internal.kernel.api.procs.QualifiedName
import org.neo4j.kernel.api.Kernel
import org.neo4j.kernel.api.procedure.CallableProcedure.BasicProcedure
import org.neo4j.kernel.api.procedure.CallableUserAggregationFunction
import org.neo4j.kernel.api.procedure.GlobalProcedures
import org.neo4j.kernel.impl.api.KernelTransactions
import org.neo4j.kernel.impl.factory.GraphDatabaseFacade
import org.neo4j.kernel.impl.query.QueryExecutionEngine

import scala.util.Using

object FeatureDatabaseManagementService {

  trait TestBase {
    def dbms: FeatureDatabaseManagementService
    def createBackingDbms(config: Config): DatabaseManagementService
    def baseConfig: Config.Builder = Config.newBuilder()
    def testApiKind: TestApiKind
  }

  sealed trait TestApiKind

  object TestApiKind {
    case object Bolt extends TestApiKind
    case object Embedded extends TestApiKind
    case object Http extends TestApiKind
  }

  trait TestUsingBolt extends TestBase {
    override val testApiKind: TestApiKind = TestApiKind.Bolt

    override def dbms: FeatureDatabaseManagementService = {
      val config = baseConfig
        .set(BoltConnector.enabled, java.lang.Boolean.TRUE)
        .set(BoltConnector.listen_address, new SocketAddress("localhost", 0))
        .build()
      val managementService = createBackingDbms(config)
      val executorFactory = DriverCypherExecutorFactory(managementService, config)
      FeatureDatabaseManagementService(managementService, executorFactory)
    }
  }

  trait TestUsingEmbedded extends TestBase {
    override val testApiKind: TestApiKind = TestApiKind.Embedded

    override def dbms: FeatureDatabaseManagementService = {
      val config = baseConfig.build()
      val managementService = createBackingDbms(config)
      val executorFactory = EmbeddedCypherExecutorFactory(managementService, config)
      FeatureDatabaseManagementService(managementService, executorFactory)
    }
  }

  trait TestUsingHttp extends TestBase {
    override val testApiKind: TestApiKind = TestApiKind.Http

    override def dbms: FeatureDatabaseManagementService = {
      val config = baseConfig
        .set(HttpConnector.enabled, java.lang.Boolean.TRUE)
        .set(HttpConnector.listen_address, new SocketAddress("localhost", 0))
        .build()
      val managementService = createBackingDbms(config)
      val executorFactory = HttpCypherExecutorFactory(managementService, config)
      FeatureDatabaseManagementService(managementService, executorFactory)
    }
  }
}

case class FeatureDatabaseManagementService(
  private val databaseManagementService: DatabaseManagementService,
  val executorFactory: CypherExecutorFactory,
  private val databaseName: Option[String] = None,
  private val notificationConfig: NotificationConfig = NotificationConfig.defaultConfig()
) {

  private val droppableIndexTypes = Set(
    IndexType.TEXT,
    IndexType.POINT,
    IndexType.RANGE,
    IndexType.FULLTEXT
  )
  def closeFactory(): Unit = executorFactory.close()

  private val database: GraphDatabaseFacade =
    databaseManagementService.database(databaseName.getOrElse(DEFAULT_DATABASE_NAME)).asInstanceOf[GraphDatabaseFacade]

  private val cypherExecutor = createExecutor()

  private lazy val maybeFabricExecutor = {
    val resolver = database.getDependencyResolver
    // Fabric executor is present only in composite databases
    if (resolver.containsDependency(classOf[FabricExecutor]))
      Option(resolver.resolveDependency(classOf[FabricExecutor]))
    else Option.empty
  }

  private lazy val kernel = database.getDependencyResolver.resolveDependency(classOf[Kernel])
  private lazy val globalProcedures = database.getDependencyResolver.provideDependency(classOf[GlobalProcedures]).get()
  private lazy val executionEngine = database.getDependencyResolver.resolveDependency(classOf[QueryExecutionEngine])

  private def createExecutor() = databaseName match {
    case Some(name) => executorFactory.executor(name)
    case None       => executorFactory.executor()
  }

  def registerProcedure(procedure: BasicProcedure): Unit = kernel.registerProcedure(procedure)

  def registerProcedure(procedure: Class[_]): Unit = globalProcedures.registerProcedure(procedure)

  def registerFunction(function: Class[_]): Unit = globalProcedures.registerFunction(function)

  def registerAggregationFunction(function: Class[_]): Unit = globalProcedures.registerAggregationFunction(function)

  def registerUserAggregation(function: CallableUserAggregationFunction): Unit =
    kernel.registerUserAggregationFunction(function)

  def clearFabricQueryCache(dbName: String): Unit =
    maybeFabricExecutor.map(fe => fe.clearQueryCachesForDatabase(dbName))

  def clearQueryCaches(): Unit = executionEngine.clearQueryCaches()
  def clearExecutableQueryCache(): Unit = executionEngine.clearExecutableQueryCache()

  def clearCompilerCaches(): Unit = executionEngine.clearCompilerCache()

  def terminateAllTransactions(): Unit = {
    database.getDependencyResolver.resolveDependency(classOf[KernelTransactions]).terminateTransactions()
  }

  def dropIndexesAndConstraints(): Unit = {
    val tx = database.beginTx()
    try {
      val schema = tx.schema()
      schema.getConstraints.forEach(c => c.drop());
      schema.getIndexes.forEach(i => if (shouldDrop(i)) i.drop());
      tx.commit()
    } finally {
      tx.close()
    }
  }

  private def shouldDrop(index: IndexDefinition): Boolean = {
    // Avoid dropping indexes that are part of the default installation
    // Like the node label index and relationship type index.
    droppableIndexTypes.contains(index.getIndexType)
  }

  def unregisterProcedures(procedures: Seq[QualifiedName]): Unit = {
    procedures.foreach(globalProcedures.unregister)
  }

  def begin(): CypherExecutorTransaction = cypherExecutor.beginTransaction()

  def begin(conf: TransactionConfig): CypherExecutorTransaction = {
    cypherExecutor.beginTransaction(conf)
  }

  /**
   * If a session based executor is used (driver) this creates an executor with a new session.
   * 
   * Caller is responsible for closing the executor.
   */
  def withNewSession(): CypherExecutor = if (cypherExecutor.sessionBased) createExecutor() else cypherExecutor

  def execute[T](statement: String, parameters: Map[String, Object], converter: StatementResult => T): T =
    cypherExecutor.execute(statement, parameters, converter)

  def execute[T](statement: String, converter: StatementResult => T): T =
    execute(statement, Map.empty, converter)

  def executeInNewSession[T](statement: String, converter: StatementResult => T): T = {
    if (cypherExecutor.sessionBased) {
      Using.resource(createExecutor())(e => e.execute(statement, Map.empty, converter))
    } else {
      execute(statement, converter)
    }
  }

  def shutdown(): Unit = {
    cypherExecutor.close()
    executorFactory.close()
    databaseManagementService.shutdown()
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy