Weird things: Controller did not get enhanced correctly - actframework/actframework GitHub Wiki

I spent 2 hours today fighting with an interesting bug in the Job App.

The Symptom is when I added a parameter to the home() method as:

    @GetAction("/")
    public static Result home(Integer limit, ActionContext context) {
        if (context.isAjax()) {
            return json(JobLog.logs(limit));
        }
        return render();
    }

Then I suddenly get the following exception at backend:

act.exception.BindException: java.lang.NullPointerException
	at act.handler.builtin.controller.impl.ReflectedHandlerInvoker.params(ReflectedHandlerInvoker.java:304)
	at act.handler.builtin.controller.impl.ReflectedHandlerInvoker.handle(ReflectedHandlerInvoker.java:152)
	at act.handler.builtin.controller.ControllerAction.handle(ControllerAction.java:20)
	at act.handler.builtin.controller.RequestHandlerProxy._handle(RequestHandlerProxy.java:321)
	at act.handler.builtin.controller.RequestHandlerProxy.handle(RequestHandlerProxy.java:150)
	at act.handler.DelegateRequestHandler.handle(DelegateRequestHandler.java:18)
	at act.route.Router$ContextualHandler.handle(Router.java:618)
	at act.xio.NetworkClient.handle(NetworkClient.java:51)
	at act.xio.undertow.ActHttpHandler.handleRequest(ActHttpHandler.java:30)
	at io.undertow.server.Connectors.executeRootHandler(Connectors.java:197)
	at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:759)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
	at java.util.TreeMap.getEntry(TreeMap.java:342)
	at java.util.TreeMap.get(TreeMap.java:273)
	at act.xio.undertow.UndertowRequest.paramVal(UndertowRequest.java:64)
	at act.app.ActionContext.paramVal(ActionContext.java:177)
	at act.handler.builtin.controller.impl.ReflectedHandlerInvoker.params(ReflectedHandlerInvoker.java:267)

In the end I found the root cause is the two new method I've added to the controller class:

    @OnAppEvent(AppEventId.EVENT_BUS_INITIALIZED)
    public static void onAppEventBusInitialized() {
        JobLog.log("onAppEventBusInitialized called");
    }

    @OnAppEvent(value = AppEventId.EVENT_BUS_INITIALIZED, async = true)
    public static void onAppEventBusInitializedAsync() {
        JobLog.log("onAppEventBusInitializedAsync called");
    }

So the story is the bytecode scanner sensed the OnAppEvent annotation and start to schedule the call to the method. And the event is AppEventId.EVENT_BUS_INITIALIZED which is a very early event, and it happens before the class loader begin to scan the bytecode. Thus the framework will trigger the job immediately. Hence it trigger the load of the class by when the meta info of the class is not ready yet. So the result is the Controller class get an early born without the correct enhancement happening.

Got-cha:

  • Do not put the @OnAppEvent(...) annotation inside a Controller class, or Mailer class.