line / armeria

Your go-to microservice framework for any situation, from the creator of Netty et al. You can build any type of microservice leveraging your favorite technologies, including gRPC, Thrift, Kotlin, Retrofit, Reactive Streams, Spring Boot and Dropwizard.
https://armeria.dev
Apache License 2.0
4.73k stars 899 forks source link

Better interop with other JVM languages #1078

Open trustin opened 6 years ago

trustin commented 6 years ago

Most notably Scala, Kotlin and Clojure. We could write a sample project that makes sure at least:

trustin commented 4 years ago

@tobias- IIRC your Armeria-based project is written in Kotlin. It'd be awesome if you can tell us any issues you've encountered so far that makes your code unnecessarily verbose.

tobias- commented 4 years ago

Most(all) of my kludges/enforced ugly code are around lambda functions, especially with decorators. The syntax for them is slightly different in Kotlin, and the compiler seems less able to figure out what generics are needed.

I'll let you know if I can think of anything else.

-- Edit: Found HttpService and SimpleDecoratingHttpService and removed all my examples

trustin commented 4 years ago

I still wish to know the before and after of your code. Would you mind pasting it?

tobias- commented 4 years ago

Sure. The first example is still not working without a lot of generics: https://gist.github.com/tobias-/cbdc441872d2256b1d7a318b54ae057e

rfkm commented 4 years ago

Scala example:

object Test {
  import brave.Tracing
  import com.linecorp.armeria.client.ClientOptionsBuilder
  import com.linecorp.armeria.client.brave.BraveClient
  import com.linecorp.armeria.client.logging.LoggingClient

  // ok
  val a = new ClientOptionsBuilder()
    .decorator(LoggingClient.newDecorator())
    .build()

  // can't compile:
  // [error] /path/to/Test.scala:62:6: overloaded method value decorator with alternatives:
  // [error]   [I <: com.linecorp.armeria.common.HttpRequest, O <: com.linecorp.armeria.common.HttpResponse](decorator: com.linecorp.armeria.client.DecoratingClientFunction[I,O])com.linecorp.armeria.client.ClientOptionsBuilder <and>
  // [error]   [T <: com.linecorp.armeria.client.Client[I,O], R <: com.linecorp.armeria.client.Client[I,O], I <: com.linecorp.armeria.common.HttpRequest, O <: com.linecorp.armeria.common.HttpResponse](decorator: java.util.function.Function[T,R])com.linecorp.armeria.client.ClientOptionsBuilder
  // [error]  cannot be applied to (java.util.function.Function[com.linecorp.armeria.client.Client[com.linecorp.armeria.common.HttpRequest,com.linecorp.armeria.common.HttpResponse],com.linecorp.armeria.client.brave.BraveClient])
  // [error]     .decorator(BraveClient.newDecorator(Tracing.current()))
  // [error]      ^
  // [error] one error found
  val b = new ClientOptionsBuilder()
    .decorator(BraveClient.newDecorator(Tracing.current()))
    .build()

  // ok
  val c = new ClientOptionsBuilder()
    .decorator[Client[HttpRequest, HttpResponse], BraveClient, HttpRequest, HttpResponse](
      BraveClient.newDecorator(Tracing.current())
    )
    .build()
}
tobias- commented 4 years ago

@rfkm Looks similar to my problem? Basically you need to force-cast with disabled warnings to get the right generics?

tobias- commented 4 years ago

One thing I'd like is not to have to use Optional<> in annotated rest interfaces. If a variable is marked as @Nullable (or one of the other 3-4 annotations with the same purpose) it would allow missing fields annotated with e.g. @Header. In Kotlin, that would mean a String? instead of Optional<String> and it would work better with kotlin-specific operators designed to handle nullability. If IntelliJ is used to program Java, annotating with @Nullable would probably be as good as Optional for clarity in code.

trustin commented 4 years ago

I'd like to assign this issue to @ikhoon because he knows Scala knows well. :wink: It'd be nice to have Scala and Kotlin (and Clojure?) examples that demonstrate smooth integration. I hope to have this as a part of our 1.0.0 release.

tobias- commented 4 years ago

I need to get some hours to check what happens with the changes in 0.96 to decorating and HttpService. It might be that the need for ugly-casting is gone.

ikhoon commented 4 years ago

I'd like to assign this issue to @ikhoon because he knows Scala knows well. 😉 It'd be nice to have Scala and Kotlin (and Clojure?) examples that demonstrate smooth integration. I hope to have this as a part of our 1.0.0 release.

I missed your mention. 😭 I like to handle this issue before 1.0.0 release 🥰

tobias- commented 4 years ago

Ok, 0.96.0 upgrade complete. Extending the api with HttpService accepting methods did help, but still a lot of weird casts/specification of interfaces. First instinct is to push this for the future and implement a kotlin extension lib with methods for decorating, getting coroutine support etc etc.

m50d commented 4 years ago

Some loose thoughts as a Scala programmer:

ikhoon commented 4 years ago

It would be great if it was easy to integrate with fs2-based code in cases where we want to be truly async all the way through (i.e. streaming the http response).

Armeria supports Reactive Streams. that means you can easily interop any stream library that supports Reactive Streams. https://fs2.io/guide.html#reactive-streams

Scrooge offers a more idiomatic Scala implementation of thrift. Their server-side support was hardcoded to use Twitter futures at one point, but it's fairly easy to add a template for a different kind of future (I used the one in my fork m50d/scrooge at a previous job)

Armeria runs on the top of HTTP protocol such as H1, H1C, H2, and H2C. IIUC, scrooge supports TCP :-( Alternatively, how about using gRPC with ScalaPB?

It would be nice if we could use armeria as a backend for routes defined in rho

That sounds good. Are you interested in making a PR? 😀

ikhoon commented 4 years ago

Scala example:

object Test {
  import brave.Tracing
  import com.linecorp.armeria.client.ClientOptionsBuilder
  import com.linecorp.armeria.client.brave.BraveClient
  import com.linecorp.armeria.client.logging.LoggingClient

  // ok
  val a = new ClientOptionsBuilder()
    .decorator(LoggingClient.newDecorator())
    .build()

  // can't compile:
  // [error] /path/to/Test.scala:62:6: overloaded method value decorator with alternatives:
  // [error]   [I <: com.linecorp.armeria.common.HttpRequest, O <: com.linecorp.armeria.common.HttpResponse](decorator: com.linecorp.armeria.client.DecoratingClientFunction[I,O])com.linecorp.armeria.client.ClientOptionsBuilder <and>
  // [error]   [T <: com.linecorp.armeria.client.Client[I,O], R <: com.linecorp.armeria.client.Client[I,O], I <: com.linecorp.armeria.common.HttpRequest, O <: com.linecorp.armeria.common.HttpResponse](decorator: java.util.function.Function[T,R])com.linecorp.armeria.client.ClientOptionsBuilder
  // [error]  cannot be applied to (java.util.function.Function[com.linecorp.armeria.client.Client[com.linecorp.armeria.common.HttpRequest,com.linecorp.armeria.common.HttpResponse],com.linecorp.armeria.client.brave.BraveClient])
  // [error]     .decorator(BraveClient.newDecorator(Tracing.current()))
  // [error]      ^
  // [error] one error found
  val b = new ClientOptionsBuilder()
    .decorator(BraveClient.newDecorator(Tracing.current()))
    .build()

  // ok
  val c = new ClientOptionsBuilder()
    .decorator[Client[HttpRequest, HttpResponse], BraveClient, HttpRequest, HttpResponse](
      BraveClient.newDecorator(Tracing.current())
    )
    .build()
}

@rfkm This is fixed by @minwoox 's work #2239 😉

trustin commented 3 years ago

What do you think about adding out-of-the-box Option[] type support to annotated services? Could probably load and instantiate the scala.Option class dynamically? I can take a stab if it makes sense and is doable with small amount of modification. Thoughts, @rfkm and @ikhoon ?

trustin commented 3 years ago

@tobias- wrote:

One thing I'd like is not to have to use Optional<> in annotated rest interfaces. If a variable is marked as @Nullable (or one of the other 3-4 annotations with the same purpose) it would allow missing fields annotated with e.g. @Header. In Kotlin, that would mean a String? instead of Optional<String> and it would work better with kotlin-specific operators designed to handle nullability. If IntelliJ is used to program Java, annotating with @Nullable would probably be as good as Optional for clarity in code.

This has been resolved via #2766. Let me know what you think. 🙇

ikhoon commented 3 years ago

What do you think about adding out-of-the-box Option[] type support to annotated services? Could probably load and instantiate the scala.Option class dynamically? I can take a stab if it makes sense and is doable with small amount of modification. Thoughts, @rfkm and @ikhoon ?

If scala.Option could be supported with small amount of changes, it makes sense.

FYI

tobias- commented 3 years ago

I know, use and like it! :+1:

Thanks :bow:

@tobias- wrote:

One thing I'd like is not to have to use Optional<> in annotated rest interfaces. If a variable is marked as @Nullable (or one of the other 3-4 annotations with the same purpose) it would allow missing fields annotated with e.g. @Header. In Kotlin, that would mean a String? instead of Optional<String> and it would work better with kotlin-specific operators designed to handle nullability. If IntelliJ is used to program Java, annotating with @Nullable would probably be as good as Optional for clarity in code.

This has been resolved via #2766. Let me know what you think.

rfkm commented 3 years ago

IMO, Most Scala users prefer routing DSL than annotated service, unlike Java users. In the future, I think, it would be better to add a DSL layer for adopting Scala users.

Yes, I agree. However, instead of creating a new DSL, it may be better to provide Armeria implementation to existing libraries (e.g. sttp, tapir, http4s, etc). For example, sttp is a popular HTTP client facade in Scala, but the one of most commonly used backend implementations is AsyncHTTPClient which doesn't support HTTP2. Contributing to their repositories might help more Scala users know about Armeria 😄

ikhoon commented 3 years ago

For example, sttp is a popular HTTP client facade in Scala,

I was personally working on sttp for Armeria backend. But I stopped because of multipart issue #253

I’ll resume it once #2894 is merged and released. 😀

ikhoon commented 3 years ago

@rfkm @m50d You might be interested in http4s/http4s#3614 PR. 😉

trustin commented 3 years ago

Let me unassign a milestone from this issue, because it's actually on-going effort spans across many releases.