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

Commit aa74e60

Browse files
committed
Use Coursier in the Bootstrap Launcher instead of a xml file containing urls
1 parent da1d0a9 commit aa74e60

File tree

14 files changed

+105
-521
lines changed

14 files changed

+105
-521
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ project/plugins/project/
2929
/config/
3030

3131
# resources of the bootstrap project
32-
/bootstrap/src/main/resources/
3332
/bin/
3433
/lib/
3534
/deploy/

.idea/runConfigurations/Bootstrap_Assembly__sbt_assembly_.xml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/Bootstrap_Assembly__sbt_bs_assembly_.xml

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/_Deploy__Generate_Bootstrap_Launcher_and_deploy.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ simple_run:
77
sbt package copy
88

99
bootstrap_deploy:
10-
sbt clean gui package bs bootstrapProject/assembly deploy
10+
sbt clean gui package bootstrapProject/assembly deploy
1111

1212
bootstrap_deploy_dev:
1313
sbt clean gui package buildProject/package deployDev

bootstrap/build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ name := "chatoverflow-bootstrap"
22
version := "0.1"
33
assemblyJarName in assembly := "ChatOverflow.jar"
44

5-
libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "1.1.1"
65
libraryDependencies += "org.jline" % "jline-terminal-jansi" % "3.11.0" // used for terminal width
6+
libraryDependencies += "io.get-coursier" %% "coursier" % "2.0.0-RC3-2" // Coursier is used to download the deps of the framework
77
fork := true
88

99
packageBin / includePom := false

bootstrap/src/main/scala/Bootstrap.scala

Lines changed: 9 additions & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import java.io.File
2-
import java.net.{HttpURLConnection, URL, UnknownHostException}
2+
import java.net.URLClassLoader
33
import java.nio.file.Paths
44

5-
import scala.sys.process._
6-
75
/**
86
* The bootstrap launcher downloads all required libraries and starts chat overflow with the correct parameters.
97
*/
@@ -18,6 +16,10 @@ object Bootstrap {
1816
// Chat Overflow Launcher / Main class (should not change anymore)
1917
val chatOverflowMainClass = "org.codeoverflow.chatoverflow.Launcher"
2018

19+
val classloader = new URLClassLoader(
20+
new File("bin").listFiles().filter(_.getName.endsWith(".jar")).map(_.toURI.toURL)
21+
)
22+
2123
/**
2224
* Software entry point
2325
*
@@ -29,13 +31,14 @@ object Bootstrap {
2931
if (testValidity()) {
3032
println("Valid ChatOverflow installation. Checking libraries...")
3133

32-
if (checkLibraries(args)) {
34+
val deps = DependencyDownloader.fetchDependencies().map(u => new File(u.getFile))
35+
if (deps.nonEmpty) {
3336
val javaPath = createJavaPath()
3437
if (javaPath.isDefined) {
3538
println("Found java installation. Starting ChatOverflow...")
3639

3740
// Start chat overflow!
38-
val process = new java.lang.ProcessBuilder(javaPath.get, "-cp", s"bin/*${File.pathSeparator}lib/*", chatOverflowMainClass)
41+
val process = new java.lang.ProcessBuilder(javaPath.get, "-cp", s"bin/*${deps.mkString(File.pathSeparator, File.pathSeparator, "")}", chatOverflowMainClass)
3942
.inheritIO().start()
4043

4144
val exitCode = process.waitFor()
@@ -56,7 +59,7 @@ object Bootstrap {
5659
*
5760
* @return the path to the java runtime or none, if the file was not found
5861
*/
59-
def createJavaPath(): Option[String] = {
62+
private def createJavaPath(): Option[String] = {
6063

6164
// Check validity of java.home path first
6265
if (!new File(javaHomePath).exists()) {
@@ -80,204 +83,6 @@ object Bootstrap {
8083

8184
}
8285

83-
/**
84-
* Checks if the library folder exists or the reload-flag is set. Triggers the download-process if libraries are missing.
85-
*
86-
* @param args the args, the launcher has been called with
87-
* @return false, if there is a serious problem
88-
*/
89-
def checkLibraries(args: Array[String]): Boolean = {
90-
91-
val libFolder = new File(s"$currentFolderPath/lib")
92-
93-
// Create folder for libs if missing
94-
if (!libFolder.exists()) {
95-
try {
96-
libFolder.mkdir()
97-
} catch {
98-
case e: Exception => println(s"Unable to create library directory. Message: ${e.getMessage}")
99-
return false
100-
}
101-
}
102-
103-
// --reload flags instructs to delete all downloaded libraries and to re-download them
104-
if (args.contains("--reload")) {
105-
for (libFile <- libFolder.listFiles()) {
106-
try {
107-
libFile.delete()
108-
} catch {
109-
case e: Exception => println(s"Unable to delete file '${libFile.getName}'. Message: ${e.getMessage}")
110-
return false
111-
}
112-
}
113-
}
114-
115-
val dependencies = getDependencies
116-
117-
// Download all libraries
118-
// try downloading libs and only if it succeeded (returned true) then try to delete older libs
119-
downloadMissingLibraries(dependencies) && deleteUndesiredLibraries(dependencies)
120-
}
121-
122-
/**
123-
* Reads the dependency xml file and tries to download every library that is missing.
124-
*
125-
* @return false, if there is a serious problem
126-
*/
127-
private def downloadMissingLibraries(dependencies: List[(String, String)]): Boolean = {
128-
val pb = new ProgressBar(dependencies.length)
129-
130-
// using par here to make multiple http requests in parallel, otherwise its awfully slow on internet connections with high RTTs
131-
val missing = dependencies.par.filterNot(dep => {
132-
val (name, url) = dep
133-
pb.countUp()
134-
pb.updateDescription(s"$name@$url")
135-
136-
isLibraryDownloaded(url)
137-
}).toList
138-
139-
pb.finish()
140-
141-
if (missing.isEmpty) {
142-
println("All required libraries are already downloaded.")
143-
} else {
144-
println(s"Downloading ${missing.length} missing libraries...")
145-
146-
val pb = new ProgressBar(missing.length)
147-
148-
for ((name, url) <- missing) {
149-
pb.countUp()
150-
pb.updateDescription(s"$name@$url")
151-
152-
if (!downloadLibrary(name, url)) {
153-
// Second try, just in case
154-
if (!downloadLibrary(name, url)) {
155-
return false // error has been displayed, stop bootstrapper from starting with missing lib
156-
}
157-
}
158-
}
159-
160-
pb.finish()
161-
}
162-
true // everything went fine
163-
}
164-
165-
/**
166-
* Deletes all undesired libraries. Currently these are all libs that aren't on the list of dependencies.
167-
* The main responsibility is to delete old libs that got updated or libs that aren't required anymore by Chat Overflow.
168-
*
169-
* @param dependencies the libs that should be kept
170-
* @return false, if a file couldn't be deleted
171-
*/
172-
private def deleteUndesiredLibraries(dependencies: List[(String, String)]): Boolean = {
173-
val libDir = new File(s"$currentFolderPath/lib")
174-
if (libDir.exists() && libDir.isDirectory) {
175-
// Desired filenames
176-
val libraryFilenames = dependencies.map(d => libraryFile(d._2).getName)
177-
178-
val undesiredFiles = libDir.listFiles().filterNot(file => libraryFilenames.contains(file.getName)) // filter out libs on the dependency list
179-
180-
// Count errors while trying to remove undesired files
181-
val errorCount = undesiredFiles.count(file => {
182-
println(s"Deleting old or unnecessary library at $file")
183-
if (file.delete()) {
184-
false // no error
185-
} else {
186-
println(s"Error: Couldn't delete file $file.")
187-
true // error
188-
}
189-
})
190-
errorCount == 0 // return false if at least one error occurred
191-
} else {
192-
// Shouldn't be possible, because this is called from checkLibraries, which creates this directory.
193-
true
194-
}
195-
}
196-
197-
/**
198-
* Downloads a specified library
199-
*/
200-
private def downloadLibrary(libraryName: String, libraryURL: String): Boolean = {
201-
val url = new URL(libraryURL)
202-
203-
try {
204-
val connection = url.openConnection().asInstanceOf[HttpURLConnection]
205-
connection.setConnectTimeout(3000)
206-
connection.setReadTimeout(3000)
207-
connection.connect()
208-
209-
if (connection.getResponseCode >= 400) {
210-
println("Error: Unable to download library.")
211-
false
212-
}
213-
else {
214-
// Save file in the lib folder (keeping the name and type)
215-
try {
216-
url #> libraryFile(libraryURL) !!
217-
218-
true
219-
} catch {
220-
case e: Exception =>
221-
println(s"Error: Unable to save library. Message: ${e.getMessage}")
222-
false
223-
} finally {
224-
connection.disconnect()
225-
}
226-
}
227-
} catch {
228-
case e: UnknownHostException =>
229-
println(s"Error. Unable to connect to the url '$url'. Message: ${e.getMessage}")
230-
false
231-
}
232-
}
233-
234-
/**
235-
* Gets all required dependencies from the dependencies.xml in the jar file
236-
*
237-
* @return a list of tuples that contain the name (e.g. log4j) without org or version and the url.
238-
*/
239-
private def getDependencies: List[(String, String)] = {
240-
val stream = getClass.getResourceAsStream("/dependencies.xml")
241-
val depXml = xml.XML.load(stream)
242-
val dependencies = depXml \\ "dependency"
243-
val dependencyTuples = dependencies.map(dep => {
244-
val name = (dep \ "name").text.trim
245-
val url = (dep \ "url").text.trim
246-
(name, url)
247-
})
248-
249-
dependencyTuples.toList
250-
}
251-
252-
/**
253-
* Checks whether this library is fully downloaded
254-
*
255-
* @param libraryURL the url of the library
256-
* @return true if it is completely downloaded, false if only partially downloaded or not downloaded at all
257-
*/
258-
private def isLibraryDownloaded(libraryURL: String): Boolean = {
259-
val f = libraryFile(libraryURL)
260-
261-
if (!f.exists()) {
262-
false
263-
} else {
264-
try {
265-
// We assume here that the libs don't change at the repo.
266-
// While this is true for Maven Central, which is immutable once a file has been uploaded, its not for JCenter.
267-
// Updating a released artifact generally isn't valued among developers
268-
// and the odds of the updated artifact having the same size is very unlikely.
269-
val url = new URL(libraryURL)
270-
url.openConnection().getContentLengthLong == f.length()
271-
} catch {
272-
case _: Exception => false
273-
}
274-
}
275-
}
276-
277-
private def libraryFile(libraryURL: String): File = {
278-
new File(s"$currentFolderPath/lib/${libraryURL.substring(libraryURL.lastIndexOf("/"))}")
279-
}
280-
28186
/**
28287
* Checks, if the installation is valid
28388
*/

0 commit comments

Comments
 (0)