yang
yang

Reputation: 508

Integrating Lucene in play framework

I'm trying to integrate Lucene in a web application. (Not for full text indexing, but for quick search and sorting. ) I created a service:

trait Index {
  def indexAd(a: Ad): Unit
}

@Singleton
class ConcreteIndex @Inject()(conf: Configuration) extends Index {
  val dir =     FSDirectory.open(FileSystems.getDefault.getPath(conf.getString("index.dir").get))
  val writer = new IndexWriter(dir, new IndexWriterConfig(new StandardAnalyzer))
    override def indexAd(a: Ad): Unit = {
        val doc = new Document
        ...
    }
}

and try to use it in controllers:

@Singleton
class AdsController @Inject()(cache: CacheApi, index:Index) extends Controller {
  ...
}

But the injection is not successful. I got

Error injecting constructor, org.apache.lucene.store.LockObtainFailedException: 
 Lock held by this virtual machine: .../backend/index/write.lock

I tried to delete the lock file and run again fresh. It still throws the same exception. Could anyone help me on this? I'm using Lucene 6.2.0. Play 2.5.x, scala 2.11.8

Upvotes: 2

Views: 207

Answers (1)

Mikesname
Mikesname

Reputation: 8901

You may need to ensure the IndexWriter is closed on shutdown in order to clear the lock. This is potentially an expensive operation, so you might want to tie the index writer lifecycle to that of your Play application, starting it in the constructor of your (singleton) ConcreteIndex instance, and shutting it down by adding a stop hook to an injected ApplicationLifecycle instance. For example:

@ImplementedBy(classOf[ConcreteIndex])
trait Index {
  def index(s: String): Unit
}

@Singleton
case class ConcreteIndex @Inject()(conf: Configuration,
                                   lifecycle: play.api.inject.ApplicationLifecycle) extends Index {

  private val dir = FSDirectory.open(FileSystems.getDefault.getPath(conf.getString("index.dir").get))
  private val writer = new IndexWriter(dir, new IndexWriterConfig(new StandardAnalyzer()))

  // Add a lifecycle stop hook that will be called when the Play
  // server is cleanly shut down...
  lifecycle.addStopHook(() => scala.concurrent.Future.successful(writer.close()))

  // For good measure you could also add a JVM runtime shutdown hook
  // which should be called even if the Play server is terminated.
  // If the writer is already closed this will be a no-op.
  Runtime.getRuntime.addShutdownHook(new Thread() { 
    override def run() = writer.close()
  })

  def index(s: String): Unit = ???
}

Upvotes: 2

Related Questions