Skip to content

Commit

Permalink
Extract FileWatcher and test case
Browse files Browse the repository at this point in the history
Signed-off-by: reidspencer <reid.spencer@yoppworks.com>
  • Loading branch information
reid-spencer committed Nov 13, 2022
1 parent 3738879 commit 67ce7c8
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import java.nio.file.attribute.FileTime
import java.nio.file.Files
import java.nio.file.Path
import java.time.Instant
import scala.collection.mutable.ArrayBuffer
import scala.jdk.CollectionConverters.*

object OnChange {
Expand Down Expand Up @@ -138,40 +137,17 @@ object OnChange {
options
}

def runHugo(source: Path, log: Logger): Boolean = {
import scala.sys.process._
val srcDir = source.toFile
require(srcDir.isDirectory, "Source directory is not a directory!")
val lineBuffer: ArrayBuffer[String] = ArrayBuffer[String]()
var hadErrorOutput: Boolean = false
var hadWarningOutput: Boolean = false

def fout(line: String): Unit = {
lineBuffer.append(line)
if (!hadWarningOutput && line.contains("WARN")) hadWarningOutput = true
}

def ferr(line: String): Unit = {
lineBuffer.append(line); hadErrorOutput = true
}

val logger = ProcessLogger(fout, ferr)
val proc = Process("hugo", cwd = Option(srcDir))
proc.!(logger) match {
case 0 =>
if (hadErrorOutput) {
log.error("hugo wrote to stderr:\n " + lineBuffer.mkString("\n "))
false
} else if (hadWarningOutput) {
log.warn("hugo issued warnings:\n " + lineBuffer.mkString("\n "))
true
} else { true }
case rc: Int =>
log.error(
s"hugo run failed with rc=$rc:\n " + lineBuffer.mkString("\n ")
)
false
}
def dirHasChangedSince(
dir: Path,
minTime: FileTime
): Boolean = {
val potentiallyChangedFiles = dir.toFile.listFiles().map(_.toPath)
val maybeModified = for {
fName <- potentiallyChangedFiles
timestamp = Files.getLastModifiedTime(fName)
isModified = timestamp.compareTo(minTime) > 0
} yield { isModified }
maybeModified.exists(x => x)
}

}
80 changes: 80 additions & 0 deletions utils/src/main/scala/com/reactific/riddl/utils/FileWatcher.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2019 Ossum, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

package com.reactific.riddl.utils

import java.io.IOException
import java.nio.file.*
import java.nio.file.StandardWatchEventKinds.*
import java.time.Instant
import java.time.temporal.ChronoUnit
import scala.jdk.CollectionConverters.CollectionHasAsScala

object FileWatcher {

@throws[IOException]
private def registerRecursively(
root: Path,
watchService: WatchService
): Unit = {
import java.nio.file.FileVisitResult
import java.nio.file.Files
import java.nio.file.SimpleFileVisitor
import java.nio.file.attribute.BasicFileAttributes
val sfv = new SimpleFileVisitor[Path]() {
override def preVisitDirectory(
dir: Path,
attrs: BasicFileAttributes
): FileVisitResult = {
dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)
FileVisitResult.CONTINUE
}
}
Files.walkFileTree(root, sfv)
}

def watchForChanges(
path: Path,
periodInSeconds: Int,
intervalInMillis: Int
)(onEvents: Seq[WatchEvent[?]] => Boolean
)(notOnEvents: => Boolean
): Boolean = {
val deadline = Instant.now().plus(periodInSeconds, ChronoUnit.SECONDS)
.toEpochMilli
val watchService: WatchService = FileSystems.getDefault.newWatchService()
try {
registerRecursively(path, watchService)
var saveKey: WatchKey = null
do {
watchService.take() match {
case key: WatchKey if key != null =>
saveKey = key
val events = key.pollEvents().asScala.toSeq
events match {
case x: Seq[WatchEvent[?]] if x.isEmpty =>
if (notOnEvents) {
key.reset()
Thread.sleep(intervalInMillis)
} else {
// they want to stop
key.cancel()
}
case events =>
if (onEvents(events)) {
// reset the key for the next trip around
key.reset()
} else {
// they want to stop
key.cancel()
}
}
}
} while (saveKey.isValid && Instant.now().toEpochMilli < deadline)
System.currentTimeMillis() < deadline
} finally { watchService.close() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.reactific.riddl.utils

import org.scalatest.matchers.must.Matchers
import org.scalatest.wordspec.AnyWordSpec

import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.WatchEvent
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.Future
import scala.concurrent.duration.Duration

class FileWatcherTest extends AnyWordSpec with Matchers {
"FileWatcher" should {
"notice changes in a directory" in {
val dir = Path.of(".").resolve("onchange").resolve("target")
.toAbsolutePath
def onEvents(events: Seq[WatchEvent[?]]): Boolean = {
info(s"Event: ${events.mkString(",")}")
false
}
def notOnEvents: Boolean = {
info("No events")
true
}
// watch for changes
val f = Future[Boolean] {
FileWatcher.watchForChanges(dir, 2, 10)(onEvents)(notOnEvents)
}
Thread.sleep(1000)
val changeFile = dir.resolve("change.file")
if (Files.exists(changeFile)) { Files.delete(changeFile) }
changeFile.toFile.createNewFile()
Files.delete(changeFile)
info(s"Future completed: ${f.isCompleted}")
val result = Await.result(f, Duration(1, "seconds"))
result must be(true)
}
}
}

0 comments on commit 67ce7c8

Please sign in to comment.