Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Add progress bars for checking and downloading libs in Bootstrap Launcher #16

Merged
merged 1 commit into from
Jun 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bootstrap/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ version := "0.1"
assemblyJarName in assembly := "ChatOverflow.jar"

libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "1.1.1"
libraryDependencies += "org.jline" % "jline-terminal-jansi" % "3.11.0" // used for terminal width
fork := true
22 changes: 18 additions & 4 deletions bootstrap/src/main/scala/Bootstrap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -125,25 +125,39 @@ object Bootstrap {
* @return false, if there is a serious problem
*/
private def downloadMissingLibraries(dependencies: List[(String, String)]): Boolean = {
val pb = new ProgressBar(dependencies.length)

// using par here to make multiple http requests in parallel, otherwise its awfully slow on internet connections with high RTTs
val missing = dependencies.par.filterNot(dep => isLibraryDownloaded(dep._2)).toList
val missing = dependencies.par.filterNot(dep => {
val (name, url) = dep
pb.countUp()
pb.updateDescription(s"$name@$url")

isLibraryDownloaded(url)
}).toList

pb.finish()

if (missing.isEmpty) {
println("All required libraries are already downloaded.")
} else {
println(s"Downloading ${missing.length} missing libraries...")

for (i <- missing.indices) {
val (name, url) = missing(i)
val pb = new ProgressBar(missing.length)

for ((name, url) <- missing) {
pb.countUp()
pb.updateDescription(s"$name@$url")

println(s"[${i + 1}/${missing.length}] $name ($url)")
if (!downloadLibrary(name, url)) {
// Second try, just in case
if (!downloadLibrary(name, url)) {
return false // error has been displayed, stop bootstrapper from starting with missing lib
}
}
}

pb.finish()
}
true // everything went fine
}
Expand Down
95 changes: 95 additions & 0 deletions bootstrap/src/main/scala/ProgressBar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import org.jline.terminal.TerminalBuilder

/**
* Progress bar used reporting the status while checking and downloading libs.
*
* @param max count of events e.g. length of the list which progress is monitored.
*/
class ProgressBar(max: Int) {
// Width of the terminal, used for size calculations
private val width = {
val width = TerminalBuilder.builder().dumb(true).build().getWidth

// Size couldn't be figured out, use a default
if (width <= 10)
80
else
width
}

private var count = 0
private var description = ""

// We need to create a empty line so that latest line before creation won't be overwritten by the draw method.
println()
draw() // Initial draw

/**
* Increases count by 1 and re-draws the progress bar with the updated count.
*/
def countUp(): Unit = {
// Thread-safeness when working with parallel collections
count.synchronized {
count += 1
draw()
}
}

/**
* Updates the description and re-draws the description line.
* @param desc the new description
*/
def updateDescription(desc: String): Unit = {
// Thread-safeness when working with parallel collections
description.synchronized {
description = desc
drawDescription()
}
}

/**
* Deletes the description to result in a blank line. The progress bar will still be visible.
* After this you can normally print at the beginning of a new line and don't start at the end of the description.
*/
def finish(): Unit = {
description = ""
drawDescription()
}

/**
* Draws the progress bar in the line above the current one.
*/
private def draw(): Unit = {
val barWidth = width - 16 // Width of the bar without percentage and counts
val percentage = count * 100 / max
val equalsSigns = "=" * (barWidth * percentage / 100)
val whiteSpaces = " " * (barWidth - equalsSigns.length)

val content = "%3d%% (%2d|%2d) [%s]".format(percentage, count, max, equalsSigns + ">" + whiteSpaces)

print(s"\033[1A\r$content\n")
// | | | |
// Go up 1 line | | |
// Go to beginning of line
// | |
// Actual progress bar
// |
// Go back down for description
}

/**
* Draws the description which is located in the current line.
*/
private def drawDescription(): Unit = {
// Cap the description at the width of the terminal, otherwise a new line is created and everything would shift.
// If the user needs to see a really long url he can just widen his terminal.
val content = description.take(width)

print(s"\r\033[0K$content")
// | |
// Go to beginning
// |
// Clear from cursor to end of the line
}

}