Skip to content

Commit 96f1842

Browse files
author
Hariharan Ramanathan
committed
Refactoring delphi cli sttp dependency for native image generation
1 parent 6ee305e commit 96f1842

File tree

10 files changed

+143
-77
lines changed

10 files changed

+143
-77
lines changed

build.sbt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ libraryDependencies += "com.lihaoyi" %% "fansi" % "0.2.5"
2929
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
3030
libraryDependencies += "au.com.bytecode" % "opencsv" % "2.4"
3131

32+
33+
libraryDependencies += "com.softwaremill.sttp" %% "core" % "1.5.4"
34+
35+
36+
3237
debianPackageDependencies := Seq("java8-runtime-headless")
3338

3439
lazy val cli = (project in file(".")).
@@ -38,11 +43,23 @@ lazy val cli = (project in file(".")).
3843
enablePlugins(BuildInfoPlugin).
3944
enablePlugins(DebianPlugin).
4045
enablePlugins(WindowsPlugin).
41-
46+
enablePlugins(GraalVMNativeImagePlugin).
47+
settings(
48+
graalVMNativeImageOptions++=Seq(
49+
"--enable-https",
50+
"--enable-http",
51+
"--enable-all-security-services",
52+
"--allow-incomplete-classpath",
53+
"--enable-url-protocols=http,https"
54+
)
55+
).
56+
enablePlugins(JDKPackagerPlugin).
4257
settings(
4358
buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion),
44-
buildInfoPackage := "de.upb.cs.swt.delphi.cli"
59+
buildInfoPackage := "de.upb.cs.swt.delphi.cli",
4560
)
4661
scalastyleConfig := baseDirectory.value / "project" / "scalastyle-config.xml"
4762

4863
trapExit := false
64+
fork := true
65+
connectInput := true

project/build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version = 1.1.1
1+
sbt.version = 1.2.8

project/plugins.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// build management and packaging
22
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.7.0")
3-
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.2")
3+
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.15")
44

55
// coverage
66
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1")

src/main/scala/de/upb/cs/swt/delphi/cli/Config.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ package de.upb.cs.swt.delphi.cli
2323
* @param verbose Marker if logging should be verbose
2424
* @param mode The command to be run
2525
*/
26-
case class Config(server: String = sys.env.getOrElse("DELPHI_SERVER", "https://delphi.cs.uni-paderborn.de/api/"),
26+
case class Config(server: String = sys.env.getOrElse("DELPHI_SERVER", "https://delphi.cs.uni-paderborn.de/api"),
2727
verbose: Boolean = false,
2828
raw: Boolean = false,
2929
csv: String = "",

src/main/scala/de/upb/cs/swt/delphi/cli/DelphiCLI.scala

Lines changed: 50 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -16,81 +16,71 @@
1616

1717
package de.upb.cs.swt.delphi.cli
1818

19-
import akka.actor.ActorSystem
20-
import akka.http.scaladsl.Http
21-
import de.upb.cs.swt.delphi.cli.commands.{RetrieveCommand, SearchCommand, TestCommand}
22-
23-
import scala.concurrent.duration.Duration
24-
import scala.concurrent.{Await, ExecutionContext}
25-
19+
import de.upb.cs.swt.delphi.cli.commands.TestCommand
2620

2721
/**
2822
* The application class for the Delphi command line interface
2923
*/
30-
object DelphiCLI extends App {
31-
32-
implicit val system = ActorSystem()
24+
object DelphiCLI {
3325

34-
val cliParser = {
35-
new scopt.OptionParser[Config]("delphi-cli") {
36-
head("Delphi Command Line Tool", s"(${BuildInfo.version})")
26+
private def cliParser = {
27+
val parser = {
28+
new scopt.OptionParser[Config]("delphi-cli") {
29+
head("Delphi Command Line Tool", s"(${BuildInfo.version})")
3730

38-
version("version").text("Prints the version of the command line tool.")
31+
version("version").text("Prints the version of the command line tool.")
3932

40-
help("help").text("Prints this help text.")
41-
override def showUsageOnError = true
33+
help("help").text("Prints this help text.")
4234

43-
opt[String]("server").action( (x,c) => c.copy(server = x)).text("The url to the Delphi server")
44-
opt[Unit] (name = "raw").action((_,c) => c.copy(raw = true)).text("Output the raw results")
45-
opt[Unit] (name = "silent").action((_,c) => c.copy(silent = true)).text("Suppress non-result output")
35+
override def showUsageOnError = true
4636

47-
checkConfig(c => if (c.server.isEmpty()) failure("Option server is required.") else success)
37+
opt[String]("server").action((x, c) => c.copy(server = x)).text("The url to the Delphi server")
38+
opt[Unit](name = "raw").action((_, c) => c.copy(raw = true)).text("Output the raw results")
39+
opt[Unit](name = "silent").action((_, c) => c.copy(silent = true)).text("Suppress non-result output")
4840

49-
cmd("test").action((_,c) => c.copy(mode = "test"))
41+
checkConfig(c => if (c.server.isEmpty()) failure("Option server is required.") else success)
5042

51-
cmd("retrieve").action((s,c) => c.copy(mode = "retrieve"))
52-
.text("Retrieve a project's description, specified by ID.")
53-
.children(
54-
arg[String]("id").action((x, c) => c.copy(id = x)).text("The ID of the project to retrieve"),
55-
opt[String]("csv").action((x, c) => c.copy(csv = x)).text("Path to the output .csv file (overwrites existing file)"),
56-
opt[Unit]('f', "file").action((_, c) => c.copy(opts = List("file"))).text("Use to load the ID from file, " +
57-
"with the filepath given in place of the ID")
58-
)
59-
60-
cmd("search").action((s, c) => c.copy(mode = "search"))
61-
.text("Search artifact using a query.")
62-
.children(
63-
arg[String]("query").action((x,c) => c.copy(query = x)).text("The query to be used."),
64-
opt[String]("csv").action((x, c) => c.copy(csv = x)).text("Path to the output .csv file (overwrites existing file)"),
65-
opt[Int]("limit").action((x, c) => c.copy(limit = Some(x))).text("The maximal number of results returned."),
66-
opt[Unit](name="list").action((_, c) => c.copy(list = true)).text("Output results as list (raw option overrides this)"),
67-
opt[Int]("timeout").action((x, c) => c.copy(timeout = Some(x))).text("Timeout in seconds.")
68-
)
69-
}
70-
}
43+
cmd("test").action((_, c) => c.copy(mode = "test"))
7144

45+
cmd("retrieve").action((s, c) => c.copy(mode = "retrieve"))
46+
.text("Retrieve a project's description, specified by ID.")
47+
.children(
48+
arg[String]("id").action((x, c) => c.copy(id = x)).text("The ID of the project to retrieve"),
49+
opt[String]("csv").action((x, c) => c.copy(csv = x)).text("Path to the output .csv file (overwrites existing file)"),
50+
opt[Unit]('f', "file").action((_, c) => c.copy(opts = List("file"))).text("Use to load the ID from file, " +
51+
"with the filepath given in place of the ID")
52+
)
7253

73-
cliParser.parse(args, Config()) match {
74-
case Some(config) =>
75-
if (!config.silent) cliParser.showHeader()
76-
config.mode match {
77-
case "test" => TestCommand.execute(config)
78-
case "retrieve" => RetrieveCommand.execute(config)
79-
case "search" => SearchCommand.execute(config)
80-
case x => config.consoleOutput.outputError(s"Unknown command: $x")
54+
cmd("search").action((s, c) => c.copy(mode = "search"))
55+
.text("Search artifact using a query.")
56+
.children(
57+
arg[String]("query").action((x, c) => c.copy(query = x)).text("The query to be used."),
58+
opt[String]("csv").action((x, c) => c.copy(csv = x)).text("Path to the output .csv file (overwrites existing file)"),
59+
opt[Int]("limit").action((x, c) => c.copy(limit = Some(x))).text("The maximal number of results returned."),
60+
opt[Unit](name = "list").action((_, c) => c.copy(list = true)).text("Output results as list (raw option overrides this)"),
61+
opt[Int]("timeout").action((x, c) => c.copy(timeout = Some(x))).text("Timeout in seconds.")
62+
)
8163
}
82-
83-
case None =>
64+
}
65+
parser
8466
}
8567

68+
def main(args: Array[String]): Unit = {
69+
70+
System.setProperty("java.library.path", javaLibPath.get)
71+
System.setProperty("javax.net.ssl.trustStore", trustStorePath.get)
72+
System.setProperty("picocli.ansi", "true")
73+
cliParser.parse(args, config) match {
74+
case Some(config) =>
75+
if (!config.silent) cliParser.showHeader()
76+
config.mode match {
77+
case "test" => TestCommand.execute
78+
// case "retrieve" => RetrieveCommand.execute(config)
79+
// case "search" => SearchCommand.execute(config)
80+
case x => config.consoleOutput.outputError(s"Unknown command: $x")
81+
}
82+
case None =>
83+
}
8684

87-
val poolShutdown = Http().shutdownAllConnectionPools()
88-
Await.result(poolShutdown, Duration.Inf)
89-
90-
implicit val ec: ExecutionContext = system.dispatcher
91-
val terminationFuture = system.terminate()
92-
93-
terminationFuture.onComplete {
94-
sys.exit(0)
9585
}
96-
}
86+
}

src/main/scala/de/upb/cs/swt/delphi/cli/commands/Command.scala

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ import akka.http.scaladsl.model.Uri.Query
2222
import akka.http.scaladsl.model.{HttpRequest, HttpResponse, StatusCodes, Uri}
2323
import akka.stream.ActorMaterializer
2424
import akka.util.ByteString
25+
import com.softwaremill.sttp._
2526
import de.upb.cs.swt.delphi.cli.Config
2627

27-
import scala.concurrent.{Await, Future}
2828
import scala.concurrent.duration._
29-
import scala.util.{Failure, Success}
29+
import scala.concurrent.{Await, Future}
30+
import scala.util.Failure
3031

3132
/**
3233
* Represents the implementation of a command of the CLI
@@ -35,17 +36,19 @@ trait Command {
3536

3637
/**
3738
* Executes the command implementation
39+
*
3840
* @param config The current configuration for the command
3941
*/
40-
def execute(config: Config)(implicit system : ActorSystem): Unit
42+
def execute(implicit config: Config): Unit = {}
4143

4244
/**
4345
* Implements a common request type using currying to avoid code duplication
46+
*
4447
* @param target The endpoint to perform a Get request on
4548
* @param config The current configuration for the command
4649
*/
47-
protected def executeGet(target: String, parameters: Map[String, String] = Map())(config: Config, system : ActorSystem) : Option[String] = {
48-
implicit val sys : ActorSystem = system
50+
protected def executeGet(target: String, parameters: Map[String, String] = Map())(config: Config, system: ActorSystem): Option[String] = {
51+
implicit val sys: ActorSystem = system
4952
implicit val materializer = ActorMaterializer()
5053
implicit val executionContext = sys.dispatcher
5154

@@ -65,7 +68,7 @@ trait Command {
6568
entity.dataBytes.runFold(ByteString(""))(_ ++ _).map { body =>
6669
Some(body.utf8String)
6770
}
68-
case resp @ HttpResponse(code, _, _, _) => {
71+
case resp@HttpResponse(code, _, _, _) => {
6972
error(config)("Artifact not found.")
7073
resp.discardEntityBytes()
7174
Future(None)
@@ -75,9 +78,35 @@ trait Command {
7578
Await.result(resultString, Duration.Inf)
7679
}
7780

81+
/**
82+
* Generic http GET request
83+
*
84+
* @param target Sub url in delphi server
85+
* @param parameters Query params
86+
* @return GET response
87+
*/
88+
protected def executeGet1(target: String, parameters: Map[String, String] = Map())
89+
(implicit config: Config, backend: SttpBackend[Id, Nothing]): Option[String] = {
90+
91+
val request = sttp.get(uri"${config.server}/$target?$parameters")
92+
config.consoleOutput.outputInformation(s"Sending request ${request.uri}")
93+
val response = request.send()
94+
response.body match {
95+
case Left(value) =>
96+
error.apply(s"Request failed:\n $value")
97+
None
98+
case Right(value) =>
99+
Some(value)
100+
}
101+
}
102+
103+
78104
protected def information(implicit config: Config): String => Unit = config.consoleOutput.outputInformation _
105+
79106
protected def reportResult(implicit config: Config): Any => Unit = config.consoleOutput.outputResult _
107+
80108
protected def error(implicit config: Config): String => Unit = config.consoleOutput.outputError _
109+
81110
protected def success(implicit config: Config): String => Unit = config.consoleOutput.outputSuccess _
82111

83112
protected def exportResult(implicit config: Config): Any => Unit = config.csvOutput.exportResult _

src/main/scala/de/upb/cs/swt/delphi/cli/commands/RetrieveCommand.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import scala.util.{Failure, Success}
3838
object RetrieveCommand extends Command with SprayJsonSupport with DefaultJsonProtocol {
3939

4040

41-
override def execute(config: Config)(implicit system: ActorSystem): Unit = {
41+
def execute(config: Config)(implicit system: ActorSystem): Unit = {
4242
implicit val ec = system.dispatcher
4343
implicit val materializer = ActorMaterializer()
4444

src/main/scala/de/upb/cs/swt/delphi/cli/commands/SearchCommand.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ object SearchCommand extends Command with SprayJsonSupport with DefaultJsonProto
4444
*
4545
* @param config The current configuration for the command
4646
*/
47-
override def execute(config: Config)(implicit system: ActorSystem): Unit = {
47+
def execute(config: Config)(implicit system: ActorSystem): Unit = {
4848
implicit val ec = system.dispatcher
4949
implicit val materializer = ActorMaterializer()
5050

src/main/scala/de/upb/cs/swt/delphi/cli/commands/TestCommand.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@
1616

1717
package de.upb.cs.swt.delphi.cli.commands
1818

19-
import akka.actor.ActorSystem
2019
import de.upb.cs.swt.delphi.cli.Config
20+
import de.upb.cs.swt.delphi.cli._
2121

2222
/**
2323
* The implementation of the test command.
2424
* Tries to connect to the Delphi server and reports on the results of the version call.
2525
*/
2626
object TestCommand extends Command {
27-
override def execute(config: Config)(implicit system : ActorSystem): Unit = executeGet(
28-
"/version"
29-
)(config, system).map(s => {
27+
override def execute(implicit config: Config): Unit = executeGet1(
28+
"version"
29+
).map(s => {
3030
success(config)("Successfully contacted Delphi server. ")
3131
information(config)("Server version: " + s)
3232
})
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (C) 2018 The Delphi Team.
2+
// See the LICENCE file distributed with this work for additional
3+
// information regarding copyright ownership.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
package de.upb.cs.swt.delphi
17+
18+
import com.softwaremill.sttp.HttpURLConnectionBackend
19+
20+
package object cli {
21+
implicit val config: Config = Config()
22+
implicit val backend = HttpURLConnectionBackend()
23+
24+
val javaLibPath = Option(System.getenv("JAVA_LIB_PATH"))
25+
.orElse(Some("/usr/lib/jvm/default-java/lib/"))
26+
27+
val trustStorePath = Option(System.getenv("JAVA_TRUSTSTORE"))
28+
.orElse(Some("/usr/lib/jvm/default-java/lib/security/cacerts"))
29+
30+
}

0 commit comments

Comments
 (0)