agrestio / agrest

Server-side Java REST Framework for easy access to data graphs from various backends
https://agrest.io
Apache License 2.0
82 stars 34 forks source link

Deprecated listeners are failed being added into LR SelectBuilder #251

Closed elena-bondareva closed 6 years ago

elena-bondareva commented 7 years ago

Annotated listeners were deprecated in favor of the functional interceptor API since 2.7. Consider a use case for the deprecated listener.

public class PojoListener {

    @SelectServerParamsApplied
    public BaseLinearProcessingStage<SelectContext<ITEntity>, ITEntity> doSomethingWithTheFlow(SelectContext<ITEntity> context,
            BaseLinearProcessingStage<SelectContext<ITEntity>, ITEntity> next) {

        ITEntity e1 = new ITEntity();
        e1.setId(5);
        e1.setName("name5");

        ITEntity e2 = new ITEntity();
        e2.setId(6);
        e2.setName("name6");

        context.setObjects(Arrays.asList(e1, e2));

        // terminate further processing
        return null;
    }
}

Having added the listener into LinkRest request processor builder, like

@Path("/lr1")
@Produces(MediaType.APPLICATION_JSON)
public class LrResource1 {

    @Context
    private Configuration config;

    @GET
    public DataResponse<ITEntity> get(UriInfo info) {
        return LinkRest.select(ITEntity.class, config).uri(info).listener(new PojoListener()).get();
    }
}

do GET request on a server:

http://127.0.0.1:8080/lr/lrservlet/lr1

The exception is thrown:

com.nhl.link.rest.LinkRestException: Error invoking listener method
    at com.nhl.link.rest.runtime.listener.ListenerInvocation.invokeOld(ListenerInvocation.java:56)
    at com.nhl.link.rest.runtime.listener.ListenerInvocation.invoke(ListenerInvocation.java:35)
    at com.nhl.link.rest.processor.Processor.lambda$andThen$0(Processor.java:19)
    at com.nhl.link.rest.processor.Processor.lambda$andThen$0(Processor.java:17)
    at com.nhl.link.rest.processor.Processor.lambda$andThen$0(Processor.java:17)
    at com.nhl.link.rest.runtime.DefaultSelectBuilder.get(DefaultSelectBuilder.java:223)
    at io.bootique.bom.linkrest.r1.LrResource1.get(LrResource1.java:25)
...
Caused by: java.lang.ClassCastException: Cannot cast com.nhl.link.rest.runtime.listener.ListenerInvocation$$Lambda$103/887401986 to com.nhl.link.rest.processor.BaseLinearProcessingStage
    at java.lang.invoke.MethodHandleImpl.newClassCastException(MethodHandleImpl.java:361)
    at java.lang.invoke.MethodHandleImpl.castReference(MethodHandleImpl.java:356)
    at com.nhl.link.rest.runtime.listener.ListenerInvocationFactoryCompiler$4.invoke(ListenerInvocationFactoryCompiler.java:253)
    at com.nhl.link.rest.runtime.listener.ListenerInvocationFactoryCompiler$1$1.doInvokeOld(ListenerInvocationFactoryCompiler.java:190)
    at com.nhl.link.rest.runtime.listener.ListenerInvocation.invokeOld(ListenerInvocation.java:53)
    ... 53 common frames omitted
atomashpolskiy commented 7 years ago

You have invalid parameter types in your listener method. Correct signature is: (C, ProcessingStage<C, ? super T>):ProcessingStage<C, ? super T>

Your listener method's signature is: (C, BaseLinearProcessingStage<SelectContext<ITEntity>, ITEntity>):BaseLinearProcessingStage<SelectContext<ITEntity>, ITEntity>

In runtime this is causing a narrowing type conversion: ProcessingStage -> BaseLinearProcessingStage (you may see what is really passed to the listener method in ListenerInvocation class)

This type of narrowing type conversions is forbidden in Java, so you're getting a ClassCastException.

General reasoning for this is that:

atomashpolskiy commented 7 years ago

Oh, and in fact lambdas are probably more lenient with regards to runtime parameter types, so your method could work, if the object being passed had proper type (pure speculation, I haven't checked). But in your case the second argument has com.nhl.link.rest.runtime.listener.ListenerInvocation$$Lambda$103/887401986 type, so it's totally different type than BaseLinearProcessingStage, hence ClassCastException.

andrus commented 7 years ago

Not to argue about the (in)correctness of the method signature, but this actually used to work in prior LR versions (This code is from Bootique integration tests). So while we have no plans of "fixing" anything here, this issue may be helpful in documenting this upgrade incompatibility.

atomashpolskiy commented 7 years ago

I guess the mock stage used to be an instance of BaseLinearProcessingStage in the past, and now it's a lambda

andrus commented 6 years ago

IIRC this was a placeholder to document the upgrade issue. We are well past 2.7 release, so I guess we can close this now.