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

com.sandinh.couchbase.CBCluster.scala Maven / Gradle / Ivy

package com.sandinh.couchbase

import java.lang
import javax.inject._

import com.couchbase.client.java.CouchbaseAsyncCluster
import com.couchbase.client.java.document.Document
import com.couchbase.client.java.env.CouchbaseEnvironment
import com.couchbase.client.java.transcoder.Transcoder
import com.sandinh.couchbase.transcoder._
import com.typesafe.config.Config

import scala.jdk.CollectionConverters._
import scala.concurrent.{Await, Future}
import scala.util.Try
import com.sandinh.couchbase.JavaConverters._
import com.sandinh.rx.Implicits._

import scala.concurrent.duration._

/** @note ensure call #disconnect() at the end of application life */
@Singleton
class CBCluster @Inject() (config: Config) {
  val env: CouchbaseEnvironment = CbEnvBuilder(config)

  val asJava: CouchbaseAsyncCluster =
    CouchbaseAsyncCluster.fromConnectionString(
      env,
      config.getString("com.sandinh.couchbase.connectionString")
    )

  /** Open bucket with typesafe config load from key com.sandinh.couchbase.buckets.`bucket`
    * @param bucket use as a subkey of typesafe config for open bucket.
    * @param legacyEncodeString set = true to choose CompatStringTranscoderLegacy, false to choose CompatStringTranscoder
    * @param transcoders extra customize transcoders.
    *
    * @note JsTranscoder & CompatStringTranscoderLegacy | CompatStringTranscoder is auto passed to underlying `CouchbaseAsyncCluster.openBucket`,
    * so don't need to be passed into `transcoders` param.
    *
    * @note couchbase will cache Bucket by name.
    * So, if you need both legacyEncodeString & not-legacyEncodeString transcoder then you MUST create another cluster.
    * see example in com.sandinh.couchbase.CompatStringSpec.bk1Compat
    */
  def openBucket(
    bucket: String,
    legacyEncodeString: Boolean,
    transcoders: Transcoder[_ <: Document[_], _]*
  ): Future[ScalaBucket] = {
    val cfg = config.getConfig(s"com.sandinh.couchbase.buckets.$bucket")
    val name = Try { cfg.getString("name") } getOrElse bucket
    val pass = cfg.getString("password")
    val stringTranscoder =
      if (legacyEncodeString) CompatStringTranscoderLegacy
      else CompatStringTranscoder
    val trans = transcoders :+ JsTranscoder :+ stringTranscoder
    asJava.openBucket(name, pass, trans.asJava).scMap(_.asScala).toFuture
  }

  /** @note You should never perform long-running blocking operations inside of an asynchronous stream (e.g. inside of maps or flatMaps)
    * @see https://issues.couchbase.com/browse/JVMCBC-79
    */
  def openBucketSync(
    bucket: String,
    legacyEncodeString: Boolean,
    transcoders: Transcoder[_ <: Document[_], _]*
  ): ScalaBucket =
    Await.result(
      openBucket(bucket, legacyEncodeString, transcoders: _*),
      env.connectTimeout.millis
    )

  /** openBucket(bucket, legacyEncodeString = true) */
  def openBucket(bucket: String): Future[ScalaBucket] =
    openBucket(bucket, legacyEncodeString = true)

  /** openBucketSync(bucket, legacyEncodeString = true)
    * @note You should never perform long-running blocking operations inside of an asynchronous stream (e.g. inside of maps or flatMaps)
    * @see https://issues.couchbase.com/browse/JVMCBC-79
    */
  def openBucketSync(bucket: String): ScalaBucket =
    openBucketSync(bucket, legacyEncodeString = true)

  def disconnect(): Future[lang.Boolean] = asJava.disconnect().toFuture

  def disconnectSync(): Boolean = Await
    .result(
      asJava.disconnect().toFuture,
      env.disconnectTimeout.millis
    )
    .booleanValue
}

private object CbEnvBuilder {
  import java.util.concurrent.TimeUnit.MILLISECONDS
  import com.couchbase.client.java.env.DefaultCouchbaseEnvironment
  import DefaultCouchbaseEnvironment.Builder

  def apply(config: Config): CouchbaseEnvironment = {
    val b = DefaultCouchbaseEnvironment.builder()
    val c = config.getConfig("com.couchbase.timeout")

    def set(k: String, f: Long => Builder): Builder =
      if (c.hasPath(k)) f(c.getDuration(k, MILLISECONDS)) else b

    set("management", b.managementTimeout)
    set("query", b.queryTimeout)
    set("view", b.viewTimeout)
    set("kv", b.kvTimeout)
    set("connect", b.connectTimeout)
    set("disconnect", b.disconnectTimeout)
      .build()
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy