0

I have my service declared this way:

public interface BlogQueryService extends Service {

  public ServiceCall<String, Source<String, ?>> tick(int interval);
  public ServiceCall<String, Source<String, ?>> tock();
  public ServiceCall<NotUsed, Source<PostSummary, ?>> newPosts();
  public ServiceCall<String, Source<PostSummary, ?>> getPostSummaries();

  @Override
  default Descriptor descriptor() {
    return named("blog-query").with(
      //pathCall("/api/bloggie/tick/:interval", this::tick),
      pathCall("/api/bloggie/tock", tock())
      //pathCall("/api/bloggie/newPosts", this::newPosts),
      //pathCall("/api/bloggie/postSummaries", this::getPostSummaries)
    ).withAutoAcl(true);
  }
}

The tick works. The tock doesn't.

When I invoke it using websocket client (to ws://localhost:9000/api/bloggie/tock ) , I got "undefined" as response, indicating no mapping found for that URL.

After some experimentings, found out why: tick works because it has url param (the :interval). Tick doesn't work because it doesn't have url param. Seriously pathCall requires you to have param in your URL? So I checked the API of Service: http://www.lagomframework.com/documentation/1.0.x/api/java/com/lightbend/lagom/javadsl/api/Service.html

There are several overloaded declarations of pathCall. Apparently the tick uses this one:

static <Request,Response,A> Descriptor.Call<Request,Response> pathCall(String pathPattern, akka.japi.function.Function<A,ServiceCall<Request,Response>> methodRef) 

So from the signature, yes it requires the method to take a parameter. So, if the method (such is tock) doesn't take a param, the binding will fail at runtime. So I guess I need to use this one instead:

static <Request,Response> Descriptor.Call<Request,Response> pathCall(String pathPattern, akka.japi.function.Creator<ServiceCall<Request,Response>> methodRef)

The problem is... I don't know how. I haven't seen any example of the use of akka.japi.function.Creator in pathCall.

I tried this:

  default Descriptor descriptor() {
    return named("blog-query").with(
      pathCall("/api/bloggie/tick/:interval", this::tick),
      pathCall("/api/bloggie/tock", new Creator<ServiceCall<String, Source<String, ?>>> () {
          public ServiceCall<String, Source<String, ?>> create() {
              return tock();
          }
      })
      //pathCall("/api/bloggie/newPosts", this::newPosts),
      //pathCall("/api/bloggie/postSummaries", this::getPostSummaries)
    ).withAutoAcl(true);
  }

It compiles. But it throws an error at runtime:

com.google.inject.CreationException: Unable to create injector, see the following errors:

1) Error in custom provider, java.lang.IllegalStateException: Unable to resolve method for service call with ID PathCallId{pathPattern='/api/bloggie/tock'}. Ensure that the you have passed a method reference (ie, this::someMethod). Passing anything else, for example lambdas, anonymous classes or actual implementation classes, is forbidden in declaring a service descriptor.
  at com.lightbend.lagom.javadsl.server.ServiceGuiceSupport.bindServices(ServiceGuiceSupport.java:43) (via modules: com.google.inject.util.Modules$OverrideModule -> sample.bloggie.impl.BlogServiceModule)
  while locating com.lightbend.lagom.internal.server.ResolvedServices

Thanks in advance!


I just did some experiments... All compiled, but none of them works....

namedCall("/api/bloggie/tock", this::tock)

Result: Compile success. Runtime: path unknown (no binding (?)).

Then I tried

pathCall("/api/bloggie/tock", () -> this.tock())

Result: exception.

com.google.inject.CreationException: Unable to create injector, see the following errors:
1) Error in custom provider, scala.MatchError: Request (of class sun.reflect.generics.reflectiveObjects.TypeVariableImpl)
  at com.lightbend.lagom.javadsl.server.ServiceGuiceSupport.bindServices(ServiceGuiceSupport.java:43) (via modules: com.google.inject.util.Modules$OverrideModule -> sample.bloggie.impl.BlogServiceModule)
  while locating com.lightbend.lagom.internal.server.ResolvedServices
    for parameter 1 at com.lightbend.lagom.internal.server.ServiceRegistrationModule$RegisterWithServiceRegistry.<init>(ServiceRegistrationModule.scala:55)
  at com.lightbend.lagom.internal.server.ServiceRegistrationModule.bindings(ServiceRegistrationModule.scala:29):
Binding(class com.lightbend.lagom.internal.server.ServiceRegistrationModule$RegisterWithServiceRegistry to self eagerly) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1)
  while locating com.lightbend.lagom.internal.server.ServiceRegistrationModule$RegisterWithServiceRegistry

Then I tried:

public ServiceCall<NotUsed, Source<String, ?>> tock(Void x);

Result: exception

com.google.inject.CreationException: Unable to create injector, see the following errors:

1) Error in custom provider, java.lang.IllegalArgumentException: Don't know how to serialize ID class java.lang.Void
  at com.lightbend.lagom.javadsl.server.ServiceGuiceSupport.bindServices(ServiceGuiceSupport.java:43) (via modules: com.google.inject.util.Modules$OverrideModule -> sample.bloggie.impl.BlogServiceModule)

Update: "Solved" (partially). Figured out that this one works:

pathCall("/tock", this::tock)

I can open it using this URL: ws://localhost:9000/tock

So..., I can't have nicely structured URL for those functions that returns stream, when those functions need no param? At least for now (?).


UPDATE: seems like this problem is happening not only with pathCall. I encountered the same problem with rest call. This one doesn't work (no binding):

public ServiceCall<NotUsed, PSequence<PostSummary>> getPostSummaries();
...
restCall(Method.GET, "/api/bloggie/postSummaries", this::getPostSummaries)

This one works:

public ServiceCall<NotUsed, PSequence<PostSummary>> getPostSummaries();
...
restCall(Method.GET, "/postSummaries", this::getPostSummaries)

Thanks!

Cokorda Raka
  • 4,375
  • 6
  • 36
  • 54

1 Answers1

1

So firstly, namedCall should only be used if you don't care about the path. You are invoking the service call directly, which means you do care about the path, so you have to use pathCall or restCall.

This should work:

pathCall("/api/bloggie/tock", this::tock)

Also, I think you're not pasting the full errors. Make sure you check right to the bottom of the list of Guice errors, that should explain exactly what the problem is, in many of the cases above, the problem is that you're not passing a method reference, you're passing a lambda, and the error message should say that.

James Roper
  • 12,695
  • 46
  • 45
  • Hi.... Sorry... finally after trying and trying, figure out ... it was my fault. I have declared in another and endpoint that conflicted..... restCall(Method.GET, "/api/bloggie/:id", this::getPost), Case closed. :) – Cokorda Raka May 16 '16 at 00:45
  • I mean... to avoid the conflict I now use : restCall(Method.GET, "/api/yeaaaahh/postSummaries", this::getPostSummaries), – Cokorda Raka May 16 '16 at 00:46