Update to Contraband

This commit is contained in:
Eugene Yokota 2016-12-01 03:14:07 -05:00
parent 274c8ec65f
commit 272e733b87
21 changed files with 418 additions and 84 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

@ -2,6 +2,7 @@ package sbt
package internal
import sbt.internal.server._
import sbt.protocol._
import BasicKeys._
private[sbt] final class NetworkChannel extends CommandChannel {
@ -13,11 +14,10 @@ private[sbt] final class NetworkChannel extends CommandChannel {
case Some(x) => x
case None => 5001
}
def onCommand(command: internal.server.Command): Unit = {
def onCommand(command: CommandMessage): Unit =
command match {
case Execution(cmd) => append(Exec(CommandSource.Network, cmd))
case x: ExecCommand => append(Exec(CommandSource.Network, x.commandLine))
}
}
server match {
case Some(x) => // do nothing
case _ =>
@ -36,8 +36,8 @@ private[sbt] final class NetworkChannel extends CommandChannel {
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))
if (cmdStatus.canEnter) StatusEvent("Ready", Vector())
else StatusEvent("Processing", cmdStatus.state.remainingCommands.toVector)
))
}
}

View File

@ -7,6 +7,7 @@ package server
import java.net.{ SocketTimeoutException, Socket }
import java.util.concurrent.atomic.AtomicBoolean
import sbt.protocol._
abstract class ClientConnection(connection: Socket) {
@ -59,7 +60,7 @@ abstract class ClientConnection(connection: Socket) {
out.flush()
}
def onCommand(command: Command): Unit
def onCommand(command: CommandMessage): Unit
def shutdown(): Unit = {
println("Shutting down client connection")

View File

@ -5,76 +5,37 @@ 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 }
import sbt.protocol._
object Serialization {
def serialize(event: Event): Array[Byte] =
def serialize(event: EventMessage): Array[Byte] =
{
import ServerCodec._
val msg = toMessage(event)
val json: JValue = Converter.toJson[EventMessage](msg).get
import codec.JsonProtocol._
val json: JValue = Converter.toJson[EventMessage](event).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] =
def deserialize(bytes: Seq[Byte]): Either[String, CommandMessage] =
{
val buffer = ByteBuffer.wrap(bytes.toArray)
Parser.parseFromByteBuffer(buffer) match {
case Success(json) =>
import ServerCodec._
import codec.JsonProtocol._
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 Success(command) => Right(command)
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

@ -8,15 +8,16 @@ package server
import java.net.{ SocketTimeoutException, InetAddress, ServerSocket, SocketException }
import java.util.concurrent.atomic.AtomicBoolean
import sbt.util.Logger
import sbt.protocol._
import scala.collection.mutable
private[sbt] sealed trait ServerInstance {
def shutdown(): Unit
def publish(event: Event): Unit
def publish(event: EventMessage): Unit
}
private[sbt] object Server {
def start(host: String, port: Int, onIncommingCommand: Command => Unit, log: Logger): ServerInstance =
def start(host: String, port: Int, onIncommingCommand: CommandMessage => Unit, log: Logger): ServerInstance =
new ServerInstance {
val lock = new AnyRef {}
@ -36,7 +37,7 @@ private[sbt] object Server {
log.info(s"new client connected from: ${socket.getPort}")
val connection = new ClientConnection(socket) {
override def onCommand(command: Command): Unit = {
override def onCommand(command: CommandMessage): Unit = {
onIncommingCommand(command)
}
}
@ -55,7 +56,7 @@ private[sbt] object Server {
serverThread.start()
/** Publish an event to all connected clients */
def publish(event: Event): Unit = {
def publish(event: EventMessage): Unit = {
// TODO do not do this on the calling thread
val bytes = Serialization.serialize(event)
lock.synchronized {

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

@ -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,33 @@
/**
* 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) extends sbt.protocol.CommandMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: ExecCommand => (this.commandLine == x.commandLine)
case _ => false
}
override def hashCode: Int = {
37 * (17 + commandLine.##)
}
override def toString: String = {
"ExecCommand(" + commandLine + ")"
}
protected[this] def copy(commandLine: String = commandLine): ExecCommand = {
new ExecCommand(commandLine)
}
def withCommandLine(commandLine: String): ExecCommand = {
copy(commandLine = commandLine)
}
}
object ExecCommand {
def apply(commandLine: String): ExecCommand = new ExecCommand(commandLine)
}

View File

@ -0,0 +1,37 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
/** Executon event. */
final class ExectionEvent private (
val success: String,
val commandLine: String) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: ExectionEvent => (this.success == x.success) && (this.commandLine == x.commandLine)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + success.##) + commandLine.##)
}
override def toString: String = {
"ExectionEvent(" + success + ", " + commandLine + ")"
}
protected[this] def copy(success: String = success, commandLine: String = commandLine): ExectionEvent = {
new ExectionEvent(success, commandLine)
}
def withSuccess(success: String): ExectionEvent = {
copy(success = success)
}
def withCommandLine(commandLine: String): ExectionEvent = {
copy(commandLine = commandLine)
}
}
object ExectionEvent {
def apply(success: String, commandLine: String): ExectionEvent = new ExectionEvent(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,37 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
/** Status event. */
final class StatusEvent private (
val status: String,
val commandQueue: Vector[String]) extends sbt.protocol.EventMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: StatusEvent => (this.status == x.status) && (this.commandQueue == x.commandQueue)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + status.##) + commandQueue.##)
}
override def toString: String = {
"StatusEvent(" + status + ", " + commandQueue + ")"
}
protected[this] def copy(status: String = status, commandQueue: Vector[String] = commandQueue): StatusEvent = {
new StatusEvent(status, commandQueue)
}
def withStatus(status: String): StatusEvent = {
copy(status = status)
}
def withCommandQueue(commandQueue: Vector[String]): StatusEvent = {
copy(commandQueue = commandQueue)
}
}
object StatusEvent {
def apply(status: String, commandQueue: Vector[String]): StatusEvent = new StatusEvent(status, commandQueue)
}

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,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.LogEventFormats with sbt.protocol.codec.StatusEventFormats =>
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat2[sbt.protocol.EventMessage, sbt.protocol.LogEvent, sbt.protocol.StatusEvent]("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 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")
unbuilder.endObject()
sbt.protocol.ExecCommand(commandLine)
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.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,8 @@
/**
* 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.LogEventFormats with sbt.protocol.codec.StatusEventFormats with sbt.protocol.codec.EventMessageFormats
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,40 @@
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!
}
## Message for events.
interface EventMessage {
}
## Log event.
type LogEvent implements EventMessage {
level: String!
message: String!
}
## Status event.
type StatusEvent implements EventMessage {
status: String!
commandQueue: [String]
}
# enum Status {
# Ready
# Processing
# }
## Executon event.
type ExectionEvent {
success: String!
commandLine: String!
}

8
server.md Normal file
View File

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