Giter VIP home page Giter VIP logo

autowire's Introduction

Autowire 0.3.3

Scala.js

Autowire is a pair of macros that allows you to perform type-safe, reflection-free RPC between Scala systems. Autowire allows you to write type-safe Ajax/RPC calls that look like:

// shared interface
trait Api{
  def add(x: Int, y: Int, z: Int): Int 
}

// server-side router and implementation 
object Server extends autowire.Server...
object ApiImpl extends Api
  def add(x: Int, y: Int, z: Int) = x + y + z 
}

// client-side callsite

import autowire._ // needed for correct macro application

object Client extends autowire.Client...
Client[Api].add(1, 2, 3).call(): Future[Int]
//         |    |             |
//         |    |             The T is pickled and wrapped in a Future[T]
//         |    The arguments to that method are pickled automatically
//         Call a method on the `Api` trait

This provides a range of nice properties: under-the-hood the macro converts your method calls into RPCs, together with the relevant serialization code, but you do not need to deal with that yourself. Furthermore, since the RPCs appear to be method calls, tools like IDEs are able to work with them, e.g. doing project-wide renames or analysis (jump-to-definition, find-usages, etc.).

Most importantly, the compiler is able to typecheck these RPCs, and ensure that you are always calling them with the correct arguments, and handling the return-value correctly in turn. This removes an entire class of errors.

Autowire is completely agnostic to both the serialization library, and the transport-mechanism.

Getting Started

Autowire is available at the following maven coordinates:

"com.lihaoyi" %% "autowire" % "0.3.3" // for Scala-JVM
"com.lihaoyi" %%% "autowire" % "0.3.3" // Scala.js (1.4.0+) and Scala Native (0.4.0)

This 0.3.3 can be used by publishing locally:

git clone https://github.com/lihaoyi/autowire.git
cd autowire
./mill __.publishLocal

And then add a library dependency in your build.sbt for your shared project:

libraryDependencies ++= Seq(
  "com.lihaoyi" %%% "autowire" % "0.3.3",
  ...
)

Autowire 0.3.3 works for Scala.js 1.x and both Scala 2.12 and 2.13 For Scala.js 0.6.x users, please use autowire 0.2.6.

Autowire works on both Scala-JVM and Scala-JS, meaning you can use it to get type-safe Ajax calls between a browser and your servers.

Minimal Example

Here is a minimal example of Autowire in action, using uPickle to serialize the transferred data:

import autowire._
import upickle._

// shared API interface 
trait MyApi{
  def doThing(i: Int, s: String): Seq[String]
}

// server-side implementation, and router 
object MyApiImpl extends MyApi{
  def doThing(i: Int, s: String) = Seq.fill(i)(s)
}
object MyServer extends autowire.Server[String, upickle.Reader, upickle.Writer]{
  def write[Result: Writer](r: Result) = upickle.write(r)
  def read[Result: Reader](p: String) = upickle.read[Result](p)
  
  val routes = MyServer.route[MyApi](MyApiImpl)
}

// client-side implementation, and call-site
object MyClient extends autowire.Client[String, upickle.Reader, upickle.Writer]{
  def write[Result: Writer](r: Result) = upickle.write(r)
  def read[Result: Reader](p: String) = upickle.read[Result](p)

  override def doCall(req: Request) = {
    println(req)
    MyServer.routes.apply(req)
  }
}

MyClient[MyApi].doThing(3, "lol").call().foreach(println)
// List(lol, lol, lol)

In this example, we have a shared MyApi trait, which contains a trait/interface which is shared by both client and server. The server contains an implementation of this trait, while the client can make calls against the trait using the route macro provided by MyServer.

Although in this example everything is happening locally, this example goes through the entire serialization/de-serialization process when transferring the arguments/return-value. Thus, it is trivial to replace the direct call to MyServer.routes to a remote call over HTTP or TCP or some other transport with MyClient and MyServer/MyApiImpl living on different machines or even running on different platforms (e.g. Scala-JS/Scala-JVM).

Here's a longer example, which takes advantage of autowire's cross-platform-ness to write an interactive client-server web-app.

Note that:

  • The client proxy, MyClient[MyApi] in this example, must be explicitly written out, and followed by .call() at each site where a remote call is made. That is, you cannot assign MyClient[MyApi] to a variable and use that instead.
  • The methods in the autowired API can also be asynchronous, in which case they return Future[T]. So the example above could be modeled async as def doThing(i: Int, s: String): Future[Seq[String]].

Reference

This section goes deeper into the steps necessary to use Autowire, and should cover extension points not obvious from the above example.

To begin with, the developer has to implement two interfaces, Client:

trait Client[PickleType, Reader[_], Writer[_]] {
  type Request = Core.Request[PickleType]
  def read[Result: Reader](p: PickleType): Result
  def write[Result: Writer](r: Result): PickleType
  
  /**
   * A method for you to override, that actually performs the heavy
   * lifting to transmit the marshalled function call from the [[Client]]
   * all the way to the [[Core.Router]]
   */
  def doCall(req: Request): Future[PickleType]
}

And Server

trait Server[PickleType, Reader[_], Writer[_]] {
  type Request = Core.Request[PickleType]
  type Router = Core.Router[PickleType]
  
  def read[Result: Reader](p: PickleType): Result
  def write[Result: Writer](r: Result): PickleType

  /**
   * A macro that generates a `Router` PartialFunction which will dispatch incoming
   * [[Requests]] to the relevant method on [[Trait]]
   */
  def route[Trait](target: Trait): Router = macro Macros.routeMacro[Trait, PickleType]
}

Both Client and Server are flexible: you get to specify multiple type parameters:

  • PickleType: or what type you want to serialize your arguments/return-value into. Typically something like String, Array[Byte], ByteString, etc.
  • Reader[_]: a context-bound used to convert data from PickleType to a desired type T. Typically something like upickle.Reader, play.api.libs.json.Reads, etc.
  • Writer[_]: a context-bound used to convert data to PickleType from an original type T. Typically something like upickle.Writer, play.api.libs.json.Writes, etc.

If the serialization library you're using doesn't need Reader[_] or Writer[_] context-bound, you can use autowire.Bounds.None as the context-bound, and it will be filled in automatically. You then need to override the read and write methods of both sides, in order to tell Autowire how to serialize and deserialize your typed values.

Lastly, you need to wire together client and server to get them talking to one another: this is done by overriding Client.doCall, which takes the Request object holding the serialized arguments and returns a Future containing the serialized response, and by using Server.route, which generates a Router PartialFunction which you can then feed Request object into to receive a serialized response in return. These types are defined as

object Core {
  /**
   * The type returned by the [[Server.route]] macro; aliased for
   * convenience, but it's really just a normal `PartialFunction`
   * and can be combined/queried/treated like any other.
   */
  type Router[PickleType] = PartialFunction[Request[PickleType], Future[PickleType]]

  /**
   * A marshalled autowire'd function call.
   *
   * @param path A series of path segments which illustrate which method
   *             to call, typically the fully qualified path of the
   *             enclosing trait followed by the name of the method
   * @param args Serialized arguments for the method that was called. Kept
   *             as a Map of arg-name -> serialized value. Values which
   *             exactly match the default value are omitted, and are
   *             simply re-constituted by the receiver.
   */
  case class Request[PickleType](path: Seq[String], args: Map[String, PickleType])
}

is outside the scope of Autowire, as is how you serialize/deserialize the arguments/return-value using the read and write functions.

In short,

  • {Client, Server}.{read, write} is your chance to substitute in whatever serialization library you want. Any library should work, as long as you can put the serialization code into the read and write functions to serialize an object of type T. Autowire works with any transport and any serialization library, including Java-serialization, Kryo and Play-Json, with unit tests to ensure that they are working.
  • Client.doCall and Server.route is your chance to choose whatever transport mechanism you want to use. By that point, the arguments/return-value have already been mostly serialized, with only a small amount of structure (e.g. the map of argument-names to serialized-values) left behind. Exactly how you get the Request object from the Client to the Server (HTTP, TCP, etc.) and the response-data back is up to you as long as you can give Autowire a Future[PickleType] in exchange for its Request.

If you still don't fully understand, and need help getting something working, take a look at the complete example.

Error Handling

Assuming that your chosen serialization library behaves as intended, Autowire only throws exceptions when de-serializing data. This could come from many sources: data corrupted in transit, external API users making mistakes, malicious users trying to pen-test your API.

Autowire's de-serialization behavior is documented in the errors it throws:

package autowire

trait Error extends Exception
object Error{
  /**
   * Signifies that something went wrong when de-serializing the
   * raw input into structured data.
   *
   * This can contain multiple exceptions, one for each parameter.
   */
  case class InvalidInput(exs: Param*) extends Exception with Error
  sealed trait Param
  object Param{

    /**
     * Some parameter was missing from the input.
     */
    case class Missing(param: String) extends Param

    /**
     * Something went wrong trying to de-serialize the input parameter;
     * the thrown exception is stored in [[ex]]
     */
    case class Invalid(param: String, ex: Throwable) extends Param
  }
}

These errors are not intended to be end-user-facing. Rather, as far as possible they are maintained as structured data, and it us up to the developer using Autowire to decide how to respond (error page, logging, etc).

Autowire only provides custom error handling on the server side, since there are multiple arguments to validate/aggregate. If something goes wrong on the client during de-serialization, you will get the exception thrown from whichever serialization library you're using.

Autowire supports interface methods with default values. When a RPC call's arguments are left out in favor of defaults, Autowire omits these arguments entirely from the pickled request, and evaluates these arguments on the server when it finds them missing from the request instead of throwing an Error.Param.Missing like it would if that particular parameter did not have default.

Why Autowire

The motivation for Autowire is the fact that Ajax requests between client-side Javascript and server-side something-else has historically been a point of fragility and boilerplate, with problems such as:

  • Being stringly-typed and impossibile to statically analyze
  • Prone to getting broken due to typos and incomplete refactoring
  • Needing reams of boilerplate to pickle/unpickle data structures on both ends
  • Using callbacks all over the place, confusing future developers

Autowire aims to solve all these problems:

  • Ajax calls using Autowire are statically checked just like the rest of your program. For example, typos are immediately spotted by the compiler:
Client[Api].fastOp(editor.code).call()

[error] /Client.scala:104: value fastOp is not a member of fiddle.Api
[error]     Client[Api].fastOp(editor.code).call()
[error]                 ^
  • Similarly, since Autowire RPC calls are (at least superficially) just method calls, things like find-usages, rename-all, and other such tools all work flawlessly across these calls.

  • Autowire deals directly with Futures; this means that it plays nicely with the rest of the Scala ecosystem, including tools such as async and await, while making it much easier for future developers to reason about than callback soup.

// endpoint definition
def completeStuff(txt: String, flag: String, offset: Int): List[(String, String)]

// somewhere else inside an `async` block
val res: List[(String, String)] = {
  await(Client[Api].completeStuff(code, flag, intOffset).call())
}
  • Also shown in the example above, serialization and deserialization of data structures is handled implicitly. Thus you can call functions which return non-trivial structures and receive the structured data directly, without having to fiddle with JSON or other ad-hoc encodings yourself.

Limitations

Autowire can only serialize and deserialize things that the chosen serialization library can. For example, if you choose to go with uPickle, this means most of the immutable data structures in the standard library and case classes, but circular data structures aren't supported, and arbitrary object graphs don't work.

Autowire does not support method overloading and type parameters on the interfaces/traits used for making the RPCs.

Apart from that, Autowire is a pretty thin layer on top of any existing serialization library and transport layer, and does not project much functionality apart from routing. It is up to the developer using Autowire to decide how he wants to transport the serialized data back and forth, how he wants to respond to errors, etc.

To see a simple example involving a ScalaJS client and Spray server, check out this example:

https://github.com/lihaoyi/workbench-example-app/tree/autowire

Changelog

0.3.3

  • Update to Scala Native 0.4.0

0.3.2

  • Cross-publish for Scala 2.12 and 2.13

  • Update to Scala-js 1.0.0+

  • Fix deprecated references

0.2.6

  • .call()s now wrap the endpoint using Future() instead of Future.successful, which means exceptions now get caught and propagated inside the Future rather than blowing up the call stack

  • Cross-publish for Scala 2.12.0

autowire's People

Contributors

asakaev avatar benhutchison avatar benjaminjackman avatar clhodapp avatar cornerman avatar jhegedus42 avatar lihaoyi avatar lolgab avatar marcgrue avatar mathieuleclaire avatar nafg avatar olivierblanvillain avatar tindzk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

autowire's Issues

How to support server-side injection of data into API calls?

Sorry if this has been addressed before (tried a few searches without luck):

I haven't been able to think of a good way to inject additional information into an autowired API call on the server side. Traditionally, we might use an additional parameter to our API function ... but the fact that, on the server side, the API service is being abstractly handled as follows:

AutowireServer.route[Api](service)(autowire.Core.Request(url, body))

combined with the fact that the additional parameter wouldn't exist on the client, seems to make this impossible or non-obvious.

Example use case: running an API command that has metadata as a parameter - the server needs to fill in the remote ip address as a field in the metadata, since the client can't be trusted to do this.

The addition of dependency to autowire causing problem with compilation

adding dependency "com.lihaoyi" %% "autowire" % "0.2.3" make compilation fails with error:

object creation impossible, since method PerRunReporting in trait Reporting of type => this.PerRunReporting is not defined
[error] val g = new scala.tools.nsc.Global(settings,reporter){
[error] ^

scala version: 2.11.1

The last version of autowire which works for me is 0.1.2

do you have any temporary workaround I can use?

Can't Use API Factory Functions for Namespacing Purposes

In a similar vein as #7, I wish to have functions (or values) on my auto-wired API that serve as namespaces for more specific functionality. The following variant on the Minimal Example illustrates the intent:

import autowire._
import upickle._

trait MySpecificApi {
  def doThing(i: Int, s: String): Seq[String]
}

object MySpecificApiImpl extends MySpecificApi {
  def doThing(i: Int, s: String) = Seq.fill(i)(s)
}

trait MyApi {
  def mySpecificApi: MySpecificApi
}

object MyApiImpl extends MyApi {
  override val mySpecificApi: MySpecificApi = MySpecificApiImpl
}

object MyServer extends autowire.Server[String, upickle.Reader, upickle.Writer]{
  def write[Result: Writer](r: Result) = upickle.write(r)
  def read[Result: Reader](p: String) = upickle.read[Result](p)

  val routes = MyServer.route[MyApi](MyApiImpl)
}

object MyClient extends autowire.Client[String, upickle.Reader, upickle.Writer]{
  def write[Result: Writer](r: Result) = upickle.write(r)
  def read[Result: Reader](p: String) = upickle.read[Result](p)

  override def doCall(req: Request) = {
    println(req)
    MyServer.routes.apply(req)
  }
}

MyClient[MyApi].mySpecificApi.doThing(3, "lol").call().foreach(println)

This fails to compile with an error similar to:

Error:(27, 38) A$A36.this.MySpecificApi does not take parameters
  val routes = MyServer.route[MyApi](MyApiImpl)
                                    ^

Is this supported, andโ€”in any caseโ€”is there any other preferred approach?

Possible issue using ClassTag in autowire calls

I had a working API that looked like

final case class OneShot(id: CommandId, body: String, meta: SysCmdMetaData)
extends Command[SysCmdMetaData]

def exec(data: List[OneShot]): Future[List[RunResult]]

with the js implementation of exec being:

  def exec(cmds: List[OneShot]): Rx[List[RunResult]] = {
    println("Verifying outer exec is called")
    AutowireClient[Api].exec(cmds).call().toRx.map {
      case Some(attempt) => attempt match {
        case Success(output) => output
        case Failure(err) =>
          List(RunResult.fakeResult(s"Error: ${err.getCause}!", NONFATAL_APP_ERR))
      }
      case None => List(RunResult.fakeResult(""))
    }
  }

In an attempt to generify things a bit, I changed the API's exec signature to:

  def exec[M <: CommandMetaData : ClassTag, C <: Command[M] : ClassTag]
  (data: List[C]): Future[List[RunResult]]

and the implementation to:

  def exec[M <: CommandMetaData : ClassTag, C <: Command[M] : ClassTag]
  (cmds: List[C]): Rx[List[RunResult]] = {
    println("Verifying outer exec is called")
    AutowireClient[Api].exec[M, C](cmds).call().toRx.map {
      case Some(attempt) => attempt match {
        case Success(output) => output
        case Failure(err) =>
          List(RunResult.fakeResult(s"Error: ${err.getCause}!", NONFATAL_APP_ERR))
      }
      case None => List(RunResult.fakeResult(""))
    }
  }

This results in the compile error:

You can't call the .call() method on autowire.`package`.unwrapClientProxy[model.Api, java.nio.ByteBuffer, boopickle.Default.Pickler, boopickle.Default.Pickler](client.AutowireClient.apply[model.Api]).exec[M, C](cmds)(evidence$1, evidence$2), only on autowired function calls.
[error]     AutowireClient[Api].exec[M, C](cmds).call().toRx.map {```

Custom header

Hi as I may need to use OAuth facilities I would like to ask if I could add some custom headers to the request from client side or not.

Hello Autowire

Hi,
very impressive work (again !) ! Do you have a simple example of autowire (meaning simpler than js-fiddle) to understand well all the concepts ?

Thanks
Mathieu

Router generation fails if API has a function with no parentheses

Minified example:

trait CustomerAPI {
  def load: Seq[Customer]
}

val customerAPI = new CustomerAPIService
val router = Router.route[CustomerAPI](customerAPI)

This does't compile and gives a "weird" error:

not enough arguments for method apply: (idx: Int)admin.shared.Customer in trait SeqLike.
[error] Unspecified value parameter idx.
[error]   val router = Router.route[CustomerAPI](customerAPI)
[error]                                         ^

It seems to somehow go into the trait and get the load function, for which you would have to call apply with an index.

Adding parentheses to the load function fixes the problem.

trait CustomerAPI {
  def load(): Seq[Customer]
}

File transfer

Hi,
how can I do to deal with File transfer with autowire (download and updload) ? What do you recommand ?
Thanks

diverging implicit expansion errors with Spray, Autowire and uPickle

Hi there, I'm facing compile errors on the server-side while trying to integrate Autowire within my existing Spray code that uses uPickle for serialization.
Here's my UPickleJsonSupport trait mixed into my Spray HttpService:

trait UPickleJsonSupport {
    implicit def genericMarshaller[T: Writer]: Marshaller[T] =
        Marshaller.delegate(ContentTypes.`application/json`)(writeJs(_))

    implicit def jsValueMarshaller: Marshaller[Js.Value] =
        Marshaller.of[Js.Value](ContentTypes.`application/json`) {
            (value, contentType, context) => {
                val entity = HttpEntity(contentType, json.write(value))

                context.marshalTo(entity)
            }
        }

    implicit def entityUnmarshaller[T: Reader]: Unmarshaller[T] =
        Unmarshaller[T](ContentTypeRange(MediaTypes.`application/json`)) {
            case entity => read[T](entity.asString(HttpCharsets.`UTF-8`))
        }
}

Ok, this is for the integration with Spray marshalling and this works fine until I throw Autowire into the mix:
Here's my AutowireServer:

object AutowireServer extends autowire.Server[Js.Value, Reader, Writer] {
    def read[Result: Reader](p: Js.Value) = upickle.default.readJs[Result](p)
    def write[Result: Writer](r: Result) = upickle.default.writeJs(r)
}

And my Spray route definition (rest of the code omitted for brevity):

    def apiRoute = post {
        path("api" / Segments) { s =>
            extract(_.request.entity.asString) { e =>
                complete {
                    AutowireServer.route[Api](ApiImpl)(
                        autowire.Core.Request(
                            s,
                            upickle.json.read(e).asInstanceOf[Js.Obj].value.toMap
                        )
                    )
                }
            }
        }
    }

With this code, I've got a sh*tload of compile errors (all related to this problem), ending with these:

...
[error]  both value derive$macro$173 of type => upickle.default.Writer[upickle.Js.Value]
[error]  and value derive$macro$175 of type => upickle.default.Writer[upickle.Js.Value]
[error]  match expected type upickle.default.Writer[upickle.Js.Value]
[error] /Users/toto/someproject/jvm/src/main/scala/some/pkg/RouteProvider.scala:38: diverging implicit expansion for type upickle.default.Writer[T1]
[error] starting with macro method macroW in trait LowPriX
[error]           AutowireServer.route[Api](ApiImpl)(
[error]                                             ^
[error] 13 errors found

My current knowledge of scala (which is rather poor) does not allow me to find the root cause of the problem. Looks like a clash between implicits generated by the macros ???
Now, the problem may come from uPickle rather than Autowire but since uPickle was working before I added Autowire, I create the ticket here ;)

In the meantime, I will simply generate Spray's HttpEntity myself (thus skipping the Marshaller) by adding this transformation on the router result .map(v => HttpEntity(jsonContentType, upickle.json.write(v))) but I guess (and hope) it should be possible to combine both libs.

Thanks for your help.

Support type parameters in autowire.Server

Goal would be to having something like this work:

object Router {
 def route[T](apiImpl: T, path: String, data: Array[Byte]): Future[_] = {
   AutowireRouter.route[T](apiImpl)(
    autowire.Core.Request(path.split("/"),
    Unpickle[Map[String, ByteBuffer]].fromBytes(ByteBuffer.wrap(data)))
  )
 }
}

According to @lihaoyi this will take a major refactoring of the macros

route does not compile for some complicated return value

import scala.concurrent.ExecutionContext.Implicits._

import upickle.default._
import upickle._
import autowire._

final case class NestedData(field1: Map[String, String], field2: Double)

sealed trait Result

final case class Result1(reason: String, suggestions: Seq[NestedData]) extends Result
final case class Result2() extends Result


trait MyApi {

  def rpcMethod(): Result

}

object MyServer extends autowire.Server[String, upickle.default.Reader, upickle.default.Writer]{
  def write[Result: Writer](r: Result) = upickle.default.write(r)
  def read[Result: Reader](p: String) = upickle.default.read[Result](p)

  val routes = route (??? : MyApi)
}
libraryDependencies += "com.lihaoyi" %% "autowire" % "0.2.6"

libraryDependencies += "com.lihaoyi" %% "upickle" % "0.4.3"
MyApi.scala:25: diverging implicit expansion for type upickle.default.Writer[T1]
starting with macro method macroW in trait LowPriX
  val routes = route (??? : MyApi)
                     ^
one error found

Nested traits

Hi

I'm not sure if I should classify this as general advice, a bug or a feature request, but here goes:

I've just tried creating an API of nested traits. The reason I wanted this is to avoid having multiple routers on the server side and multiple clients on the client side while still "namespacing" the API and therefore avoid putting all methods in the same trait. I figure it's less messy. Anyway, here's some example code:

trait Calculator {
  def add(a: Int, b: Int): Int
  def slowAdd(a: Int, b: Int): Future[Int]

  trait Multiplication {
    def times(a: Int, b: Int): Int
    def slowTimes(a: Int, b: Int): Future[Int]
  }

  def multiplication: Multiplication
}

object CalculatorImpl extends api.Calculator {
  def add(a: Int, b: Int): Int = a + b
  def slowAdd(a: Int, b: Int): Future[Int] = Future.successful(a + b)

  object multiplication extends Multiplication {
    def slowTimes(a: Int, b: Int): Future[Int] = Future.successful(a * b)
    def times(a: Int, b: Int): Int = a * b
  }
}

This fails compiling:

[error] /home/alen/projects/skim/jvm/app/controllers/Api.scala:22: api.CalculatorImpl.multiplication.type does not take parameters
[error]     val calculator = Server.route[Calculator](CalculatorImpl)

So, the questions here are:

  1. Should I not bother and just have multiple clients for each distinct API?
  2. What is the parameter that is trying to get passed?
  3. Is this a potential feature in the future?
  4. Is this a bug?

Cheers, Alen

Serialization library

Hi,
I really need to serialize Classes not depending to a sealed trait. Upickle, unfortunately, cannot do this. Is it planned to implement this in upickle or is there an alternative library achieving this working in autowire ?

Thanks
Mathieu

Autowire 0.1.4: HTTP ERROR 500

Hi,
after upgrading successfully upickle to 2.1 (https://github.com/mathieuleclaire/scalaTraJSTagsWireRx), I tried upgrading autowire to 0.1.4, and after modifying the minor API changes, my application fails when I launch web page with that message :

HTTP ERROR 500

Problem accessing /. Reason:

    scala.tools.nsc.Global$gen$.mkBlock(Lscala/collection/immutable/List;)Lscala/reflect/internal/Trees$Tree;

Caused by:

org.fusesource.scalate.TemplateException: scala.tools.nsc.Global$gen$.mkBlock(Lscala/collection/immutable/List;)Lscala/reflect/internal/Trees$Tree;
    at org.fusesource.scalate.TemplateEngine.compileAndLoad(TemplateEngine.scala:732)
    at org.fusesource.scalate.TemplateEngine.compileAndLoadEntry(TemplateEngine.scala:699)
    at org.fusesource.scalate.TemplateEngine.liftedTree1$1(TemplateEngine.scala:419)
    at org.fusesource.scalate.TemplateEngine.load(TemplateEngine.scala:413)
    at org.fusesource.scalate.TemplateEngine.load(TemplateEngine.scala:483)
    at org.scalatra.scalate.ScalateSupport$class.org$scalatra$scalate$ScalateSupport$$renderScalateErrorPage(ScalateSupport.scala:148)
    at org.scalatra.scalate.ScalateSupport$class.renderUncaughtException(ScalateSupport.scala:139)
    at fr.iscpif.app.MyScalatraServlet.renderUncaughtException(MyScalatraServlet.scala:19)
    at org.scalatra.ScalatraBase$$anonfun$executeRoutes$2$$anonfun$apply$5.apply(ScalatraBase.scala:182)
    at org.scalatra.ScalatraBase$$anonfun$executeRoutes$2$$anonfun$apply$5.apply(ScalatraBase.scala:179)
    at org.scalatra.ScalatraBase$class.org$scalatra$ScalatraBase$$cradleHalt(ScalatraBase.scala:206)
    at org.scalatra.ScalatraBase$$anonfun$executeRoutes$2.apply(ScalatraBase.scala:176)
    at org.scalatra.ScalatraBase$$anonfun$executeRoutes$2.apply(ScalatraBase.scala:175)
    at org.scalatra.ScalatraBase$class.org$scalatra$ScalatraBase$$cradleHalt(ScalatraBase.scala:206)
    at org.scalatra.ScalatraBase$class.executeRoutes(ScalatraBase.scala:175)
    at org.scalatra.ScalatraServlet.executeRoutes(ScalatraServlet.scala:49)
    at org.scalatra.ScalatraBase$$anonfun$handle$1.apply$mcV$sp(ScalatraBase.scala:113)
    at org.scalatra.ScalatraBase$$anonfun$handle$1.apply(ScalatraBase.scala:113)
    at org.scalatra.ScalatraBase$$anonfun$handle$1.apply(ScalatraBase.scala:113)
    at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
    at org.scalatra.DynamicScope$class.withResponse(DynamicScope.scala:80)
    at org.scalatra.ScalatraServlet.withResponse(ScalatraServlet.scala:49)
    at org.scalatra.DynamicScope$$anonfun$withRequestResponse$1.apply(DynamicScope.scala:60)
    at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
    at org.scalatra.DynamicScope$class.withRequest(DynamicScope.scala:71)
    at org.scalatra.ScalatraServlet.withRequest(ScalatraServlet.scala:49)
    at org.scalatra.DynamicScope$class.withRequestResponse(DynamicScope.scala:59)
    at org.scalatra.ScalatraServlet.withRequestResponse(ScalatraServlet.scala:49)
    at org.scalatra.ScalatraBase$class.handle(ScalatraBase.scala:111)
    at org.scalatra.ScalatraServlet.org$scalatra$servlet$ServletBase$$super$handle(ScalatraServlet.scala:49)
    at org.scalatra.servlet.ServletBase$class.handle(ServletBase.scala:43)
    at fr.iscpif.app.MyScalatraServlet.org$scalatra$scalate$ScalateSupport$$super$handle(MyScalatraServlet.scala:19)
    at org.scalatra.scalate.ScalateSupport$class.handle(ScalateSupport.scala:130)
    at fr.iscpif.app.MyScalatraServlet.handle(MyScalatraServlet.scala:19)
    at org.scalatra.ScalatraServlet.service(ScalatraServlet.scala:54)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:255)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
    at org.eclipse.jetty.server.Server.handle(Server.java:365)
    at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485)
    at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926)
    at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988)
    at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635)
    at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235)
    at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:628)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:52)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
    at java.lang.Thread.run(Thread.java:744)
Caused by: java.lang.NoSuchMethodError: scala.tools.nsc.Global$gen$.mkBlock(Lscala/collection/immutable/List;)Lscala/reflect/internal/Trees$Tree;
    at scala.tools.nsc.ast.parser.TreeBuilder.makeBlock(TreeBuilder.scala:110)
    at scala.tools.nsc.ast.parser.Parsers$Parser.block(Parsers.scala:1689)
    at scala.tools.nsc.ast.parser.Parsers$Parser.blockExpr(Parsers.scala:1678)
    at scala.tools.nsc.ast.parser.Parsers$Parser.simpleExpr(Parsers.scala:1597)
    at scala.tools.nsc.ast.parser.Parsers$Parser.prefixExpr(Parsers.scala:1565)
    at scala.tools.nsc.ast.parser.Parsers$Parser.postfixExpr(Parsers.scala:1548)
    at scala.tools.nsc.ast.parser.Parsers$Parser.parseOther$1(Parsers.scala:1446)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr0(Parsers.scala:1500)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$expr$1.apply(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$expr$1.apply(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser.withPlaceholders(Parsers.scala:1195)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr(Parsers.scala:1348)
    at scala.tools.nsc.ast.parser.Parsers$Parser.parseIf$1(Parsers.scala:1357)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr0(Parsers.scala:1362)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$expr$1.apply(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$expr$1.apply(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser.withPlaceholders(Parsers.scala:1195)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser.statement(Parsers.scala:1323)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$blockStatSeq$1.apply(Parsers.scala:3110)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$blockStatSeq$1.apply(Parsers.scala:3092)
    at scala.tools.nsc.ast.parser.Parsers$Parser.checkNoEscapingPlaceholders(Parsers.scala:464)
    at scala.tools.nsc.ast.parser.Parsers$Parser.blockStatSeq(Parsers.scala:3092)
    at scala.tools.nsc.ast.parser.Parsers$Parser.block(Parsers.scala:1689)
    at scala.tools.nsc.ast.parser.Parsers$Parser.blockExpr(Parsers.scala:1678)
    at scala.tools.nsc.ast.parser.Parsers$Parser.simpleExpr(Parsers.scala:1597)
    at scala.tools.nsc.ast.parser.Parsers$Parser.prefixExpr(Parsers.scala:1565)
    at scala.tools.nsc.ast.parser.Parsers$Parser.postfixExpr(Parsers.scala:1548)
    at scala.tools.nsc.ast.parser.Parsers$Parser.parseOther$1(Parsers.scala:1446)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr0(Parsers.scala:1500)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$expr$1.apply(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$expr$1.apply(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser.withPlaceholders(Parsers.scala:1195)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser.statement(Parsers.scala:1323)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$blockStatSeq$1.apply(Parsers.scala:3110)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$blockStatSeq$1.apply(Parsers.scala:3092)
    at scala.tools.nsc.ast.parser.Parsers$Parser.checkNoEscapingPlaceholders(Parsers.scala:464)
    at scala.tools.nsc.ast.parser.Parsers$Parser.blockStatSeq(Parsers.scala:3092)
    at scala.tools.nsc.ast.parser.Parsers$Parser.block(Parsers.scala:1689)
    at scala.tools.nsc.ast.parser.Parsers$Parser.blockExpr(Parsers.scala:1678)
    at scala.tools.nsc.ast.parser.Parsers$Parser.simpleExpr(Parsers.scala:1597)
    at scala.tools.nsc.ast.parser.Parsers$Parser.prefixExpr(Parsers.scala:1565)
    at scala.tools.nsc.ast.parser.Parsers$Parser.postfixExpr(Parsers.scala:1548)
    at scala.tools.nsc.ast.parser.Parsers$Parser.parseOther$1(Parsers.scala:1446)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr0(Parsers.scala:1500)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$expr$1.apply(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$expr$1.apply(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser.withPlaceholders(Parsers.scala:1195)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser.statement(Parsers.scala:1323)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$blockStatSeq$1.apply(Parsers.scala:3110)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$blockStatSeq$1.apply(Parsers.scala:3092)
    at scala.tools.nsc.ast.parser.Parsers$Parser.checkNoEscapingPlaceholders(Parsers.scala:464)
    at scala.tools.nsc.ast.parser.Parsers$Parser.blockStatSeq(Parsers.scala:3092)
    at scala.tools.nsc.ast.parser.Parsers$Parser.block(Parsers.scala:1689)
    at scala.tools.nsc.ast.parser.Parsers$Parser.blockExpr(Parsers.scala:1678)
    at scala.tools.nsc.ast.parser.Parsers$Parser.simpleExpr(Parsers.scala:1597)
    at scala.tools.nsc.ast.parser.Parsers$Parser.prefixExpr(Parsers.scala:1565)
    at scala.tools.nsc.ast.parser.Parsers$Parser.postfixExpr(Parsers.scala:1548)
    at scala.tools.nsc.ast.parser.Parsers$Parser.parseOther$1(Parsers.scala:1446)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr0(Parsers.scala:1500)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$expr$1.apply(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$expr$1.apply(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser.withPlaceholders(Parsers.scala:1195)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr(Parsers.scala:1350)
    at scala.tools.nsc.ast.parser.Parsers$Parser.expr(Parsers.scala:1348)
    at scala.tools.nsc.ast.parser.Parsers$Parser.funDefRest(Parsers.scala:2605)
    at scala.tools.nsc.ast.parser.Parsers$Parser.funDefOrDcl(Parsers.scala:2566)
    at scala.tools.nsc.ast.parser.Parsers$Parser.defOrDcl(Parsers.scala:2451)
    at scala.tools.nsc.ast.parser.Parsers$Parser.nonLocalDefOrDcl(Parsers.scala:2463)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$templateStat$1$$anonfun$applyOrElse$3.apply(Parsers.scala:3020)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$templateStat$1$$anonfun$applyOrElse$3.apply(Parsers.scala:3020)
    at scala.tools.nsc.ast.parser.Parsers$Parser.joinComment(Parsers.scala:701)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$templateStat$1.applyOrElse(Parsers.scala:3020)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$templateStat$1.applyOrElse(Parsers.scala:3015)
    at scala.tools.nsc.ast.parser.Parsers$Parser.statSeq(Parsers.scala:2947)
    at scala.tools.nsc.ast.parser.Parsers$Parser.templateStats(Parsers.scala:3014)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$templateStatSeq$1.apply(Parsers.scala:3001)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$templateStatSeq$1.apply(Parsers.scala:2978)
    at scala.tools.nsc.ast.parser.Parsers$Parser.checkNoEscapingPlaceholders(Parsers.scala:464)
    at scala.tools.nsc.ast.parser.Parsers$Parser.templateStatSeq(Parsers.scala:2978)
    at scala.tools.nsc.ast.parser.Parsers$Parser.templateBody(Parsers.scala:2907)
    at scala.tools.nsc.ast.parser.Parsers$Parser.templateBodyOpt(Parsers.scala:2914)
    at scala.tools.nsc.ast.parser.Parsers$Parser.templateOpt(Parsers.scala:2878)
    at scala.tools.nsc.ast.parser.Parsers$Parser.objectDef(Parsers.scala:2763)
    at scala.tools.nsc.ast.parser.Parsers$Parser.tmplDef(Parsers.scala:2702)
    at scala.tools.nsc.ast.parser.Parsers$Parser.topLevelTmplDef(Parsers.scala:2683)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$topStat$1$$anonfun$applyOrElse$2.apply(Parsers.scala:2970)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$topStat$1$$anonfun$applyOrElse$2.apply(Parsers.scala:2970)
    at scala.tools.nsc.ast.parser.Parsers$Parser.joinComment(Parsers.scala:701)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$topStat$1.applyOrElse(Parsers.scala:2970)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$topStat$1.applyOrElse(Parsers.scala:2963)
    at scala.tools.nsc.ast.parser.Parsers$Parser.statSeq(Parsers.scala:2947)
    at scala.tools.nsc.ast.parser.Parsers$Parser.topStatSeq(Parsers.scala:2962)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$compilationUnit$1.topstats$1(Parsers.scala:3160)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$compilationUnit$1.topstats$1(Parsers.scala:3152)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$compilationUnit$1.apply(Parsers.scala:3166)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$compilationUnit$1.apply(Parsers.scala:3128)
    at scala.tools.nsc.ast.parser.Parsers$Parser.checkNoEscapingPlaceholders(Parsers.scala:464)
    at scala.tools.nsc.ast.parser.Parsers$Parser.compilationUnit(Parsers.scala:3128)
    at scala.tools.nsc.ast.parser.Parsers$SourceFileParser$$anonfun$parseStartRule$1.apply(Parsers.scala:146)
    at scala.tools.nsc.ast.parser.Parsers$SourceFileParser$$anonfun$parseStartRule$1.apply(Parsers.scala:146)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$parse$1.apply(Parsers.scala:354)
    at scala.tools.nsc.ast.parser.Parsers$Parser$$anonfun$parse$1.apply(Parsers.scala:354)
    at scala.tools.nsc.ast.parser.Parsers$Parser.parseRule(Parsers.scala:347)
    at scala.tools.nsc.ast.parser.Parsers$Parser.parse(Parsers.scala:354)
    at scala.tools.nsc.ast.parser.Parsers$UnitParser.smartParse(Parsers.scala:243)
    at scala.tools.nsc.ast.parser.SyntaxAnalyzer.scala$tools$nsc$ast$parser$SyntaxAnalyzer$$initialUnitBody(SyntaxAnalyzer.scala:87)
    at scala.tools.nsc.ast.parser.SyntaxAnalyzer$ParserPhase.apply(SyntaxAnalyzer.scala:99)
    at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:430)
    at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:397)
    at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:397)
    at scala.collection.Iterator$class.foreach(Iterator.scala:743)
    at scala.collection.AbstractIterator.foreach(Iterator.scala:1177)
    at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:397)
    at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1625)
    at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1610)
    at scala.tools.nsc.Global$Run.compileSources(Global.scala:1605)
    at scala.tools.nsc.Global$Run.compile(Global.scala:1703)
    at org.fusesource.scalate.support.ScalaCompiler.compile(ScalaCompiler.scala:100)
    at org.fusesource.scalate.TemplateEngine.compileAndLoad(TemplateEngine.scala:757)
    ... 59 more

Pickle type for traits in method arguments

Just encountered a problem when using traits as arguments in api methods with boopickle.

So, I defined the following Api in the backend with a corresponding route:

sealed trait Tret
case class Clars(id: Int) extends Tret

trait Api {
  def fun(x: Tret): Int
}

Then, when calling this method in the frontend: Client.wire[Api].fun(Clars(12)).call().
It generates the following code:

Client.wire.apply[api.Api].self.doCall(autowire.Core.Request.apply[java.nio.ByteBuffer](collection.this.Seq.apply[String]("api", "Api", "fun"), scala.this.Predef.Map.apply[String, java.nio.ByteBuffer](scala.this.Predef.ArrowAssoc[String]("x").->[java.nio.ByteBuffer](Client.wire.apply[api.Api].self.write[api.Clars](api.Clars.apply(12))

Specifically this part (Client.wire.apply[api.Api].self.write[api.Clars](api.Clars.apply(12))) is crucial when using boopickle. Our autowire.Server defines serialization as:

object AutowireServer extends autowire.Server[ByteBuffer, Pickler, Pickler] {
  def read[Result: Pickler](p: ByteBuffer) = Unpickle[Result].fromBytes(p)
  def write[Result: Pickler](r: Result) = Pickle.intoBytes(r)
}

With Clars as type parameter, it will pickle the parameter as a Clars and not as a Tret. This is why, boopickle is unable to deserialize this parameter in the backend and (probably?) explains why I end up with a NotImplementedError in the generated router code.

As a workaround, I can explicitly specify the type: Client.wire[Api].fun(Clars(12) : Tret).call().
But this is very easy to forget without the compiler noticing and it will only result in runtime errors.

Would it be possible to handle this in autowire when calling write? So, we could take the parameter type according to the signature of the called method (which would be Tret in this example).

Support Scala 2.12

As projects move on to Scala 2.12, autowire lib should provide support for it.

Compilation issues

Although the compilation works fine in sbt, there are a couple of warnings. In IntelliJ, though, the project cannot be imported properly as it isn't able to resolve the dependencies.

[info] Updating {file:/Users/tim/dev/autowire/}jvm...
[warn] Binary version (0.9.0-SNAPSHOT) for dependency org.scala-lang#scala-pickling_2.10;0.9.0-SNAPSHOT
[warn]  in com.lihaoyi#autowire_2.10;0.2.3 differs from Scala binary version in project (2.10).
[info] Resolving org.scala-lang#scala-library;2.10.4 ...
[info] Compiling 3 Scala sources to /Users/tim/dev/autowire/js/target/scala-2.10/test-classes...
[info] Resolving org.jsawn#jawn-parser_2.10;0.5.4 ...
[warn]  module not found: org.jsawn#jawn-parser_2.10;0.5.4
[warn] ==== local: tried
[warn]   /Users/tim/.ivy2/local/org.jsawn/jawn-parser_2.10/0.5.4/ivys/ivy.xml
[warn] ==== public: tried
[warn]   http://repo1.maven.org/maven2/org/jsawn/jawn-parser_2.10/0.5.4/jawn-parser_2.10-0.5.4.pom
[warn] ==== sonatype-snapshots: tried
[warn]   https://oss.sonatype.org/content/repositories/snapshots/org/jsawn/jawn-parser_2.10/0.5.4/jawn-parser_2.10-0.5.4.pom
[warn] ==== Typesafe Repo: tried
[warn]   http://repo.typesafe.com/typesafe/releases/org/jsawn/jawn-parser_2.10/0.5.4/jawn-parser_2.10-0.5.4.pom
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  ::          UNRESOLVED DEPENDENCIES         ::
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  :: org.jsawn#jawn-parser_2.10;0.5.4: not found
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::

Also, is it intended that you're using an outdated version of Scala?

issues with accessor methods

I've been doing some scalajs hacking lately, and had a look at autowire the other day.
I exported a trait with an accessor method (no parameters), and that made my project stop compiling.

It really took me a while to figure out why, so i made a PR with a failing (that is, doesnt compile) test, so we can work it out. See #35

Bugs with Upickle 0.3.8

Hi,
I tested it with upickle 0.3.8 but it seems that it does not work yet, doesn'it ?
I have strange errors for one call in particular (but it works with a lot of others calls) :

/home/mathieu/work/cogit/openmole/openmole/gui/client/org.openmole.gui.client.core/src/main/scala/org/openmole/gui/client/core/ExecutionPanel.scala:70: type mismatch;
[error]  found   : upickle.default.Reader[(org.openmole.gui.ext.data.RunningData, org.openmole.gui.ext.data.RunningData)]
[error]  required: upickle.default.Aliases.R[(Seq[org.openmole.gui.ext.data.RunningEnvironmentData], Seq[org.openmole.gui.ext.data.RunningOutputData])]
[error]     (which expands to)  upickle.default.Reader[(Seq[org.openmole.gui.ext.data.RunningEnvironmentData], Seq[org.openmole.gui.ext.data.RunningOutputData])]
[error]             errorLevelSelector.content().map{_.level}.getOrElse(ErrorLevel())).call().andThen {
[error]

where RunningData is defined as:

case class RunningData(environmentsData: Seq[RunningEnvironmentData], outputsData: Seq[RunningOutputData])```

compiler issue with autowire macro

Hi,

I've been trying to adapt the spray-autowire example (https://github.com/lihaoyi/workbench-example-app/tree/autowire) so that we can use a servlet instead of spray for the server. While attempting this I've run into an issue with one of the macros from autowire.

I'm getting a stackoverflowexception when compiling the following code:

package example

import javax.servlet.annotation.WebServlet
import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
//import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{ExecutionContext, Await, Future}
import scala.concurrent.duration._

@WebServlet(urlPatterns=Array("/api/*"))
class MyServlet extends HttpServlet {
  implicit val ec: ExecutionContext = ExecutionContext.fromExecutor(new scala.concurrent.forkjoin.ForkJoinPool)

  override def doGet(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
    resp.getOutputStream.println("<p>Test</p>")
    val pathSegments: Array[String] = req.getPathInfo.split("/")
    pathSegments.foreach(s => d(resp, s"<p>${s}</p>"))
  }

  override def doPost(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
    val pathSegments: Array[String] = req.getPathInfo.split("/")
    pathSegments.foreach(d(resp, _))

    var line: String = ""
    var message: String = ""
    do {
      line = req.getReader().readLine()
      message += line
    } while (line != null)

    val z:Future[String] = AutowireServer.route[Api](Server)(
      autowire.Core.Request(pathSegments, upickle.read[Map[String, String]](message))
    )
    val s: String = Await.result(z, 200.millis)
    d(resp, s)
  }

  def d(resp: HttpServletResponse, s: String): Unit = {
    resp.getOutputStream.println(s)
  }
}

Any ideas / suggestions for workarounds?

Does autowire works with generic methods?

Hi, may I know if autowire works with methods that takes in type parameters?

For example

def generateData[T](implicit generator: Gen[T]): T = ???

If it does not, what is the recommended way?

write methods for each type I want to use?

ClientCallable.call() signature

This is a bit of a nitpick but when call() is expanded it includes a call to Future.map which requires an execution context. My IDE kept deleting the import because it didn't know it needed it. Would it be possible to change the signature of call() to something like call(implicit executor: ExecutionContext)?

Can not assign server.route to a val and use that

Type parameters for client here worked

  case class objParam(url:String)
  def getAtw[t](): ClientProxy[t, ByteBuffer, Default.Pickler, Default.Pickler] ={ // ok
    val a=autowireJs
    a.atwParams=objParam(sharedOlogx.rpcAuthed_ologx_path)
    a[t]
  }
  object autowireJs2 extends autowire.Client[ByteBuffer, Pickler, Pickler]   {
    override def doCall(req: Request): Future[ByteBuffer] = {
      dom.ext.Ajax
        .post(
          url = sharedOlogx.rpcAuthed_ologx_path + "/" + req.path.mkString("/"),
          data = Pickle.intoBytes(req.args),
          responseType = "arraybuffer",
          headers = Map(("Content-Type", "application/octet-stream"))
        )
        .map(r => TypedArrayBuffer.wrap(r.response.asInstanceOf[ArrayBuffer]))
    }

but the server side do not work(it compiles,but get runtime error when client tries to do rpc call)

      object autowireServer extends autowire.Server[ByteBuffer, Pickler, Pickler]{
        override def read[R: Pickler](p: ByteBuffer) = Unpickle[R].fromBytes(p)

        override def write[R: Pickler](r: R) = Pickle.intoBytes(r)

        @inline
        final def run[t](apiImpl: t,  // damn! this can not be made generic
                   reqPathList: List[String],
                   reqBodyBytes: ByteString): Future[ByteBuffer] = {
          lg("atw reqPathList:"+reqPathList)
          this.route[t](apiImpl)( // the problem is route[t] 
            autowire.Core.Request(
              reqPathList,
              read[Map[String, ByteBuffer]](reqBodyBytes.asByteBuffer))
          )
        }

      }

even if i inline it

the error is

scala.MatchError: Request(List(rpcAuthedOlogx, tst),Map()) (of class autowire.Core$Request)
at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:254)
at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:252)
at jvmUtils$akkaHttpUtil$rpc_server$autowireServerCls$$anonfun$run$1.applyOrElse(jvmUtils.scala:246)

Traits and inheritance

Not being a prononent of 'monolithic' APIs, I'd like to split my API up into several traits. As a consequence I can easily implement the logic in separate classes as well. On the server-side everything works out fine, but when compiling my Scala.JS client, I'm getting the following exception:

[error] scala.ScalaReflectionException: <none> is not a method
[error]         at scala.reflect.api.Symbols$SymbolApi$class.asMethod(Symbols.scala:228)
[error]         at scala.reflect.internal.Symbols$SymbolContextApiImpl.asMethod(Symbols.scala:82)
[error]         at autowire.Macros$$anonfun$8$$anonfun$apply$2.apply(Macros.scala:84)
[error]         at autowire.Macros$$anonfun$8$$anonfun$apply$2.apply(Macros.scala:73)
[error]         at autowire.Macros$Win.map(Macros.scala:26)
[error]         at autowire.Macros$Win.map(Macros.scala:25)
[error]         at autowire.Macros$$anonfun$8.apply(Macros.scala:73)
[error]         at autowire.Macros$$anonfun$8.apply(Macros.scala:49)
[error]         at autowire.Macros$Win.flatMap(Macros.scala:27)
[error]         at autowire.Macros$.clientMacro(Macros.scala:49)

If it wasn't clear what I'm trying to achieve, here is an example:

trait BookProtocol {
  def bookList(): Seq[BookListItem]
  ...
}

trait ArticleProtocol {
  ...
}

trait Protocol extends BookProtocol with ArticleProtocol

Asynchronous return values

I'm wondering whether it is a good idea to design a protocol with blocking logic. I'm using a similar setup as in the workbench-example-app. Spray enables us to return responses encapsulated in a Future. I would like to use ReactiveMongo, but if I would need to wait before returning the result, I will have synchronous behaviour anyway. What's your take on that?

Support Futures (ie sloww)

Any plans to add support for futures in autowire (or I guess, upickle)?

I threw together this hack which mostly seems to work, and lets me use a function like sloww from the test code:

  class FutureReader[T](implicit reader: Reader[T]) extends Reader[Future[T]] {
    def read: PartialFunction[Js.Value, Future[T]] = {
      case blah => Future.successful(reader.read(blah))
    }
  }

Called with:

implicit def iwishihada[T](implicit reader: Reader[T]): Reader[Future[T]] = new FutureReader[T]

Autowire RPC with REST fallback

I've been using autowire for a couple days now in an app that I'm building to familiarize myself with scalaJS.

I have working autowire RPC's in my app, but I was wondering if there was a way to use the server side endpoints as REST endpoints if my client app does not have an autowire client configured.

I totally understand if this is outside the scope of this library, but I was wondering if you or anyone else has explored this use case.

Can omission of .call() result in compile error

Hopefully this makes sense. If I'm doing anything stupid please let me know : )

I'm wondering if there's any way to have call() be required to compile. I've had a couple occasions already during development where I forgot to call it and finding out why ScalaJS blew up was fairly difficult.

trait MyApi {
  def now(): Future[Long]
}

val client = new Client(...)[MyApi]

client.now().call() map (l => println("Res: " + l))
client.now() map (l => println("Res: " + l)) // Any ideas on how to make this fail to compile?

simple example does not compile when using type parameters : could not find implicit value for evidence parameter of type MyReader[T]

      trait MyApi {
        val subApiString: SubApi[String]
      }

      trait SubApi[T] {
        def fooFighter(s: T): String // does not compile
//        def fooFighterString(s:String): String // compiles fine
      }

      // server-side implementation, and router
      object MyApiImpl extends MyApi {

        override val subApiString: SubApi[String] = null
      }

Full code:
https://github.com/jhegedus42/autowire/blob/9662a1bf1c97e65fe16d29c2243dc440b86deb3b/autowire/shared/src/test/scala/autowire/UpickleTests.scala#L24

Gives:

Error:(54, 101) could not find implicit value for evidence parameter of type MyReader[T]
        val routes: PartialFunction[Core.Request[String], Future[String]] = MyServerObj.route[MyApi](MyApiImpl)
Error:(54, 101) not enough arguments for method read: (implicit evidence$2: MyReader[T])T.
Unspecified value parameter evidence$2.
        val routes: PartialFunction[Core.Request[String], Future[String]] = MyServerObj.route[MyApi](MyApiImpl)
Error:(54, 101) type mismatch;
 found   : T @unchecked
 required: String
        val routes: PartialFunction[Core.Request[String], Future[String]] = MyServerObj.route[MyApi](MyApiImpl)

Any idea how to solve this issue ?

I guess the macro should understand that T is a String, or put a context bound requiring a type class instance for T .

How can I do that ?

This is how the macro generated route looks like:

Warning:scala: (RES,(<empty> match {
  case autowire.Core.Request(Seq("autowire", "Test", "MyApi", "subApiString", "fooFighter"), (args$macro$1 @ _)) => autowire.Internal.doValidate({
    <synthetic> <artifact> val x$2 = autowire.Internal.read[String, T](args$macro$1, scala.util.Left(autowire.Error.Param.Missing("s")), "s", ((x$1) => MyServerObj.read[T](x$1)));
    Nil.$colon$colon(x$2)
  }) match {
    case scala.$colon$colon((s @ (_: T @unchecked)), Nil) => scala.concurrent.Future(MyApiImpl.subApiString.fooFighter(s)).map(((x$3) => MyServerObj.write(x$3)))
    case _ => $qmark$qmark$qmark
  }
  case autowire.Core.Request(Seq("autowire", "Test", "MyApi", "subApiString", "fooFighter"), (args$macro$2 @ _)) => autowire.Internal.doValidate({
    <synthetic> <artifact> val x$5 = autowire.Internal.read[String, T](args$macro$2, scala.util.Left(autowire.Error.Param.Missing("s")), "s", ((x$4) => MyServerObj.read[T](x$4)));
    Nil.$colon$colon(x$5)
  }) match {
    case scala.$colon$colon((s @ (_: T @unchecked)), Nil) => scala.concurrent.Future(MyApiImpl.subApiString.fooFighter(s)).map(((x$6) => MyServerObj.write(x$6)))
    case _ => $qmark$qmark$qmark
  }
  case autowire.Core.Request(Seq("autowire", "Test", "MyApi", "subApiString", "fooFighter"), (args$macro$3 @ _)) => autowire.Internal.doValidate({
    <synthetic> <artifact> val x$8 = autowire.Internal.read[String, T](args$macro$3, scala.util.Left(autowire.Error.Param.Missing("s")), "s", ((x$7) => MyServerObj.read[T](x$7)));
    Nil.$colon$colon(x$8)
  }) match {
    case scala.$colon$colon((s @ (_: T @unchecked)), Nil) => scala.concurrent.Future(MyApiImpl.subApiString.fooFighter(s)).map(((x$9) => MyServerObj.write(x$9)))
    case _ => $qmark$qmark$qmark
  }
  case autowire.Core.Request(Seq("autowire", "Test", "MyApi", "subApiString", "fooFighter"), (args$macro$4 @ _)) => autowire.Internal.doValidate({
    <synthetic> <artifact> val x$11 = autowire.Internal.read[String, T](args$macro$4, scala.util.Left(autowire.Error.Param.Missing("s")), "s", ((x$10) => MyServerObj.read[T](x$10)));
    Nil.$colon$colon(x$11)
  }) match {
    case scala.$colon$colon((s @ (_: T @unchecked)), Nil) => scala.concurrent.Future(MyApiImpl.subApiString.fooFighter(s)).map(((x$12) => MyServerObj.write(x$12)))
    case _ => $qmark$qmark$qmark
  }
}: autowire.Core.Router[String]))

How to use it with unfiltered?

In the example, it's using spray on the server side.

I tried to use unfiltered, but not sure how to do it. Could you give some ideas?

Not sure how to write similar code with unfiltered for following code:

 post {
        path("api" / Segments){ s =>
          extract(_.request.entity.asString) { e =>
            complete {
              AutowireServer.route[Api](Server)(
                autowire.Core.Request(s, upickle.read[Map[String, String]](e))
              )
            }
          }
        }
      }

Seems to add escaping not needed to serialized format

From my testing of scalajs-transport, it seems like Autowire (or uPickle) adds escaping of "-s and even backslashes:

["{"req":{"path":["shared","Api","echo"],"args":{"s":""Test from client""}},"id":1}"]

["{"res":""Received on server: Test from client"","id":1}"]

exception during macro expansion after importing widok packages

I'm experimenting with an autowire-based scalajs client. It works as expected as long as i stick to plain scalajs/dom. Now i want to try adding widok (https://widok.github.io/) to the party..

For me, just adding widok as a sbt-dependency to the javascript project (crossproject) and importing org.widok._ causes autowire to fail compilation with the following error:

[error] /Users/janne/projects/private/gb/example/js/src/main/scala/example/ScalaJSExample.scala:40: exception during macro expansion:
[error] java.lang.AssertionError: assertion failed
[error] at scala.Predef$.assert(Predef.scala:151)
[error] at upickle.Macros$.macroRImpl(Macros.scala:32)
[error] Client[Api].chapters().call().map { c =>
[error] ^

I don't know if this issue belongs to autowire or widok.. or upickle? Filing it here anyway.

Let me know if I can do anything to assist with logs or debugging. I'm really looking forward to getting these really promising technologies working together :-)

sbt compile hangs after adding autowire to a play 2.4.2 project

I've created a bare minimum play-scala project using activator, added autowire and upickle. Then added the necessary autowire.Server implementation. Sbt compile just hangs and doesnt quit, i had to kill the process. I might be doing something wrong here, although i've tried to stay as close as possible to the samples in this repo. Here's the controller.

package controllers

import play.api._
import play.api.mvc.Results._
import play.api.mvc._
import upickle.default.{Reader => UpickleReader, Writer => UpickleWriter}
import autowire._

class Application extends Controller {

  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }

  def something = Action.async { request =>
    AutowireRouter.route[TestApi](TestApiImpl)(autowire.Core.Request(
      "controllers/TestApi/getPeople".split(","),
      upickle.default.read[Map[String, String]](request.body.asText.getOrElse("")))).map{ people =>
      Ok(people)
    }.recover{case e => InternalServerError(e.getMessage)}
  }

}

case class TestModel(name: String, age: Int)

trait TestApi {
  val people: Seq[TestModel] = Seq(TestModel("a",1), TestModel("b",2), TestModel("c",3), TestModel("d",4))
  def getPeople(howMany: Int): Seq[TestModel]
}

object TestApiImpl extends TestApi {
  def getPeople(howMany: Int) = people.take(howMany)
}

object AutowireRouter extends Server[String, UpickleReader, UpickleWriter] {
  def read[Result: UpickleReader](input: String) = upickle.default.read[Result](input)
  def write[Result: UpickleWriter](result: Result) = upickle.default.write(result)
}

build.sbt

name := """tester"""

version := "1.0-SNAPSHOT"

lazy val root = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.11.6"

libraryDependencies ++= Seq(
  jdbc,
  cache,
  ws,
  specs2 % Test,
  "com.lihaoyi" %% "autowire" % "0.2.5",
  "com.lihaoyi" %% "upickle" % "0.3.4"
)

resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases"

// Play provides two styles of routers, one expects its actions to be injected, the
// other, legacy style, accesses its actions statically.
routesGenerator := InjectedRoutesGenerator

Value classes?

I have a class that takes a (case) value class as a parameter.
It causes a compile error, such as
Cannot materialize pickler for non-case class: Array[com.sample.Event]. If this is a collection, the error can refer to the class inside.

Am I doing something wrong, or are value classes not supported by Autowire?

Default parameters not supported?

When I try interfaces like this:

trait Protocol {
val dictionary: protocol.Dictionary
val userRegistry: protocol.UserRegistry
}

trait UserRegistry {
def createUser(newUser: User): User
def lookupFirstNamePart(firstname: String, exact: Boolean = false): Seq[User]
}

and an implementation like this:

override def lookupFirstNamePart(firstnamePart: String, exact: Boolean = false)

the compile fails at line:

val router = AutowireServer.routeProtocol

with error: "value lookupFirstNamePart$default$2 is not a member of io.widok.server.Controllers"

Am I doing something wrong, or is the macro magic missing support for default values?

Time out

Hi! I haven't found where is the proper place to set the implicit timeout for sendReceive... so, if I'd like to have a timeout beyond the 60s default, what can I do?

Overloading methods

Apparently, methods cannot be overloaded. I have a trait with the following two methods:

def bookRead(id: String): Option[BookPage]
def bookRead(id: String, page: Int): Option[BookPage]

This results in:

[error] scala.ScalaReflectionException: value bookRead encapsulates multiple overloaded alternatives and cannot be treated as a method. Consider invoking `<offending symbol>.asTerm.alternatives` and manually picking the required method
[error]         at scala.reflect.api.Symbols$SymbolApi$class.asMethod(Symbols.scala:228)
[error]         at scala.reflect.internal.Symbols$SymbolContextApiImpl.asMethod(Symbols.scala:82)
[error]         at autowire.Macros$$anonfun$8$$anonfun$apply$2.apply(Macros.scala:84)
[error]         at autowire.Macros$$anonfun$8$$anonfun$apply$2.apply(Macros.scala:73)
[error]         at autowire.Macros$Win.map(Macros.scala:26)
[error]         at autowire.Macros$Win.map(Macros.scala:25)
[error]         at autowire.Macros$$anonfun$8.apply(Macros.scala:73)
[error]         at autowire.Macros$$anonfun$8.apply(Macros.scala:49)
[error]         at autowire.Macros$Win.flatMap(Macros.scala:27)
[error]         at autowire.Macros$.clientMacro(Macros.scala:49)

Case classes

Hi,
You say in the doc: "case classes and such don't come by default". But what do we need to do to deal with them ?

Thanks !
Mathieu

Strange exception "autowire.Error$InvalidInput" with no message

@lihaoyi I am trying to get autowire to work.

My url is http://0.0.0.0:8000/service/v1/dagr/api/DagrApi/query, body is

{
	"name" : "task"
}

and the stacktrace (server-site) is

[ERROR] [06/29/2017 10:49:41.513] [dagr-server-akka.actor.default-dispatcher-6] [akka.actor.ActorSystemImpl(dagr-server)] Error during processing of request: 'autowire.Error$InvalidInput (No error message supplied)'. Completing with 500 Internal Server Error response. To change default exception handling behavior, provide a custom ExceptionHandler.
autowire.Error$InvalidInput
	at autowire.Internal$.doValidate(Internal.scala:49)
	at dagr.webservice.DagrApiService$$anonfun$1.applyOrElse(DagrApiService.scala:120)
	at dagr.webservice.DagrApiService$$anonfun$1.applyOrElse(DagrApiService.scala:120)
	at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:34)
	at dagr.webservice.DagrApiService.$anonfun$queryRoute$3(DagrApiService.scala:120)
	at akka.http.scaladsl.server.util.ApplyConverterInstances$$anon$1.$anonfun$apply$1(ApplyConverterInstances.scala:14)
	at akka.http.scaladsl.server.ConjunctionMagnet$$anon$2.$anonfun$apply$3(Directive.scala:162)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$mapRouteResult$2(BasicDirectives.scala:61)
	at akka.http.scaladsl.server.directives.FutureDirectives.$anonfun$onComplete$3(FutureDirectives.scala:37)
	at akka.http.scaladsl.util.FastFuture$.$anonfun$transformWith$1(FastFuture.scala:37)
	at akka.http.scaladsl.util.FastFuture$.strictTransform$1(FastFuture.scala:41)
	at akka.http.scaladsl.util.FastFuture$.transformWith$extension1(FastFuture.scala:45)
	at akka.http.scaladsl.util.FastFuture$.transformWith$extension0(FastFuture.scala:37)
	at akka.http.scaladsl.server.directives.FutureDirectives.$anonfun$onComplete$2(FutureDirectives.scala:37)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$textract$2(BasicDirectives.scala:154)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$mapRouteResult$2(BasicDirectives.scala:61)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$textract$2(BasicDirectives.scala:154)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$mapRequestContext$2(BasicDirectives.scala:43)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$textract$2(BasicDirectives.scala:154)
	at akka.http.scaladsl.server.RouteConcatenation$RouteWithConcatenation.$anonfun$$tilde$2(RouteConcatenation.scala:47)
	at akka.http.scaladsl.util.FastFuture$.strictTransform$1(FastFuture.scala:41)
	at akka.http.scaladsl.util.FastFuture$.transformWith$extension1(FastFuture.scala:45)
	at akka.http.scaladsl.util.FastFuture$.flatMap$extension(FastFuture.scala:26)
	at akka.http.scaladsl.server.RouteConcatenation$RouteWithConcatenation.$anonfun$$tilde$1(RouteConcatenation.scala:44)
	at akka.http.scaladsl.server.directives.ExecutionDirectives.$anonfun$handleExceptions$2(ExecutionDirectives.scala:32)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$mapRouteResultWith$2(BasicDirectives.scala:67)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$textract$2(BasicDirectives.scala:154)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$mapRouteResult$2(BasicDirectives.scala:61)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$textract$2(BasicDirectives.scala:154)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$mapRouteResultWith$2(BasicDirectives.scala:67)
	at akka.http.scaladsl.server.directives.BasicDirectives.$anonfun$textract$2(BasicDirectives.scala:154)
	at akka.http.scaladsl.server.directives.ExecutionDirectives.$anonfun$handleExceptions$2(ExecutionDirectives.scala:32)
	at akka.http.scaladsl.server.Route$.$anonfun$asyncHandler$1(Route.scala:79)
	at akka.stream.impl.fusing.MapAsync$$anon$24.onPush(Ops.scala:1169)
	at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:747)
	at akka.stream.impl.fusing.GraphInterpreter.processEvent(GraphInterpreter.scala:710)
	at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:616)
	at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:471)
	at akka.stream.impl.fusing.GraphInterpreterShell.receive(ActorGraphInterpreter.scala:423)
	at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:603)
	at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:618)
	at akka.actor.Actor.aroundReceive(Actor.scala:502)
	at akka.actor.Actor.aroundReceive$(Actor.scala:500)
	at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:529)
	at akka.actor.ActorCell.receiveMessage(ActorCell.scala:526)
	at akka.actor.ActorCell.invoke(ActorCell.scala:495)
	at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:257)
	at akka.dispatch.Mailbox.run(Mailbox.scala:224)
	at akka.dispatch.Mailbox.exec(Mailbox.scala:234)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

Branch (if you want to check out): https://github.com/fulcrumgenomics/dagr/tree/nh_akka_http

Command:
java -Xmx8G -jar dagr/webservice/target/scala-2.12/dagr-webservice-0.2.1-SNAPSHOT.jar --webservice true --host 0.0.0.0 --port 8000 SleepyPipeline -j -n 10 -s 42

Server pushes through autowire?

Autowire is a very cool piece of technology, making it very easy to communicate from Scala.js to a Scala-based server process (personally, I use Play).

It would be cool if it also supported the reverse case as well: "calls" / "pushes" / "updates" coming from the server to the client (using Play websockets, Atmosphere, or whatever one wants to use).

Of course this is a feature-request, but I suspect (or hope) it is something that autowire is already capable of when correctly set-up...?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.