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

com.nawforce.apexlink.indexer.Monitor.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2023 Certinia Inc. All rights reserved.
 */

package com.nawforce.apexlink.indexer

import better.files
import com.nawforce.apexlink.api.ServerOps
import com.nawforce.pkgforce.path.PathLike
import com.nawforce.runtime.platform.Path
import io.methvin.better.files.RecursiveFileMonitor
import io.methvin.watcher.hashing.FileHasher

import java.nio.file.WatchEvent
import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global

/* Workspace FS change monitor. OS X fsevents only supports recursive monitoring which makes selective monitoring of
 parts of a workspace difficult since you can not use a non-recursive monitor of parent directories to track changes
 to the root of the sub-directories you are specifically interested in, such as a interesting sub-directory being
 created after you have started monitoring. To work around this we use a single recursive monitor for the whole
 workspace but support quick filtering by subdirectories. */
class Monitor(workspace: PathLike) {

  private val workspacePath               = workspace.asInstanceOf[Path]
  private val callbacks                   = mutable.Map[PathLike, String => Unit]()
  private var callbackKeys: Set[PathLike] = Set.empty

  // Workspace wide recursive monitor, dispatches via callbacks on case-sensitive match of absolute path
  private val workspaceMonitor = {
    if (ServerOps.getIndexerConfiguration.enabled) {
      Some(new RecursiveFileMonitor(workspacePath.native, Some(FileHasher.LAST_MODIFIED_TIME)) {
        override def onEvent(
          eventType: WatchEvent.Kind[java.nio.file.Path],
          file: files.File,
          count: Int
        ): Unit = {
          if (!file.isDirectory) {
            val path = new Path(file.path)
            path
              .findParentOf(callbackKeys)
              .foreach(key => {
                callbacks(key)(path.toString)
              })
          }
        }
      })
    } else {
      None
    }
  }
  workspaceMonitor.foreach(_.start())

  /* Add monitoring for a sub-directory of the workspace. The monitored directory can not be a child of an existing
   * monitored directory. */
  def monitor(path: PathLike, onFileChanged: String => Unit): Unit = {
    if (workspace != path && !workspace.isParentOf(path))
      throw new IllegalArgumentException(
        s"Path ${path.toString} must be the whole workspace or a subdirectory of ${workspace.toString}"
      )

    callbacks.keys
      .find(p => Path(p).isParentOf(path))
      .foreach(monitoredParent => {
        throw new IllegalArgumentException(
          s"Path ${path.toString} can not be a subdirectory of another monitored path $monitoredParent"
        )
      })

    callbacks.put(path, onFileChanged)
    callbackKeys = callbacks.keySet.toSet
  }

  /* Terminate the monitoring, mostly useful for testing */
  def stop(): Unit = {
    workspaceMonitor.foreach(_.stop())
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy