This is an automated email from the ASF dual-hosted git repository.
aradzinski pushed a commit to branch NLPCRAFT-108
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
The following commit(s) were added to refs/heads/NLPCRAFT-108 by this push:
new 8879f03 WIP.
8879f03 is described below
commit 8879f03286777247030fb57ad664655e6ef26d1b
Author: Aaron Radzinski <[email protected]>
AuthorDate: Thu Oct 1 21:39:44 2020 -0700
WIP.
---
.../org/apache/nlpcraft/common/util/NCUtils.scala | 24 ---
.../nlpcraft/model/tools/cmdline/NCCli.scala | 179 +++++++++++++++++----
2 files changed, 151 insertions(+), 52 deletions(-)
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
index c7a50e8..e80f973 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
@@ -1047,19 +1047,12 @@ object NCUtils extends LazyLogging {
*/
implicit class IntMemoryUnits(v: Int) {
def TB: Int = v * 1024 * 1024 * 1024 * 1024
-
def GB: Int = v * 1024 * 1024 * 1024
-
def MB: Int = v * 1024 * 1024
-
def KB: Int = v * 1024
-
def tb: Int = TB
-
def gb: Int = GB
-
def mb: Int = MB
-
def kb: Int = KB
}
@@ -1070,19 +1063,12 @@ object NCUtils extends LazyLogging {
*/
implicit class LongMemoryUnits(v: Long) {
def TB: Long = v * 1024 * 1024 * 1024 * 1024
-
def GB: Long = v * 1024 * 1024 * 1024
-
def MB: Long = v * 1024 * 1024
-
def KB: Long = v * 1024
-
def tb: Long = TB
-
def gb: Long = GB
-
def mb: Long = MB
-
def kb: Long = KB
}
@@ -1094,25 +1080,15 @@ object NCUtils extends LazyLogging {
*/
implicit class IntTimeUnits(v: Int) {
def MSECS: Int = v
-
def MS: Int = v
-
def SECS: Int = v * 1000
-
def MINS: Int = v * 1000 * 60
-
def HOURS: Int = v * 1000 * 60 * 60
-
def DAYS: Int = v * 1000 * 60 * 60 * 24
-
def ms: Int = MS
-
def secs: Int = SECS
-
def mins: Int = MINS
-
def hours: Int = HOURS
-
def days: Int = DAYS
}
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala
index 9d2918a..6c0dbb1 100644
---
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala
@@ -39,12 +39,14 @@ import java.lang.management.ManagementFactory
import java.text.DateFormat
import java.util.Date
+import org.apache.nlpcraft.common.util.NCUtils.IntTimeUnits
import resource.managed
import scala.collection.mutable
import scala.compat.java8.OptionConverters._
import scala.collection.JavaConverters._
import scala.compat.Platform.currentTime
+import scala.util.Try
/**
* 'nlpcraft' script entry point.
@@ -136,7 +138,8 @@ object NCCli extends App {
names = Seq("start-server"),
synopsis = s"Starts local REST server.",
desc = Some(
- s"REST server is started in the external JVM process with both
stdout and stderr piped out into log file."
+ s"REST server is started in the external JVM process with both
stdout and stderr piped out into log file. " +
+ s"Command will block until the server is started unless
${y("--no-wait")} parameter is used."
),
body = cmdStartServer,
params = Seq(
@@ -146,10 +149,10 @@ object NCCli extends App {
value = Some("path"),
optional = true,
desc =
- "Configuration absolute file path. Server will
automatically look for 'nlpcraft.conf' " +
- "configuration file in the same directory as NLPCraft
JAR file. If the configuration file has " +
- "different name or in different location use this
parameter to provide an alternative path. " +
- "Note that the REST server and the data probe can use
the same file for their configuration."
+ s"Configuration absolute file path. Server will
automatically look for ${y("nlpcraft.conf")} " +
+ s"configuration file in the same directory as NLPCraft
JAR file. If the configuration file has " +
+ s"different name or in different location use this
parameter to provide an alternative path. " +
+ s"Note that the REST server and the data probe can use
the same file for their configuration."
),
Parameter(
id = "igniteConfig",
@@ -157,11 +160,11 @@ object NCCli extends App {
value = Some("path"),
optional = true,
desc =
- "Apache Ignite configuration absolute file path. Note
that Apache Ignite is used as a cluster " +
- "computing plane and a default distributed storage.
REST server will automatically look for " +
- "'ignite.xml' configuration file in the same directory
as NLPCraft JAR file. If the " +
- "configuration file has different name or in different
location use this parameter to " +
- "provide an alternative path."
+ s"Apache Ignite configuration absolute file path. Note
that Apache Ignite is used as a cluster " +
+ s"computing plane and a default distributed storage.
REST server will automatically look for " +
+ s"${y("ignite.xml")} configuration file in the same
directory as NLPCraft JAR file. If the " +
+ s"configuration file has different name or in
different location use this parameter to " +
+ s"provide an alternative path."
),
Parameter(
id = "output",
@@ -170,7 +173,14 @@ object NCCli extends App {
optional = true,
desc =
"File path for both REST server stdout and stderr
output. If not provided, the REST server" +
- s"output will be piped into
'$${USER_HOME}/.nlpcraft/server-output-xxx.txt' file."
+ s"output will be piped into
${y("${USER_HOME}/.nlpcraft/server-output-xxx.txt")}' file."
+ ),
+ Parameter(
+ id = "noWait",
+ names = Seq("--no-wait"),
+ optional = true,
+ desc =
+ s"Instructs command not to wait for the server startup
and return immediately."
)
),
examples = Seq(
@@ -185,6 +195,12 @@ object NCCli extends App {
)
),
Command(
+ id = "get-server",
+ names = Seq("get-server"),
+ synopsis = s"Basic information about locally running REST server.",
+ body = cmdServerInfo
+ ),
+ Command(
id = "no-ansi",
names = Seq("no-ansi"),
synopsis = s"Disables usage of ANSI escape codes (colors &
terminal controls).",
@@ -339,6 +355,42 @@ object NCCli extends App {
private final val ANSI_CMD = CMDS.find(_.id == "ansi").get
/**
+ *
+ * @param endpoint
+ * @return
+ */
+ private def restHealth(endpoint: String): Int =
+ httpGet(endpoint, "health", new ResponseHandler[Int]() {
+ override def handleResponse(resp: HttpResponse): Int =
resp.getStatusLine.getStatusCode
+ })
+
+ /**
+ *
+ * @param pathOpt
+ */
+ private def checkFilePath(pathOpt: Option[Argument]): Unit = {
+ if (pathOpt.isDefined) {
+ val file = new File(pathOpt.get.value.get)
+
+ if (!file.exists() || !file.isFile)
+ throw new IllegalArgumentException(s"File not found:
${file.getAbsolutePath}")
+ }
+ }
+
+ /**
+ *
+ * @param pathOpt
+ */
+ private def checkDirPath(pathOpt: Option[Argument]): Unit = {
+ if (pathOpt.isDefined) {
+ val file = new File(pathOpt.get.value.get)
+
+ if (!file.exists() || !file.isDirectory)
+ throw new IllegalArgumentException(s"Directory not found:
${file.getAbsolutePath}")
+ }
+ }
+
+ /**
* @param cmd Command descriptor.
* @param args Arguments, if any, for this command.
* @param repl Whether or not running from REPL.
@@ -346,11 +398,15 @@ object NCCli extends App {
private def cmdStartServer(cmd: Command, args: Seq[Argument], repl:
Boolean): Unit = {
val cfgPath = args.find(_.parameter.id == "config")
val igniteCfgPath = args.find(_.parameter.id == "igniteConfig")
+ val noWait = args.exists(_.parameter.id == "noWait")
val output = args.find(_.parameter.id == "output") match {
case Some(arg) ⇒ new File(arg.value.get)
case None ⇒ new File(SystemUtils.getUserHome,
s".nlpcraft/server-output-$currentTime.txt")
}
+ checkFilePath(cfgPath)
+ checkFilePath(igniteCfgPath)
+
val pb = new ProcessBuilder(
JAVA,
"-ea",
@@ -369,11 +425,11 @@ object NCCli extends App {
"org.apache.nlpcraft.NCStart",
"-server",
cfgPath match {
- case Some(path) ⇒ s"-config=$path"
+ case Some(path) ⇒ s"-config=${path.value.get}"
case None ⇒ ""
},
igniteCfgPath match {
- case Some(path) ⇒ s"-igniteConfig=$path"
+ case Some(path) ⇒ s"-igniteConfig=${path.value.get}"
case None ⇒ ""
},
)
@@ -383,17 +439,78 @@ object NCCli extends App {
pb.redirectOutput(Redirect.appendTo(output))
try {
+ val startMs = currentTime
+
pb.start()
- logln(s"REST server is starting, output redirected to
${c(output.getAbsolutePath)}")
- logln(s"Use ${g("stop-server")} command to stop it.")
+ logln(s"Server output: ${c(output.getAbsolutePath)}")
+
+ if (noWait)
+ logln(s"Server is starting...")
+ else {
+ log(s"Server is starting ")
+
+ val timeout = currentTime + 5.mins
+
+ def getServerBeacon = loadServerBeacon().map(_._1).orNull
+
+ var beacon = getServerBeacon
+ var online = false
+
+ val spinner = mkSpinner()
+
+ spinner.start()
+
+ while (currentTime < timeout && !online) {
+ if (beacon == null)
+ beacon = getServerBeacon
+ else
+ online = Try(restHealth("http://" +
beacon.restEndpoint) == 200).getOrElse(false)
+
+ if (!online)
+ Thread.sleep(2.secs)
+ }
+
+ spinner.stop()
+
+ if (!online) {
+ logln()
+ error(s"Cannot detect live server.")
+ }
+ else {
+ val dur = currentTime - startMs
+
+ logln()
+ logln(s"Server is started ${c(s"[${dur / 1000}s]")}")
+ }
+ }
+
+ val tbl = new NCAsciiTable()
+
+ tbl += (s"${g("stop-server")}", "Start the server.")
+ tbl += (s"${g("ping-server")}", "Ping the server.")
+ tbl += (s"${g("get-server")}", "Get server information.")
+
+ logln(s"Handy commands:\n${tbl.toString}")
}
catch {
- case e: Exception ⇒ error(s"REST server failed to start:
${y(e.getLocalizedMessage)}")
+ case e: Exception ⇒ error(s"Server failed to start:
${y(e.getLocalizedMessage)}")
}
}
/**
+ * Makes default spinner.
+ *
+ * @return
+ */
+ private def mkSpinner() = new NCAnsiSpinner(
+ System.out,
+ ansiCyanFg,
+ // ANSI is NOT disabled & we ARE NOT running from IDEA or Eclipse...
+ NCAnsi.isEnabled && U.sysEnv("NLPCRAFT_CLI").isDefined
+ )
+
+ /**
* @param cmd Command descriptor.
* @param args Arguments, if any, for this command.
* @param repl Whether or not executing from REPL.
@@ -419,21 +536,14 @@ object NCCli extends App {
while (i < num) {
log(s"(${i + 1} of $num) pinging REST server at ${b(endpoint)} ")
- val spinner = new NCAnsiSpinner(
- System.out,
- ansiCyanFg,
- // ANSI is NOT disabled & we ARE NOT running from IDEA or
Eclipse...
- NCAnsi.isEnabled && U.sysEnv("NLPCRAFT_CLI").isDefined
- )
+ val spinner = mkSpinner()
spinner.start()
val startMs = currentTime
try
- httpGet(endpoint, "health", new ResponseHandler[Int]() {
- override def handleResponse(resp: HttpResponse): Int =
resp.getStatusLine.getStatusCode
- }) match {
+ restHealth(endpoint) match {
case 200 ⇒
spinner.stop()
@@ -676,6 +786,19 @@ object NCCli extends App {
* @param args Arguments, if any, for this command.
* @param repl Whether or not executing from REPL.
*/
+ private def cmdServerInfo(cmd: Command, args: Seq[Argument], repl:
Boolean): Unit = {
+ loadServerBeacon() match {
+ case Some((beacon, _)) ⇒ logln(s"Local REST
server:\n${mkServerBeaconTable(beacon).toString}")
+ case None ⇒ error(s"Cannot detect local REST server.")
+ }
+ }
+
+ /**
+ *
+ * @param cmd Command descriptor.
+ * @param args Arguments, if any, for this command.
+ * @param repl Whether or not executing from REPL.
+ */
private def cmdRepl(cmd: Command, args: Seq[Argument], repl: Boolean):
Unit = {
loadServerBeacon() match {
case Some((beacon, _)) ⇒ logln(s"Local REST server
detected:\n${mkServerBeaconTable(beacon).toString}")
@@ -796,7 +919,7 @@ object NCCli extends App {
* @param cmd
* @return
*/
- private def prepUrl(baseUrl: String, cmd: String): String =
+ private def prepRestUrl(baseUrl: String, cmd: String): String =
if (baseUrl.endsWith("/")) s"${baseUrl}api/v1/$cmd" else
s"$baseUrl/api/v1/$cmd"
/**
@@ -810,7 +933,7 @@ object NCCli extends App {
* @throws IOException
*/
private def httpPost[T](baseUrl: String, cmd: String, resp:
ResponseHandler[T], jsParams: (String, AnyRef)*): T = {
- val post = new HttpPost(prepUrl(baseUrl, cmd))
+ val post = new HttpPost(prepRestUrl(baseUrl, cmd))
post.setHeader("Content-Type", "application/json")
post.setEntity(new StringEntity(gson.toJson(jsParams.filter(_._2 !=
null).toMap.asJava), "UTF-8"))
@@ -832,7 +955,7 @@ object NCCli extends App {
* @throws IOException
*/
private def httpGet[T](baseUrl: String, cmd: String, resp:
ResponseHandler[T], jsParams: (String, AnyRef)*): T = {
- val bldr = new URIBuilder(prepUrl(baseUrl, cmd))
+ val bldr = new URIBuilder(prepRestUrl(baseUrl, cmd))
jsParams.foreach(p ⇒ bldr.setParameter(p._1, p._2.toString))