Merge pull request #2859 from eed3si9n/wip/protocol

Start lightweight client
This commit is contained in:
Dale Wijnand 2017-01-12 15:43:32 +00:00 committed by GitHub
commit c1648ac0a5
53 changed files with 1252 additions and 549 deletions

View File

@ -175,9 +175,20 @@ lazy val actionsProj = (project in file("main-actions")).
addSbtZinc, addSbtCompilerIvyIntegration, addSbtCompilerInterface,
addSbtIO, addSbtUtilLogging, addSbtUtilRelation, addSbtLm, addSbtUtilTracking)
lazy val protocolProj = (project in file("protocol")).
enablePlugins(ContrabandPlugin, JsonCodecPlugin).
settings(
testedBaseSettings,
name := "Protocol",
libraryDependencies ++= Seq(sjsonNewScalaJson),
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala"
).
configure(addSbtUtilLogging)
// General command support and core commands not specific to a build system
lazy val commandProj = (project in file("main-command")).
enablePlugins(ContrabandPlugin, JsonCodecPlugin).
dependsOn(protocolProj).
settings(
testedBaseSettings,
name := "Command",
@ -248,7 +259,7 @@ lazy val myProvided = config("provided") intransitive
def allProjects = Seq(
testingProj, testAgentProj, taskProj, stdTaskProj, runProj,
scriptedSbtProj, scriptedPluginProj,
scriptedSbtProj, scriptedPluginProj, protocolProj,
actionsProj, commandProj, mainSettingsProj, mainProj, sbtProj, bundledLauncherProj)
def projectsWithMyProvided = allProjects.map(p => p.copy(configurations = (p.configurations.filter(_ != Provided)) :+ myProvided))

View File

@ -0,0 +1,26 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait CommandSourceFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val CommandSourceFormat: JsonFormat[sbt.CommandSource] = new JsonFormat[sbt.CommandSource] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.CommandSource = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val channelName = unbuilder.readField[String]("channelName")
unbuilder.endObject()
sbt.CommandSource(channelName)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.CommandSource, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("channelName", obj.channelName)
builder.endObject()
}
}
}

View File

@ -0,0 +1,30 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait ExecFormats { self: CommandSourceFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val ExecFormat: JsonFormat[sbt.Exec] = new JsonFormat[sbt.Exec] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.Exec = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val commandLine = unbuilder.readField[String]("commandLine")
val execId = unbuilder.readField[Option[String]]("execId")
val source = unbuilder.readField[Option[sbt.CommandSource]]("source")
unbuilder.endObject()
sbt.Exec(commandLine, execId, source)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.Exec, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("commandLine", obj.commandLine)
builder.addField("execId", obj.execId)
builder.addField("source", obj.source)
builder.endObject()
}
}
}

View File

@ -0,0 +1,32 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt
final class CommandSource private (
val channelName: String) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: CommandSource => (this.channelName == x.channelName)
case _ => false
}
override def hashCode: Int = {
37 * (17 + channelName.##)
}
override def toString: String = {
"CommandSource(" + channelName + ")"
}
protected[this] def copy(channelName: String = channelName): CommandSource = {
new CommandSource(channelName)
}
def withChannelName(channelName: String): CommandSource = {
copy(channelName = channelName)
}
}
object CommandSource {
def apply(channelName: String): CommandSource = new CommandSource(channelName)
}

View File

@ -0,0 +1,49 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt
final class Exec private (
val commandLine: String,
val execId: Option[String],
val source: Option[sbt.CommandSource]) extends Serializable {
private def this(commandLine: String, source: Option[sbt.CommandSource]) = this(commandLine, None, source)
override def equals(o: Any): Boolean = o match {
case x: Exec => (this.commandLine == x.commandLine) && (this.execId == x.execId) && (this.source == x.source)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + commandLine.##) + execId.##) + source.##)
}
override def toString: String = {
"Exec(" + commandLine + ", " + execId + ", " + source + ")"
}
protected[this] def copy(commandLine: String = commandLine, execId: Option[String] = execId, source: Option[sbt.CommandSource] = source): Exec = {
new Exec(commandLine, execId, source)
}
def withCommandLine(commandLine: String): Exec = {
copy(commandLine = commandLine)
}
def withExecId(execId: Option[String]): Exec = {
copy(execId = execId)
}
def withExecId(execId: String): Exec = {
copy(execId = Option(execId))
}
def withSource(source: Option[sbt.CommandSource]): Exec = {
copy(source = source)
}
def withSource(source: sbt.CommandSource): Exec = {
copy(source = Option(source))
}
}
object Exec {
def newExecId: String = java.util.UUID.randomUUID.toString
def apply(commandLine: String, source: Option[sbt.CommandSource]): Exec = new Exec(commandLine, None, source)
def apply(commandLine: String, source: sbt.CommandSource): Exec = new Exec(commandLine, None, Option(source))
def apply(commandLine: String, execId: Option[String], source: Option[sbt.CommandSource]): Exec = new Exec(commandLine, execId, source)
def apply(commandLine: String, execId: String, source: sbt.CommandSource): Exec = new Exec(commandLine, Option(execId), Option(source))
}

View File

@ -1,37 +0,0 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.internal.server
final class CommandMessage private (
val `type`: String,
val commandLine: Option[String]) extends Serializable {
private def this(`type`: String) = this(`type`, None)
override def equals(o: Any): Boolean = o match {
case x: CommandMessage => (this.`type` == x.`type`) && (this.commandLine == x.commandLine)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + `type`.##) + commandLine.##)
}
override def toString: String = {
"CommandMessage(" + `type` + ", " + commandLine + ")"
}
protected[this] def copy(`type`: String = `type`, commandLine: Option[String] = commandLine): CommandMessage = {
new CommandMessage(`type`, commandLine)
}
def withType(`type`: String): CommandMessage = {
copy(`type` = `type`)
}
def withCommandLine(commandLine: Option[String]): CommandMessage = {
copy(commandLine = commandLine)
}
}
object CommandMessage {
def apply(`type`: String): CommandMessage = new CommandMessage(`type`, None)
def apply(`type`: String, commandLine: Option[String]): CommandMessage = new CommandMessage(`type`, commandLine)
}

View File

@ -1,57 +0,0 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.internal.server
final class EventMessage private (
val `type`: String,
val status: Option[String],
val commandQueue: scala.Vector[String],
val level: Option[String],
val message: Option[String],
val success: Option[Boolean],
val commandLine: Option[String]) extends Serializable {
private def this(`type`: String) = this(`type`, None, Vector(), None, None, None, None)
override def equals(o: Any): Boolean = o match {
case x: EventMessage => (this.`type` == x.`type`) && (this.status == x.status) && (this.commandQueue == x.commandQueue) && (this.level == x.level) && (this.message == x.message) && (this.success == x.success) && (this.commandLine == x.commandLine)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + `type`.##) + status.##) + commandQueue.##) + level.##) + message.##) + success.##) + commandLine.##)
}
override def toString: String = {
"EventMessage(" + `type` + ", " + status + ", " + commandQueue + ", " + level + ", " + message + ", " + success + ", " + commandLine + ")"
}
protected[this] def copy(`type`: String = `type`, status: Option[String] = status, commandQueue: scala.Vector[String] = commandQueue, level: Option[String] = level, message: Option[String] = message, success: Option[Boolean] = success, commandLine: Option[String] = commandLine): EventMessage = {
new EventMessage(`type`, status, commandQueue, level, message, success, commandLine)
}
def withType(`type`: String): EventMessage = {
copy(`type` = `type`)
}
def withStatus(status: Option[String]): EventMessage = {
copy(status = status)
}
def withCommandQueue(commandQueue: scala.Vector[String]): EventMessage = {
copy(commandQueue = commandQueue)
}
def withLevel(level: Option[String]): EventMessage = {
copy(level = level)
}
def withMessage(message: Option[String]): EventMessage = {
copy(message = message)
}
def withSuccess(success: Option[Boolean]): EventMessage = {
copy(success = success)
}
def withCommandLine(commandLine: Option[String]): EventMessage = {
copy(commandLine = commandLine)
}
}
object EventMessage {
def apply(`type`: String): EventMessage = new EventMessage(`type`, None, Vector(), None, None, None, None)
def apply(`type`: String, status: Option[String], commandQueue: scala.Vector[String], level: Option[String], message: Option[String], success: Option[Boolean], commandLine: Option[String]): EventMessage = new EventMessage(`type`, status, commandQueue, level, message, success, commandLine)
}

View File

@ -1,29 +0,0 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.internal.server.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val CommandMessageFormat: JsonFormat[sbt.internal.server.CommandMessage] = new JsonFormat[sbt.internal.server.CommandMessage] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.server.CommandMessage = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val `type` = unbuilder.readField[String]("type")
val commandLine = unbuilder.readField[Option[String]]("commandLine")
unbuilder.endObject()
sbt.internal.server.CommandMessage(`type`, commandLine)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.server.CommandMessage, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("type", obj.`type`)
builder.addField("commandLine", obj.commandLine)
builder.endObject()
}
}
}

View File

@ -1,39 +0,0 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.internal.server.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val EventMessageFormat: JsonFormat[sbt.internal.server.EventMessage] = new JsonFormat[sbt.internal.server.EventMessage] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.server.EventMessage = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val `type` = unbuilder.readField[String]("type")
val status = unbuilder.readField[Option[String]]("status")
val commandQueue = unbuilder.readField[scala.Vector[String]]("commandQueue")
val level = unbuilder.readField[Option[String]]("level")
val message = unbuilder.readField[Option[String]]("message")
val success = unbuilder.readField[Option[Boolean]]("success")
val commandLine = unbuilder.readField[Option[String]]("commandLine")
unbuilder.endObject()
sbt.internal.server.EventMessage(`type`, status, commandQueue, level, message, success, commandLine)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.server.EventMessage, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("type", obj.`type`)
builder.addField("status", obj.status)
builder.addField("commandQueue", obj.commandQueue)
builder.addField("level", obj.level)
builder.addField("message", obj.message)
builder.addField("success", obj.success)
builder.addField("commandLine", obj.commandLine)
builder.endObject()
}
}
}

View File

@ -1,73 +0,0 @@
{
"codecNamespace": "sbt.internal.server.codec",
"types": [
{
"name": "CommandMessage",
"namespace": "sbt.internal.server",
"type": "record",
"target": "Scala",
"fields": [
{
"name": "type",
"type": "String",
"since": "0.0.0"
},
{
"name": "commandLine",
"type": "Option[String]",
"default": "None",
"since": "0.1.0"
}
]
},
{
"name": "EventMessage",
"namespace": "sbt.internal.server",
"type": "record",
"target": "Scala",
"fields": [
{
"name": "type",
"type": "String",
"since": "0.0.0"
},
{
"name": "status",
"type": "Option[String]",
"default": "None",
"since": "0.1.0"
},
{
"name": "commandQueue",
"type": "scala.Vector[String]",
"default": "Vector()",
"since": "0.1.0"
},
{
"name": "level",
"type": "Option[String]",
"default": "None",
"since": "0.1.0"
},
{
"name": "message",
"type": "Option[String]",
"default": "None",
"since": "0.1.0"
},
{
"name": "success",
"type": "Option[Boolean]",
"default": "None",
"since": "0.1.0"
},
{
"name": "commandLine",
"type": "Option[String]",
"default": "None",
"since": "0.1.0"
}
]
}
]
}

View File

@ -0,0 +1,14 @@
package sbt
@target(Scala)
type Exec {
commandLine: String!
execId: String @since("0.0.1")
source: sbt.CommandSource
#xcompanion def newExecId: String = java.util.UUID.randomUUID.toString
}
type CommandSource {
channelName: String!
}

View File

@ -152,6 +152,9 @@ object BasicCommandStrings {
def Server = "server"
def ServerDetailed = "Provides a network server and an interactive prompt from which commands can be run."
def Client = "client"
def ClientDetailed = "Provides an interactive prompt from which commands can be run on a server."
def StashOnFailure = "sbtStashOnFailure"
def PopOnFailure = "sbtPopOnFailure"

View File

@ -6,7 +6,8 @@ import sbt.internal.util.complete.{ Completion, Completions, DefaultParsers, His
import sbt.internal.util.Types.{ const, idFun }
import sbt.internal.inc.classpath.ClasspathUtilities.toLoader
import sbt.internal.inc.ModuleUtilities
import sbt.internal.{ Exec, CommandSource, CommandStatus }
import sbt.internal.{ ConsolePromptEvent, ConsoleUnpromptEvent }
import sbt.internal.client.NetworkClient
import DefaultParsers._
import Function.tupled
import Command.applyEffect
@ -16,12 +17,11 @@ import BasicKeys._
import java.io.File
import sbt.io.IO
import java.util.concurrent.atomic.AtomicBoolean
import scala.util.control.NonFatal
object BasicCommands {
lazy val allBasicCommands = Seq(nop, ignore, help, completionsCommand, multi, ifLast, append, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, reboot, call, early, exit, continuous, history, shell, server, read, alias) ++ compatCommands
lazy val allBasicCommands = Seq(nop, ignore, help, completionsCommand, multi, ifLast, append, setOnFailure, clearOnFailure,
stashOnFailure, popOnFailure, reboot, call, early, exit, continuous, history, shell, server, client, read, alias) ++ compatCommands
def nop = Command.custom(s => success(() => s))
def ignore = Command.command(FailureWall)(idFun)
@ -82,13 +82,13 @@ object BasicCommands {
state
}
def multiParser(s: State): Parser[Seq[String]] =
def multiParser(s: State): Parser[List[String]] =
{
val nonSemi = token(charClass(_ != ';').+, hide = const(true))
(token(';' ~> OptSpace) flatMap { _ => matched((s.combinedParser & nonSemi) | nonSemi) <~ token(OptSpace) } map (_.trim)).+
(token(';' ~> OptSpace) flatMap { _ => matched((s.combinedParser & nonSemi) | nonSemi) <~ token(OptSpace) } map (_.trim)).+ map { _.toList }
}
def multiApplied(s: State) =
def multiApplied(s: State): Parser[() => State] =
Command.applyEffect(multiParser(s))(_ ::: s)
def multi = Command.custom(multiApplied, Help(Multi, MultiBrief, MultiDetailed))
@ -101,11 +101,11 @@ object BasicCommands {
if (s.remainingCommands.isEmpty) arg :: s else s
}
def append = Command(AppendCommand, Help.more(AppendCommand, AppendLastDetailed))(otherCommandParser) { (s, arg) =>
s.copy(remainingCommands = s.remainingCommands :+ arg)
s.copy(remainingCommands = s.remainingCommands :+ Exec(arg, s.source))
}
def setOnFailure = Command(OnFailure, Help.more(OnFailure, OnFailureDetailed))(otherCommandParser) { (s, arg) =>
s.copy(onFailure = Some(arg))
s.copy(onFailure = Some(Exec(arg, s.source)))
}
private[sbt] def compatCommands = Seq(
Command.command(Compat.ClearOnFailure) { s =>
@ -114,7 +114,7 @@ object BasicCommands {
},
Command.arb(s => token(Compat.OnFailure, hide = const(true)).flatMap(x => otherCommandParser(s))) { (s, arg) =>
s.log.warn(Compat.OnFailureDeprecated)
s.copy(onFailure = Some(arg))
s.copy(onFailure = Some(Exec(arg, s.source)))
},
Command.command(Compat.FailureWall) { s =>
s.log.warn(Compat.FailureWallDeprecated)
@ -187,7 +187,7 @@ object BasicCommands {
val line = reader.readLine(prompt)
line match {
case Some(line) =>
val newState = s.copy(onFailure = Some(Shell), remainingCommands = line +: Shell +: s.remainingCommands).setInteractive(true)
val newState = s.copy(onFailure = Some(Exec(Shell, None)), remainingCommands = Exec(line, s.source) +: Exec(Shell, None) +: s.remainingCommands).setInteractive(true)
if (line.trim.isEmpty) newState else newState.clearGlobalLog
case None => s.setInteractive(false)
}
@ -196,14 +196,29 @@ object BasicCommands {
def server = Command.command(Server, Help.more(Server, ServerDetailed)) { s0 =>
val exchange = State.exchange
val s1 = exchange.run(s0)
exchange.publishStatus(CommandStatus(s0, true), None)
val Exec(source, line) = exchange.blockUntilNextExec
val newState = s1.copy(onFailure = Some(Server), remainingCommands = line +: Server +: s1.remainingCommands).setInteractive(true)
exchange.publishStatus(CommandStatus(newState, false), Some(source))
if (line.trim.isEmpty) newState
exchange.publishEvent(ConsolePromptEvent(s0))
val exec: Exec = exchange.blockUntilNextExec
val newState = s1.copy(onFailure = Some(Exec(Server, None)), remainingCommands = exec +: Exec(Server, None) +: s1.remainingCommands).setInteractive(true)
exchange.publishEvent(ConsoleUnpromptEvent(exec.source))
if (exec.commandLine.trim.isEmpty) newState
else newState.clearGlobalLog
}
def client = Command.make(Client, Help.more(Client, ClientDetailed))(clientParser)
def clientParser(s0: State) =
{
val p = (token(Space) ~> repsep(StringBasic, token(Space))) | (token(EOF) map { case _ => Nil })
applyEffect(p)({ inputArg =>
val arguments = inputArg.toList ++
(s0.remainingCommands.toList match {
case e :: Nil if e.commandLine == "shell" :: Nil => Nil
case xs => xs map { _.commandLine }
})
NetworkClient.run(arguments)
"exit" :: s0.copy(remainingCommands = Nil)
})
}
def read = Command.make(ReadCommand, Help.more(ReadCommand, ReadDetailed))(s => applyEffect(readParser(s))(doRead(s)))
def readParser(s: State) =
{
@ -217,7 +232,7 @@ object BasicCommands {
val port = math.abs(portAndSuccess)
val previousSuccess = portAndSuccess >= 0
readMessage(port, previousSuccess) match {
case Some(message) => (message :: (ReadCommand + " " + port) :: s).copy(onFailure = Some(ReadCommand + " " + (-port)))
case Some(message) => (message :: (ReadCommand + " " + port) :: s).copy(onFailure = Some(Exec(ReadCommand + " " + (-port), s.source)))
case None =>
System.err.println("Connection closed.")
s.fail
@ -225,7 +240,7 @@ object BasicCommands {
case Right(from) =>
val notFound = notReadable(from)
if (notFound.isEmpty)
readLines(from) ::: s // this means that all commands from all files are loaded, parsed, and inserted before any are executed
readLines(from).toList ::: s // this means that all commands from all files are loaded, parsed, and inserted before any are executed
else {
s.log.error("Command file(s) not readable: \n\t" + notFound.mkString("\n\t"))
s

View File

@ -11,6 +11,6 @@ object BasicKeys {
val serverPort = AttributeKey[Int]("server-port", "The port number used by server command.", 10000)
private[sbt] val interactive = AttributeKey[Boolean]("interactive", "True if commands are currently being entered from an interactive environment.", 10)
private[sbt] val classLoaderCache = AttributeKey[ClassLoaderCache]("class-loader-cache", "Caches class loaders based on the classpath entries and last modified times.", 10)
private[sbt] val OnFailureStack = AttributeKey[List[Option[String]]]("on-failure-stack", "Stack that remembers on-failure handlers.", 10)
private[sbt] val OnFailureStack = AttributeKey[List[Option[Exec]]]("on-failure-stack", "Stack that remembers on-failure handlers.", 10)
private[sbt] val explicitGlobalLogLevels = AttributeKey[Boolean]("explicit-global-log-levels", "True if the global logging levels were explicitly set by the user.", 10)
}

View File

@ -7,6 +7,7 @@ import sbt.internal.inc.ReflectUtilities
import sbt.internal.util.complete.{ DefaultParsers, EditDistance, Parser }
import sbt.internal.util.Types.const
import sbt.internal.util.{ AttributeKey, AttributeMap, Util }
import sbt.protocol.ExecStatusEvent
sealed trait Command {
def help: State => Help
@ -87,15 +88,20 @@ object Command {
}
}
def process(command: String, state: State): State =
/** This is the main function State transfer function of the sbt command processing, called by MainLoop.next, */
def process(exec: Exec, state: State): State =
{
val channelName = exec.source map { _.channelName }
State.exchange.publishEvent(ExecStatusEvent("Processing", channelName, exec.execId, Vector()))
val parser = combine(state.definedCommands)
parse(command, parser(state)) match {
val newState = parse(exec.commandLine, parser(state)) match {
case Right(s) => s() // apply command. command side effects happen here
case Left(errMsg) =>
state.log.error(errMsg)
state.fail
}
State.exchange.publishEvent(ExecStatusEvent("Done", channelName, exec.execId, newState.remainingCommands.toVector map { _.commandLine }))
newState
}
def invalidValue(label: String, allowed: Iterable[String])(value: String): String =
"Not a valid " + label + ": " + value + similar(value, allowed)

View File

@ -27,11 +27,12 @@ final case class State(
configuration: xsbti.AppConfiguration,
definedCommands: Seq[Command],
exitHooks: Set[ExitHook],
onFailure: Option[String],
remainingCommands: Seq[String],
onFailure: Option[Exec],
remainingCommands: List[Exec],
history: State.History,
attributes: AttributeMap,
globalLogging: GlobalLogging,
source: Option[CommandSource],
next: State.Next
) extends Identity {
lazy val combinedParser = Command.combine(definedCommands)(this)
@ -45,14 +46,20 @@ trait Identity {
/** Convenience methods for State transformations and operations. */
trait StateOps {
def process(f: (String, State) => State): State
def process(f: (Exec, State) => State): State
/** Schedules `commands` to be run before any remaining commands.*/
def :::(commands: Seq[String]): State
def :::(newCommands: List[String]): State
/** Schedules `commands` to be run before any remaining commands.*/
def ++:(newCommands: List[Exec]): State
/** Schedules `command` to be run before any remaining commands.*/
def ::(command: String): State
/** Schedules `command` to be run before any remaining commands.*/
def +:(command: Exec): State
/** Sets the next command processing action to be to continue processing the next command.*/
def continue: State
@ -69,6 +76,9 @@ trait StateOps {
/** Sets the next command processing action to do.*/
def setNext(n: State.Next): State
/** Sets the current command source channel.*/
def setSource(source: CommandSource): State
@deprecated("Use setNext", "0.11.0") def setResult(ro: Option[xsbti.MainResult]): State
/**
@ -158,9 +168,9 @@ object State {
* @param executed the list of the most recently executed commands, with the most recent command first.
* @param maxSize the maximum number of commands to keep, or 0 to keep an unlimited number.
*/
final class History private[State] (val executed: Seq[String], val maxSize: Int) {
final class History private[State] (val executed: Seq[Exec], val maxSize: Int) {
/** Adds `command` as the most recently executed command.*/
def ::(command: String): History =
def ::(command: Exec): History =
{
val prependTo = if (maxSize > 0 && executed.size >= maxSize) executed.take(maxSize - 1) else executed
new History(command +: prependTo, maxSize)
@ -168,8 +178,8 @@ object State {
/** Changes the maximum number of commands kept, adjusting the current history if necessary.*/
def setMaxSize(size: Int): History =
new History(if (size <= 0) executed else executed.take(size), size)
def currentOption: Option[String] = executed.headOption
def previous: Option[String] = executed.drop(1).headOption
def currentOption: Option[Exec] = executed.headOption
def previous: Option[Exec] = executed.drop(1).headOption
}
/** Constructs an empty command History with a default, finite command limit.*/
def newHistory = new History(Vector.empty, HistoryCommands.MaxLines)
@ -177,29 +187,37 @@ object State {
def defaultReload(state: State): Reboot =
{
val app = state.configuration.provider
new Reboot(app.scalaProvider.version, state.remainingCommands, app.id, state.configuration.baseDirectory)
new Reboot(
app.scalaProvider.version,
state.remainingCommands map { case e: Exec => e.commandLine },
app.id, state.configuration.baseDirectory
)
}
private[sbt] lazy val exchange = new CommandExchange()
/** Provides operations and transformations on State. */
implicit def stateOps(s: State): StateOps = new StateOps {
def process(f: (String, State) => State): State =
def process(f: (Exec, State) => State): State =
s.remainingCommands match {
case Seq() => exit(true)
case Seq(x, xs @ _*) =>
case List() => exit(true)
case x :: xs =>
log.debug(s"> $x")
f(x, s.copy(remainingCommands = xs, history = x :: s.history))
}
def :::(newCommands: Seq[String]): State = s.copy(remainingCommands = newCommands ++ s.remainingCommands)
def ::(command: String): State = (command :: Nil) ::: this
def :::(newCommands: List[String]): State = ++:(newCommands map { Exec(_, s.source) })
def ++:(newCommands: List[Exec]): State = s.copy(remainingCommands = newCommands ::: s.remainingCommands)
def ::(command: String): State = +:(Exec(command, s.source))
def +:(command: Exec): State = (command :: Nil) ++: this
def ++(newCommands: Seq[Command]): State = s.copy(definedCommands = (s.definedCommands ++ newCommands).distinct)
def +(newCommand: Command): State = this ++ (newCommand :: Nil)
def baseDir: File = s.configuration.baseDirectory
def setNext(n: Next) = s.copy(next = n)
def setSource(x: CommandSource): State = s.copy(source = Some(x))
def setResult(ro: Option[xsbti.MainResult]) = ro match { case None => continue; case Some(r) => setNext(new Return(r)) }
def continue = setNext(Continue)
def reboot(full: Boolean) = { runExitHooks(); throw new xsbti.FullReload(s.remainingCommands.toArray, full) }
def reboot(full: Boolean) = { runExitHooks(); throw new xsbti.FullReload((s.remainingCommands map { case e: Exec => e.commandLine }).toArray, full) }
def reload = runExitHooks().setNext(new Return(defaultReload(s)))
def clearGlobalLog = setNext(ClearGlobalLog)
def keepLastLog = setNext(KeepLastLog)
@ -220,7 +238,7 @@ object State {
else
applyOnFailure(s, remaining, s.copy(remainingCommands = remaining))
}
private[this] def applyOnFailure(s: State, remaining: Seq[String], noHandler: => State): State =
private[this] def applyOnFailure(s: State, remaining: List[Exec], noHandler: => State): State =
s.onFailure match {
case Some(c) => s.copy(remainingCommands = c +: remaining, onFailure = None)
case None => noHandler

View File

@ -2,6 +2,7 @@ package sbt
package internal
import java.util.concurrent.ConcurrentLinkedQueue
import sbt.protocol.EventMessage
/**
* A command channel represents an IO device such as network socket or human
@ -14,23 +15,22 @@ abstract class CommandChannel {
commandQueue.add(exec)
def poll: Option[Exec] = Option(commandQueue.poll)
/** start listening for a command exec. */
def run(s: State): State
def publishStatus(status: CommandStatus, lastSource: Option[CommandSource]): Unit
def publishEvent(event: EventMessage): Unit
def publishBytes(bytes: Array[Byte]): Unit
def shutdown(): Unit
def name: String
}
case class Exec(source: CommandSource, commandLine: String)
// case class Exec(commandLine: String, source: Option[CommandSource])
sealed trait CommandSource
object CommandSource {
case object Human extends CommandSource
case object Network extends CommandSource
}
// case class CommandSource(channelName: String)
/**
* This is a data that is passed on to the channels.
* The canEnter paramter indicates that the console devise or UI
* should stop listening.
/*
* This is a data passed specifically for local prompting console.
*/
case class CommandStatus(state: State, canEnter: Boolean)
case class ConsolePromptEvent(state: State) extends EventMessage
/*
* This is a data passed specifically for unprompting local console.
*/
case class ConsoleUnpromptEvent(lastSource: Option[CommandSource]) extends EventMessage

View File

@ -1,9 +1,15 @@
package sbt
package internal
import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
import java.net.SocketException
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicInteger
import sbt.internal.server._
import sbt.protocol.{ EventMessage, Serialization, ChannelAcceptedEvent }
import scala.collection.mutable.ListBuffer
import scala.annotation.tailrec
import BasicKeys.serverPort
import java.net.Socket
/**
* The command exchange merges multiple command channels (e.g. network and console),
@ -12,14 +18,17 @@ import java.util.concurrent.ConcurrentLinkedQueue
* this exchange, which could serve command request from either of the channel.
*/
private[sbt] final class CommandExchange {
private val lock = new AnyRef {}
private var server: Option[ServerInstance] = None
private var consoleChannel: Option[ConsoleChannel] = None
private val commandQueue: ConcurrentLinkedQueue[Exec] = new ConcurrentLinkedQueue()
private val channelBuffer: ListBuffer[CommandChannel] = new ListBuffer()
private val nextChannelId: AtomicInteger = new AtomicInteger(0)
def channels: List[CommandChannel] = channelBuffer.toList
def subscribe(c: CommandChannel): Unit =
channelBuffer.append(c)
subscribe(new ConsoleChannel())
subscribe(new NetworkChannel())
lock.synchronized {
channelBuffer.append(c)
}
// periodically move all messages from all the channels
@tailrec def blockUntilNextExec: Exec =
@ -40,13 +49,86 @@ private[sbt] final class CommandExchange {
}
}
// fanout run to all channels
def run(s: State): State =
(s /: channels) { (acc, c) => c.run(acc) }
{
consoleChannel match {
case Some(x) => // do nothing
case _ =>
val x = new ConsoleChannel("console0")
consoleChannel = Some(x)
subscribe(x)
}
runServer(s)
}
// fanout publishStatus to all channels
def publishStatus(status: CommandStatus, lastSource: Option[CommandSource]): Unit =
channels foreach { c =>
c.publishStatus(status, lastSource)
private def newChannelName: String = s"channel-${nextChannelId.incrementAndGet()}"
private def runServer(s: State): State =
{
val port = (s get serverPort) match {
case Some(x) => x
case None => 5001
}
def onIncomingSocket(socket: Socket): Unit =
{
s.log.info(s"new client connected from: ${socket.getPort}")
val channel = new NetworkChannel(newChannelName, socket)
subscribe(channel)
channel.publishEvent(ChannelAcceptedEvent(channel.name))
}
server match {
case Some(x) => // do nothing
case _ =>
server = Some(Server.start("127.0.0.1", port, onIncomingSocket, s.log))
}
s
}
def shutdown(): Unit =
{
channels foreach { c =>
c.shutdown()
}
// interrupt and kill the thread
server.foreach(_.shutdown())
server = None
}
// fanout publisEvent
def publishEvent(event: EventMessage): Unit =
{
val toDel: ListBuffer[CommandChannel] = ListBuffer.empty
event match {
// Special treatment for ConsolePromptEvent since it's hand coded without codec.
case e: ConsolePromptEvent =>
channels collect {
case c: ConsoleChannel => c.publishEvent(e)
}
case e: ConsoleUnpromptEvent =>
channels collect {
case c: ConsoleChannel => c.publishEvent(e)
}
case _ =>
// TODO do not do this on the calling thread
val bytes = Serialization.serializeEvent(event)
channels.foreach {
case c: ConsoleChannel =>
c.publishEvent(event)
case c: NetworkChannel =>
try {
c.publishBytes(bytes)
} catch {
case e: SocketException =>
toDel += c
}
}
}
toDel.toList match {
case Nil => // do nothing
case xs =>
lock.synchronized {
channelBuffer --= xs
}
}
}
}

View File

@ -4,11 +4,11 @@ package internal
import sbt.internal.util._
import BasicKeys._
import java.io.File
import sbt.protocol.EventMessage
private[sbt] final class ConsoleChannel extends CommandChannel {
private[sbt] final class ConsoleChannel(val name: String) extends CommandChannel {
private var askUserThread: Option[Thread] = None
def makeAskUserThread(status: CommandStatus): Thread = new Thread("ask-user-thread") {
val s = status.state
def makeAskUserThread(s: State): Thread = new Thread("ask-user-thread") {
val history = (s get historyPath) getOrElse Some(new File(s.baseDir, ".history"))
val prompt = (s get shellPrompt) match {
case Some(pf) => pf(s)
@ -19,8 +19,8 @@ private[sbt] final class ConsoleChannel extends CommandChannel {
// This internally handles thread interruption and returns Some("")
val line = reader.readLine(prompt)
line match {
case Some(cmd) => append(Exec(CommandSource.Human, cmd))
case None => append(Exec(CommandSource.Human, "exit"))
case Some(cmd) => append(Exec(cmd, Some(Exec.newExecId), Some(CommandSource(name))))
case None => append(Exec("exit", Some(Exec.newExecId), Some(CommandSource(name))))
}
askUserThread = None
}
@ -28,25 +28,29 @@ private[sbt] final class ConsoleChannel extends CommandChannel {
def run(s: State): State = s
def publishStatus(status: CommandStatus, lastSource: Option[CommandSource]): Unit =
if (status.canEnter) {
askUserThread match {
case Some(x) => //
case _ =>
val x = makeAskUserThread(status)
askUserThread = Some(x)
x.start
}
} else {
lastSource match {
case Some(src) if src != CommandSource.Human =>
askUserThread match {
case Some(x) =>
shutdown()
case _ =>
}
case _ =>
}
def publishBytes(bytes: Array[Byte]): Unit = ()
def publishEvent(event: EventMessage): Unit =
event match {
case e: ConsolePromptEvent =>
askUserThread match {
case Some(x) => //
case _ =>
val x = makeAskUserThread(e.state)
askUserThread = Some(x)
x.start
}
case e: ConsoleUnpromptEvent =>
e.lastSource match {
case Some(src) if src.channelName != name =>
askUserThread match {
case Some(x) =>
shutdown()
case _ =>
}
case _ =>
}
case _ => //
}
def shutdown(): Unit =

View File

@ -1,43 +0,0 @@
package sbt
package internal
import sbt.internal.server._
import BasicKeys._
private[sbt] final class NetworkChannel extends CommandChannel {
private var server: Option[ServerInstance] = None
def run(s: State): State =
{
val port = (s get serverPort) match {
case Some(x) => x
case None => 5001
}
def onCommand(command: internal.server.Command): Unit = {
command match {
case Execution(cmd) => append(Exec(CommandSource.Network, cmd))
}
}
server match {
case Some(x) => // do nothing
case _ =>
server = Some(Server.start("127.0.0.1", port, onCommand, s.log))
}
s
}
def shutdown(): Unit =
{
// interrupt and kill the thread
server.foreach(_.shutdown())
server = None
}
def publishStatus(cmdStatus: CommandStatus, lastSource: Option[CommandSource]): Unit = {
server.foreach(server =>
server.publish(
if (cmdStatus.canEnter) StatusEvent(Ready)
else StatusEvent(Processing("TODO current command", cmdStatus.state.remainingCommands))
))
}
}

View File

@ -0,0 +1,116 @@
/*
* Copyright (C) 2016 Lightbend Inc. <http://www.lightbend.com>
*/
package sbt
package internal
package client
import java.net.{ URI, Socket, InetAddress, SocketException }
import java.util.UUID
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
import sbt.protocol._
import sbt.internal.util.JLine
import scala.collection.mutable.ListBuffer
import scala.util.control.NonFatal
class NetworkClient(arguments: List[String]) { self =>
private val channelName = new AtomicReference("_")
private val status = new AtomicReference("Ready")
private val lock: AnyRef = new AnyRef {}
private val running = new AtomicBoolean(true)
private val pendingExecIds = ListBuffer.empty[String]
def usageError = sys.error("Expecting: sbt client 127.0.0.1:port")
val connection = init()
start()
def init(): ServerConnection = {
val u = arguments match {
case List(x) =>
if (x contains "://") new URI(x)
else new URI("tcp://" + x)
case _ => usageError
}
val host = Option(u.getHost) match {
case None => usageError
case Some(x) => x
}
val port = Option(u.getPort) match {
case None => usageError
case Some(x) if x == -1 => usageError
case Some(x) => x
}
println(s"client on port $port")
val socket = new Socket(InetAddress.getByName(host), port)
new ServerConnection(socket) {
override def onEvent(event: EventMessage): Unit = self.onEvent(event)
override def onShutdown(): Unit =
{
running.set(false)
}
}
}
def onEvent(event: EventMessage): Unit =
event match {
case e: ChannelAcceptedEvent =>
channelName.set(e.channelName)
println(event)
case e: ExecStatusEvent =>
status.set(e.status)
// println(event)
e.execId foreach { execId =>
if (e.status == "Done" && (pendingExecIds contains execId)) {
lock.synchronized {
pendingExecIds -= execId
}
}
}
case e => println(e.toString)
}
def start(): Unit =
{
val reader = JLine.simple(None, JLine.HandleCONT, injectThreadSleep = true)
while (running.get) {
reader.readLine("> ", None) match {
case Some("exit") =>
running.set(false)
case Some(s) =>
val execId = UUID.randomUUID.toString
publishCommand(ExecCommand(s, execId))
lock.synchronized {
pendingExecIds += execId
}
while (pendingExecIds contains execId) {
Thread.sleep(100)
}
case _ => //
}
}
}
def publishCommand(command: CommandMessage): Unit =
{
val bytes = Serialization.serializeCommand(command)
try {
connection.publish(bytes)
} catch {
case e: SocketException =>
// log.debug(e.getMessage)
// toDel += client
}
lock.synchronized {
status.set("Processing")
}
}
}
object NetworkClient {
def run(arguments: List[String]): Unit =
try {
new NetworkClient(arguments)
} catch {
case NonFatal(e) => println(e.getMessage)
}
}

View File

@ -3,19 +3,20 @@
*/
package sbt
package internal
package server
package client
import java.net.{ SocketTimeoutException, Socket }
import java.util.concurrent.atomic.AtomicBoolean
import sbt.protocol._
abstract class ClientConnection(connection: Socket) {
abstract class ServerConnection(connection: Socket) {
private val running = new AtomicBoolean(true)
private val delimiter: Byte = '\n'.toByte
private val out = connection.getOutputStream
val thread = new Thread(s"sbt-client-${connection.getPort}") {
val thread = new Thread(s"sbt-serverconnection-${connection.getPort}") {
override def run(): Unit = {
try {
val readBuffer = new Array[Byte](4096)
@ -26,18 +27,19 @@ abstract class ClientConnection(connection: Socket) {
while (bytesRead != -1 && running.get) {
try {
bytesRead = in.read(readBuffer)
val bytes = readBuffer.toVector.take(bytesRead)
buffer = buffer ++ bytes
buffer = buffer ++ readBuffer.toVector.take(bytesRead)
// handle un-framing
val delimPos = bytes.indexOf(delimiter)
val delimPos = buffer.indexOf(delimiter)
if (delimPos > 0) {
val chunk = buffer.take(delimPos)
buffer = buffer.drop(delimPos + 1)
Serialization.deserialize(chunk).fold(
errorDesc => println("Got invalid chunk from client: " + errorDesc),
onCommand
Serialization.deserializeEvent(chunk).fold(
{ errorDesc =>
val s = new String(chunk.toArray, "UTF-8")
println(s"Got invalid chunk from server: $s \n" + errorDesc)
},
onEvent
)
}
@ -45,7 +47,6 @@ abstract class ClientConnection(connection: Socket) {
case _: SocketTimeoutException => // its ok
}
}
} finally {
shutdown()
}
@ -53,18 +54,21 @@ abstract class ClientConnection(connection: Socket) {
}
thread.start()
def publish(event: Array[Byte]): Unit = {
out.write(event)
out.write(delimiter)
def publish(command: Array[Byte]): Unit = {
out.write(command)
out.write(delimiter.toInt)
out.flush()
}
def onCommand(command: Command): Unit
def onEvent(event: EventMessage): Unit
def onShutdown(): Unit
def shutdown(): Unit = {
println("Shutting down client connection")
running.set(false)
out.close()
onShutdown
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2016 Lightbend Inc. <http://www.typesafe.com>
*/
package sbt
package internal
package server
import java.net.{ Socket, SocketTimeoutException }
import java.util.concurrent.atomic.AtomicBoolean
import sbt.protocol.{ Serialization, CommandMessage, ExecCommand, EventMessage }
final class NetworkChannel(val name: String, connection: Socket) extends CommandChannel {
private val running = new AtomicBoolean(true)
private val delimiter: Byte = '\n'.toByte
private val out = connection.getOutputStream
val thread = new Thread(s"sbt-networkchannel-${connection.getPort}") {
override def run(): Unit = {
try {
val readBuffer = new Array[Byte](4096)
val in = connection.getInputStream
connection.setSoTimeout(5000)
var buffer: Vector[Byte] = Vector.empty
var bytesRead = 0
while (bytesRead != -1 && running.get) {
try {
bytesRead = in.read(readBuffer)
buffer = buffer ++ readBuffer.toVector.take(bytesRead)
// handle un-framing
val delimPos = buffer.indexOf(delimiter)
if (delimPos > 0) {
val chunk = buffer.take(delimPos)
buffer = buffer.drop(delimPos + 1)
Serialization.deserializeCommand(chunk).fold(
errorDesc => println("Got invalid chunk from client: " + errorDesc),
onCommand
)
}
} catch {
case _: SocketTimeoutException => // its ok
}
}
} finally {
shutdown()
}
}
}
thread.start()
def publishEvent(event: EventMessage): Unit =
{
val bytes = Serialization.serializeEvent(event)
publishBytes(bytes)
}
def publishBytes(event: Array[Byte]): Unit =
{
out.write(event)
out.write(delimiter.toInt)
out.flush()
}
def onCommand(command: CommandMessage): Unit =
command match {
case x: ExecCommand => append(Exec(x.commandLine, x.execId orElse Some(Exec.newExecId), Some(CommandSource(name))))
}
def shutdown(): Unit = {
println("Shutting down client connection")
running.set(false)
out.close()
}
}

View File

@ -1,80 +0,0 @@
/*
* Copyright (C) 2016 Lightbend Inc. <http://www.typesafe.com>
*/
package sbt
package internal
package server
import sjsonnew.{ JsonFormat, BasicJsonProtocol }
import sjsonnew.support.scalajson.unsafe.{ Converter, CompactPrinter }
import scala.json.ast.unsafe.JValue
import sjsonnew.support.scalajson.unsafe.Parser
import java.nio.ByteBuffer
import scala.util.{ Success, Failure }
object Serialization {
def serialize(event: Event): Array[Byte] =
{
import ServerCodec._
val msg = toMessage(event)
val json: JValue = Converter.toJson[EventMessage](msg).get
CompactPrinter(json).getBytes("UTF-8")
}
def toMessage(event: Event): EventMessage =
event match {
case LogEvent(level, message) =>
EventMessage(
`type` = "logEvent",
status = None, commandQueue = Vector(),
level = Some(level), message = Some(message), success = None, commandLine = None
)
case StatusEvent(Ready) =>
EventMessage(
`type` = "statusEvent",
status = Some("ready"), commandQueue = Vector(),
level = None, message = None, success = None, commandLine = None
)
case StatusEvent(Processing(command, commandQueue)) =>
EventMessage(
`type` = "statusEvent",
status = Some("processing"), commandQueue = commandQueue.toVector,
level = None, message = None, success = None, commandLine = None
)
case ExecutionEvent(command, status) =>
EventMessage(
`type` = "executionEvent",
status = None, commandQueue = Vector(),
level = None, message = None, success = Some(status), commandLine = Some(command)
)
}
/**
* @return A command or an invalid input description
*/
def deserialize(bytes: Seq[Byte]): Either[String, Command] =
{
val buffer = ByteBuffer.wrap(bytes.toArray)
Parser.parseFromByteBuffer(buffer) match {
case Success(json) =>
import ServerCodec._
Converter.fromJson[CommandMessage](json) match {
case Success(command) =>
command.`type` match {
case "exec" =>
command.commandLine match {
case Some(cmd) => Right(Execution(cmd))
case None => Left("Missing or invalid command_line field")
}
case cmd => Left(s"Unknown command type $cmd")
}
case Failure(e) => Left(e.getMessage)
}
case Failure(e) =>
Left(s"Parse error: ${e.getMessage}")
}
}
}
object ServerCodec extends ServerCodec
trait ServerCodec extends codec.EventMessageFormats with codec.CommandMessageFormats with BasicJsonProtocol

View File

@ -5,22 +5,21 @@ package sbt
package internal
package server
import java.net.{ SocketTimeoutException, InetAddress, ServerSocket, SocketException }
import java.net.{ SocketTimeoutException, InetAddress, ServerSocket, Socket }
import java.util.concurrent.atomic.AtomicBoolean
import sbt.util.Logger
import scala.collection.mutable
private[sbt] sealed trait ServerInstance {
def shutdown(): Unit
def publish(event: Event): Unit
}
private[sbt] object Server {
def start(host: String, port: Int, onIncommingCommand: Command => Unit, log: Logger): ServerInstance =
def start(host: String, port: Int, onIncomingSocket: Socket => Unit,
/*onIncommingCommand: CommandMessage => Unit,*/ log: Logger): ServerInstance =
new ServerInstance {
val lock = new AnyRef {}
val clients: mutable.ListBuffer[ClientConnection] = mutable.ListBuffer.empty
// val lock = new AnyRef {}
// val clients: mutable.ListBuffer[ClientConnection] = mutable.ListBuffer.empty
val running = new AtomicBoolean(true)
val serverThread = new Thread("sbt-socket-server") {
@ -33,18 +32,7 @@ private[sbt] object Server {
while (running.get()) {
try {
val socket = serverSocket.accept()
log.info(s"new client connected from: ${socket.getPort}")
val connection = new ClientConnection(socket) {
override def onCommand(command: Command): Unit = {
onIncommingCommand(command)
}
}
lock.synchronized {
clients += connection
}
onIncomingSocket(socket)
} catch {
case _: SocketTimeoutException => // its ok
}
@ -54,25 +42,6 @@ private[sbt] object Server {
}
serverThread.start()
/** Publish an event to all connected clients */
def publish(event: Event): Unit = {
// TODO do not do this on the calling thread
val bytes = Serialization.serialize(event)
lock.synchronized {
val toDel: mutable.ListBuffer[ClientConnection] = mutable.ListBuffer.empty
clients.foreach { client =>
try {
client.publish(bytes)
} catch {
case e: SocketException =>
log.debug(e.getMessage)
toDel += client
}
}
clients --= toDel.toList
}
}
override def shutdown(): Unit = {
log.info("shutting down server")
running.set(false)

View File

@ -1,26 +0,0 @@
/*
* Copyright (C) 2016 Lightbend Inc. <http://www.typesafe.com>
*/
package sbt
package internal
package server
/*
* These classes are the protocol for client-server interaction,
* commands can come from the client side, while events are emitted
* from sbt to inform the client of state changes etc.
*/
private[sbt] sealed trait Event
private[sbt] final case class LogEvent(level: String, message: String) extends Event
sealed trait Status
private[sbt] final case object Ready extends Status
private[sbt] final case class Processing(command: String, commandQueue: Seq[String]) extends Status
private[sbt] final case class StatusEvent(status: Status) extends Event
private[sbt] final case class ExecutionEvent(command: String, success: Boolean) extends Event
private[sbt] sealed trait Command
private[sbt] final case class Execution(cmd: String) extends Command

View File

@ -137,7 +137,7 @@ object Cross {
Seq(s"$SwitchCommand $verbose $version", projects.map(_ + "/" + aggCommand).mkString("all ", " ", ""))
}
allCommands ::: CrossRestoreSessionCommand :: captureCurrentSession(state, x)
allCommands.toList ::: CrossRestoreSessionCommand :: captureCurrentSession(state, x)
}
}
@ -169,7 +169,7 @@ object Cross {
private def switchCommandImpl(state: State, args: Switch): State = {
val switchedState = switchScalaVersion(args, state)
args.command.toSeq ::: switchedState
args.command.toList ::: switchedState
}
private def switchScalaVersion(switch: Switch, state: State): State = {

View File

@ -94,9 +94,9 @@ object StandardMain {
import BasicCommandStrings.isEarlyCommand
val userCommands = configuration.arguments.map(_.trim)
val (earlyCommands, normalCommands) = (preCommands ++ userCommands).partition(isEarlyCommand)
val commands = earlyCommands ++ normalCommands
val commands = (earlyCommands ++ normalCommands).toList map { x => Exec(x, None) }
val initAttrs = BuiltinCommands.initialAttributes
val s = State(configuration, initialDefinitions, Set.empty, None, commands, State.newHistory, initAttrs, initialGlobalLogging, State.Continue)
val s = State(configuration, initialDefinitions, Set.empty, None, commands, State.newHistory, initAttrs, initialGlobalLogging, None, State.Continue)
s.initializeClassLoaderCache
}
}
@ -115,7 +115,7 @@ object BuiltinCommands {
def DefaultCommands: Seq[Command] = Seq(ignore, help, completionsCommand, about, tasks, settingsCommand, loadProject,
projects, project, reboot, read, history, set, sessionCommand, inspect, loadProjectImpl, loadFailed, Cross.crossBuild, Cross.switchVersion,
Cross.crossRestoreSession, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, setLogLevel, plugin, plugins,
ifLast, multi, shell, BasicCommands.server, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, early, initialize, act) ++
ifLast, multi, shell, BasicCommands.server, BasicCommands.client, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, early, initialize, act) ++
compatCommands
def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil
@ -129,7 +129,7 @@ object BuiltinCommands {
// This parser schedules the default boot commands unless overridden by an alias
def bootParser(s: State) =
{
val orElse = () => DefaultBootCommands ::: s
val orElse = () => DefaultBootCommands.toList ::: s
delegateToAlias(BootCommand, success(orElse))(s)
}
@ -242,7 +242,7 @@ object BuiltinCommands {
}
def initialize = Command.command(InitCommand) { s =>
/*"load-commands -base ~/.sbt/commands" :: */ readLines(readable(sbtRCs(s))) ::: s
/*"load-commands -base ~/.sbt/commands" :: */ readLines(readable(sbtRCs(s))).toList ::: s
}
def eval = Command.single(EvalCommand, Help.more(EvalCommand, evalDetailed)) { (s, arg) =>

View File

@ -35,21 +35,15 @@ object Aggregation {
runTasks(s, structure, ts, DummyTaskMap(Nil), show)
}
@deprecated("Use `timedRun` and `showRun` directly or use `runTasks`.", "0.13.0")
def runTasksWithResult[T](s: State, structure: BuildStructure, ts: Values[Task[T]], extra: DummyTaskMap, show: ShowConfig)(implicit display: Show[ScopedKey[_]]): (State, Result[Seq[KeyValue[T]]]) =
private def showRun[T](complete: Complete[T], show: ShowConfig)(implicit display: Show[ScopedKey[_]]): Unit =
{
val complete = timedRun[T](s, ts, extra)
showRun(complete, show)
(complete.state, complete.results)
import complete._
val log = state.log
val extracted = Project.extract(state)
val success = results match { case Value(_) => true; case Inc(_) => false }
results.toEither.right.foreach { r => if (show.taskValues) printSettings(r, show.print) }
if (show.success) printSuccess(start, stop, extracted, success, log)
}
def showRun[T](complete: Complete[T], show: ShowConfig)(implicit display: Show[ScopedKey[_]]): Unit = {
import complete._
val log = state.log
val extracted = Project.extract(state)
val success = results match { case Value(_) => true; case Inc(_) => false }
results.toEither.right.foreach { r => if (show.taskValues) printSettings(r, show.print) }
if (show.success) printSuccess(start, stop, extracted, success, log)
}
def timedRun[T](s: State, ts: Values[Task[T]], extra: DummyTaskMap): Complete[T] =
{
import EvaluateTask._

View File

@ -20,7 +20,7 @@ object IvyConsole {
final val Name = "ivy-console"
lazy val command =
Command.command(Name) { state =>
val Dependencies(managed, repos, unmanaged) = parseDependencies(state.remainingCommands, state.log)
val Dependencies(managed, repos, unmanaged) = parseDependencies(state.remainingCommands map { _.commandLine }, state.log)
val base = new File(CommandUtil.bootDirectory(state), Name)
IO.createDirectory(base)
@ -39,7 +39,7 @@ object IvyConsole {
val append = Load.transformSettings(Load.projectScope(currentRef), currentRef.build, rootProject, depSettings)
val newStructure = Load.reapply(session.original ++ append, structure)
val newState = state.copy(remainingCommands = "console-quick" :: Nil)
val newState = state.copy(remainingCommands = Exec("console-quick", None) :: Nil)
Project.setProject(session, newStructure, newState)
}

View File

@ -19,7 +19,7 @@ object Script {
final val Name = "script"
lazy val command =
Command.command(Name) { state =>
val scriptArg = state.remainingCommands.headOption getOrElse sys.error("No script file specified")
val scriptArg = state.remainingCommands.headOption map { _.commandLine } getOrElse sys.error("No script file specified")
val scriptFile = new File(scriptArg).getAbsoluteFile
val hash = Hash.halve(Hash.toHex(Hash(scriptFile.getAbsolutePath)))
val base = new File(CommandUtil.bootDirectory(state), hash)

View File

@ -52,7 +52,7 @@ object FakeState {
try {
System.setOut(new PrintStream(outBuffer, true))
val state = FakeState(enabledPlugins: _*)
Command.process(input, state)
Command.process(Exec(input, None), state)
new String(outBuffer.toByteArray)
} finally {
System.setOut(previousOut)
@ -106,10 +106,11 @@ object FakeState {
Seq(BuiltinCommands.plugin),
Set.empty,
None,
Seq.empty,
List(),
State.newHistory,
attributes,
GlobalLogging.initial(MainLogging.globalDefault(ConsoleOut.systemOut), File.createTempFile("sbt", ".log"), ConsoleOut.systemOut),
None,
State.Continue
)

View File

@ -0,0 +1,32 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
final class ChannelAcceptedEvent private (
val channelName: String) extends sbt.protocol.EventMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: ChannelAcceptedEvent => (this.channelName == x.channelName)
case _ => false
}
override def hashCode: Int = {
37 * (17 + channelName.##)
}
override def toString: String = {
"ChannelAcceptedEvent(" + channelName + ")"
}
protected[this] def copy(channelName: String = channelName): ChannelAcceptedEvent = {
new ChannelAcceptedEvent(channelName)
}
def withChannelName(channelName: String): ChannelAcceptedEvent = {
copy(channelName = channelName)
}
}
object ChannelAcceptedEvent {
def apply(channelName: String): ChannelAcceptedEvent = new ChannelAcceptedEvent(channelName)
}

View File

@ -0,0 +1,26 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
/** Message to invoke command. */
abstract class CommandMessage() extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: CommandMessage => true
case _ => false
}
override def hashCode: Int = {
17
}
override def toString: String = {
"CommandMessage()"
}
}
object CommandMessage {
}

View File

@ -0,0 +1,26 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
/** Message for events. */
abstract class EventMessage() extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: EventMessage => true
case _ => false
}
override def hashCode: Int = {
17
}
override def toString: String = {
"EventMessage()"
}
}
object EventMessage {
}

View File

@ -0,0 +1,42 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
/** Command to execute sbt command. */
final class ExecCommand private (
val commandLine: String,
val execId: Option[String]) extends sbt.protocol.CommandMessage() with Serializable {
private def this(commandLine: String) = this(commandLine, None)
override def equals(o: Any): Boolean = o match {
case x: ExecCommand => (this.commandLine == x.commandLine) && (this.execId == x.execId)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + commandLine.##) + execId.##)
}
override def toString: String = {
"ExecCommand(" + commandLine + ", " + execId + ")"
}
protected[this] def copy(commandLine: String = commandLine, execId: Option[String] = execId): ExecCommand = {
new ExecCommand(commandLine, execId)
}
def withCommandLine(commandLine: String): ExecCommand = {
copy(commandLine = commandLine)
}
def withExecId(execId: Option[String]): ExecCommand = {
copy(execId = execId)
}
def withExecId(execId: String): ExecCommand = {
copy(execId = Option(execId))
}
}
object ExecCommand {
def apply(commandLine: String): ExecCommand = new ExecCommand(commandLine, None)
def apply(commandLine: String, execId: Option[String]): ExecCommand = new ExecCommand(commandLine, execId)
def apply(commandLine: String, execId: String): ExecCommand = new ExecCommand(commandLine, Option(execId))
}

View File

@ -0,0 +1,52 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
/** Status event. */
final class ExecStatusEvent private (
val status: String,
val channelName: Option[String],
val execId: Option[String],
val commandQueue: Vector[String]) extends sbt.protocol.EventMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: ExecStatusEvent => (this.status == x.status) && (this.channelName == x.channelName) && (this.execId == x.execId) && (this.commandQueue == x.commandQueue)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (37 * (17 + status.##) + channelName.##) + execId.##) + commandQueue.##)
}
override def toString: String = {
"ExecStatusEvent(" + status + ", " + channelName + ", " + execId + ", " + commandQueue + ")"
}
protected[this] def copy(status: String = status, channelName: Option[String] = channelName, execId: Option[String] = execId, commandQueue: Vector[String] = commandQueue): ExecStatusEvent = {
new ExecStatusEvent(status, channelName, execId, commandQueue)
}
def withStatus(status: String): ExecStatusEvent = {
copy(status = status)
}
def withChannelName(channelName: Option[String]): ExecStatusEvent = {
copy(channelName = channelName)
}
def withChannelName(channelName: String): ExecStatusEvent = {
copy(channelName = Option(channelName))
}
def withExecId(execId: Option[String]): ExecStatusEvent = {
copy(execId = execId)
}
def withExecId(execId: String): ExecStatusEvent = {
copy(execId = Option(execId))
}
def withCommandQueue(commandQueue: Vector[String]): ExecStatusEvent = {
copy(commandQueue = commandQueue)
}
}
object ExecStatusEvent {
def apply(status: String, channelName: Option[String], execId: Option[String], commandQueue: Vector[String]): ExecStatusEvent = new ExecStatusEvent(status, channelName, execId, commandQueue)
def apply(status: String, channelName: String, execId: String, commandQueue: Vector[String]): ExecStatusEvent = new ExecStatusEvent(status, Option(channelName), Option(execId), commandQueue)
}

View File

@ -0,0 +1,37 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
/** Execution event. */
final class ExecutionEvent private (
val success: String,
val commandLine: String) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: ExecutionEvent => (this.success == x.success) && (this.commandLine == x.commandLine)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + success.##) + commandLine.##)
}
override def toString: String = {
"ExecutionEvent(" + success + ", " + commandLine + ")"
}
protected[this] def copy(success: String = success, commandLine: String = commandLine): ExecutionEvent = {
new ExecutionEvent(success, commandLine)
}
def withSuccess(success: String): ExecutionEvent = {
copy(success = success)
}
def withCommandLine(commandLine: String): ExecutionEvent = {
copy(commandLine = commandLine)
}
}
object ExecutionEvent {
def apply(success: String, commandLine: String): ExecutionEvent = new ExecutionEvent(success, commandLine)
}

View File

@ -0,0 +1,37 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
/** Log event. */
final class LogEvent private (
val level: String,
val message: String) extends sbt.protocol.EventMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: LogEvent => (this.level == x.level) && (this.message == x.message)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + level.##) + message.##)
}
override def toString: String = {
"LogEvent(" + level + ", " + message + ")"
}
protected[this] def copy(level: String = level, message: String = message): LogEvent = {
new LogEvent(level, message)
}
def withLevel(level: String): LogEvent = {
copy(level = level)
}
def withMessage(message: String): LogEvent = {
copy(message = message)
}
}
object LogEvent {
def apply(level: String, message: String): LogEvent = new LogEvent(level, message)
}

View File

@ -0,0 +1,27 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait ChannelAcceptedEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val ChannelAcceptedEventFormat: JsonFormat[sbt.protocol.ChannelAcceptedEvent] = new JsonFormat[sbt.protocol.ChannelAcceptedEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ChannelAcceptedEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val channelName = unbuilder.readField[String]("channelName")
unbuilder.endObject()
sbt.protocol.ChannelAcceptedEvent(channelName)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.ChannelAcceptedEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("channelName", obj.channelName)
builder.endObject()
}
}
}

View File

@ -0,0 +1,10 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ExecCommandFormats =>
implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat1[sbt.protocol.CommandMessage, sbt.protocol.ExecCommand]("type")
}

View File

@ -0,0 +1,27 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait ConsolePromptEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val ConsolePromptEventFormat: JsonFormat[sbt.protocol.ConsolePromptEvent] = new JsonFormat[sbt.protocol.ConsolePromptEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ConsolePromptEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
unbuilder.endObject()
sbt.protocol.ConsolePromptEvent()
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.ConsolePromptEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.endObject()
}
}
}

View File

@ -0,0 +1,10 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats =>
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat3[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent]("type")
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait ExecCommandFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val ExecCommandFormat: JsonFormat[sbt.protocol.ExecCommand] = new JsonFormat[sbt.protocol.ExecCommand] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ExecCommand = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val commandLine = unbuilder.readField[String]("commandLine")
val execId = unbuilder.readField[Option[String]]("execId")
unbuilder.endObject()
sbt.protocol.ExecCommand(commandLine, execId)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.ExecCommand, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("commandLine", obj.commandLine)
builder.addField("execId", obj.execId)
builder.endObject()
}
}
}

View File

@ -0,0 +1,33 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait ExecStatusEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val ExecStatusEventFormat: JsonFormat[sbt.protocol.ExecStatusEvent] = new JsonFormat[sbt.protocol.ExecStatusEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ExecStatusEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val status = unbuilder.readField[String]("status")
val channelName = unbuilder.readField[Option[String]]("channelName")
val execId = unbuilder.readField[Option[String]]("execId")
val commandQueue = unbuilder.readField[Vector[String]]("commandQueue")
unbuilder.endObject()
sbt.protocol.ExecStatusEvent(status, channelName, execId, commandQueue)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.ExecStatusEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("status", obj.status)
builder.addField("channelName", obj.channelName)
builder.addField("execId", obj.execId)
builder.addField("commandQueue", obj.commandQueue)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait ExectionEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val ExectionEventFormat: JsonFormat[sbt.protocol.ExectionEvent] = new JsonFormat[sbt.protocol.ExectionEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ExectionEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val success = unbuilder.readField[String]("success")
val commandLine = unbuilder.readField[String]("commandLine")
unbuilder.endObject()
sbt.protocol.ExectionEvent(success, commandLine)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.ExectionEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("success", obj.success)
builder.addField("commandLine", obj.commandLine)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait ExecutionEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val ExecutionEventFormat: JsonFormat[sbt.protocol.ExecutionEvent] = new JsonFormat[sbt.protocol.ExecutionEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ExecutionEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val success = unbuilder.readField[String]("success")
val commandLine = unbuilder.readField[String]("commandLine")
unbuilder.endObject()
sbt.protocol.ExecutionEvent(success, commandLine)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.ExecutionEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("success", obj.success)
builder.addField("commandLine", obj.commandLine)
builder.endObject()
}
}
}

View File

@ -0,0 +1,15 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.protocol.codec.ExecCommandFormats
with sbt.protocol.codec.CommandMessageFormats
with sbt.protocol.codec.ChannelAcceptedEventFormats
with sbt.protocol.codec.LogEventFormats
with sbt.protocol.codec.ExecStatusEventFormats
with sbt.protocol.codec.EventMessageFormats
with sbt.protocol.codec.ExecutionEventFormats
object JsonProtocol extends JsonProtocol

View File

@ -0,0 +1,29 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait LogEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val LogEventFormat: JsonFormat[sbt.protocol.LogEvent] = new JsonFormat[sbt.protocol.LogEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.LogEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val level = unbuilder.readField[String]("level")
val message = unbuilder.readField[String]("message")
unbuilder.endObject()
sbt.protocol.LogEvent(level, message)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.LogEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("level", obj.level)
builder.addField("message", obj.message)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait StatusEventFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val StatusEventFormat: JsonFormat[sbt.protocol.StatusEvent] = new JsonFormat[sbt.protocol.StatusEvent] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.StatusEvent = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val status = unbuilder.readField[String]("status")
val commandQueue = unbuilder.readField[Vector[String]]("commandQueue")
unbuilder.endObject()
sbt.protocol.StatusEvent(status, commandQueue)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.StatusEvent, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("status", obj.status)
builder.addField("commandQueue", obj.commandQueue)
builder.endObject()
}
}
}

View File

@ -0,0 +1,47 @@
package sbt.protocol
@target(Scala)
@codecPackage("sbt.protocol.codec")
@fullCodec("JsonProtocol")
## Message to invoke command.
interface CommandMessage {
}
## Command to execute sbt command.
type ExecCommand implements CommandMessage {
commandLine: String!
execId: String @since("0.0.1")
}
## Message for events.
interface EventMessage {
}
type ChannelAcceptedEvent implements EventMessage {
channelName: String!
}
## Log event.
type LogEvent implements EventMessage {
level: String!
message: String!
}
## Status event.
type ExecStatusEvent implements EventMessage {
status: String!
channelName: String
execId: String
commandQueue: [String]
}
# enum Status {
# Ready
# Processing
# }
## Execution event.
type ExecutionEvent {
success: String!
commandLine: String!
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2016 Lightbend Inc. <http://www.typesafe.com>
*/
package sbt
package protocol
import sjsonnew.support.scalajson.unsafe.{ Converter, CompactPrinter }
import scala.json.ast.unsafe.JValue
import sjsonnew.support.scalajson.unsafe.Parser
import java.nio.ByteBuffer
import scala.util.{ Success, Failure }
object Serialization {
def serializeCommand(command: CommandMessage): Array[Byte] =
{
import codec.JsonProtocol._
val json: JValue = Converter.toJson[CommandMessage](command).get
CompactPrinter(json).getBytes("UTF-8")
}
def serializeEvent(event: EventMessage): Array[Byte] =
{
import codec.JsonProtocol._
val json: JValue = Converter.toJson[EventMessage](event).get
CompactPrinter(json).getBytes("UTF-8")
}
/**
* @return A command or an invalid input description
*/
def deserializeCommand(bytes: Seq[Byte]): Either[String, CommandMessage] =
{
val buffer = ByteBuffer.wrap(bytes.toArray)
Parser.parseFromByteBuffer(buffer) match {
case Success(json) =>
import codec.JsonProtocol._
Converter.fromJson[CommandMessage](json) match {
case Success(command) => Right(command)
case Failure(e) => Left(e.getMessage)
}
case Failure(e) =>
Left(s"Parse error: ${e.getMessage}")
}
}
/**
* @return A command or an invalid input description
*/
def deserializeEvent(bytes: Seq[Byte]): Either[String, EventMessage] =
{
val buffer = ByteBuffer.wrap(bytes.toArray)
Parser.parseFromByteBuffer(buffer) match {
case Success(json) =>
import codec.JsonProtocol._
Converter.fromJson[EventMessage](json) match {
case Success(event) => Right(event)
case Failure(e) => Left(e.getMessage)
}
case Failure(e) =>
Left(s"Parse error: ${e.getMessage}")
}
}
}

8
server.md Normal file
View File

@ -0,0 +1,8 @@
### ExecCommand
```json
{ "type": "ExecCommand", "commandLine": "compile" }
```