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

sbt.io.WatchService.scala Maven / Gradle / Ivy

/*
 * sbt IO
 * Copyright Scala Center, Lightbend, and Mark Harrah
 *
 * Licensed under Apache License 2.0
 * SPDX-License-Identifier: Apache-2.0
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package sbt.io

import java.nio.file.{
  ClosedWatchServiceException,
  FileSystems,
  WatchEvent,
  WatchKey,
  Path => JPath,
  WatchService => JWatchService
}
import java.util.concurrent.TimeUnit

import sbt.internal.nio

import scala.annotation.tailrec
import scala.collection.JavaConverters._
import scala.collection.{ immutable, mutable }
import scala.concurrent.duration.{ Duration, FiniteDuration }
import scala.util.Properties

object WatchService {

  /**
   * Adapts a Java `WatchService` to be used with sbt's `WatchService` infrastructure.
   * @param service The `WatchService` to use.
   */
  implicit final class WatchServiceAdapter(service: JWatchService)
      extends WatchService
      with Unregisterable {
    private var closed: Boolean = false
    private val registered: mutable.Map[JPath, WatchKey] = mutable.Map.empty

    override def init(): Unit =
      ()

    override def pollEvents(): Map[WatchKey, immutable.Seq[WatchEvent[JPath]]] = {
      val values = registered.synchronized(registered.values.toIndexedSeq)
      values.flatMap { k =>
        val events = k.pollEvents()
        if (events.isEmpty) None
        else Some((k, events.asScala.asInstanceOf[Seq[WatchEvent[JPath]]].toIndexedSeq))
      }.toMap
    }

    @tailrec
    override def poll(timeout: Duration): WatchKey =
      if (timeout.isFinite) {
        service.poll(timeout.toMillis, TimeUnit.MILLISECONDS)
      } else {
        service.poll(1000L, TimeUnit.MILLISECONDS) match {
          case null => poll(timeout)
          case key  => key
        }
      }

    override def register(path: JPath, events: WatchEvent.Kind[JPath]*): WatchKey = {
      if (closed) throw new ClosedWatchServiceException
      else {
        registered.synchronized {
          registered.get(path) match {
            case None =>
              val key = path.register(service, events: _*)
              registered += path -> key
              key
            case Some(key) =>
              key
          }
        }
      }
    }

    override def unregister(path: JPath): Unit = {
      if (closed) throw new ClosedWatchServiceException
      registered.synchronized {
        registered.get(path) match {
          case Some(key) =>
            key.cancel()
            registered -= path
          case _ =>
        }
      }
      ()
    }

    override def close(): Unit = {
      closed = true
      service.close()
    }

    override def toString: String = service.toString
  }
  private[sbt] def default: WatchService =
    if (Properties.isMac) new MacOSXWatchService else FileSystems.getDefault.newWatchService
  def polling(delay: FiniteDuration): WatchService = new PollingWatchService(delay)
}

/**
 * A service that will monitor the file system for file creation, deletion
 * and modification.
 */
trait WatchService {

  /** Initializes the watchservice. */
  def init(): Unit

  /**
   * Retrieves all the events and groups them by watch key.
   * Does not wait if no event is available.
   * @return The pending events.
   */
  def pollEvents(): Map[WatchKey, immutable.Seq[WatchEvent[JPath]]]

  /**
   * Retrieves the next `WatchKey` that has a `WatchEvent` waiting. Waits
   * until the `timeout` is expired is no such key exists.
   * @param timeout Maximum time to wait
   * @return The next `WatchKey` that received an event, or null if no such
   *         key exists.
   */
  def poll(timeout: Duration): WatchKey

  /**
   * Registers a path to be monitored.
   * @param path The path to monitor.
   * @param events The events that should be registered.
   * @return A `WatchKey`, that represents a token of registration.
   */
  def register(path: JPath, events: WatchEvent.Kind[JPath]*): WatchKey

  /**
   * Closes this `WatchService`.
   */
  def close(): Unit
}

private[sbt] trait Unregisterable { self: WatchService =>

  /**
   * Unregisters a monitored path.
   * @param path The monitored path.
   */
  def unregister(path: JPath): Unit
}

private[sbt] class MacOSXWatchService extends sbt.internal.io.MacOSXWatchService
private[sbt] class PollingWatchService(delay: FiniteDuration) extends nio.PollingWatchService(delay)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy