Sylar
Sylar

Reputation: 2333

Howto measure action execution time in JSF?

I am looking for a way to measure the execution times of all my actions in my JSF application.

A hacky solution I found so far is to extend com.sun.faces.application.ActionListenerImpl, overwrite processAction, and call super.processAction in my implementation:

public class MyActionListener extends ActionListenerImpl {

            public void processAction(ActionEvent event) throws FacesException {
                   watch.start();
                    super.processAction(event);
                   watch.stop();
            }
}

Then I add my own ActionListener implementation to faces config:

<application>
        <action-listener>MyActionListener</action-listener>
</application

But this adds a dependency on jsf-impl and is hacky. Is there a better solution?

Upvotes: 1

Views: 726

Answers (2)

Sylar
Sylar

Reputation: 2333

The solution that worked best was to implement a ServletContextListener, implement contextInitialized, create a reflection proxy for the current default action listener and measure the time in the proxy's invocation handler.

@Override
    public void contextInitialized(ServletContextEvent sce) {
        // get JSF application factory
        ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
        Application application = applicationFactory.getApplication();
        ActionListener defaultActionListener = application.getActionListener();

        // create proxy for the default actionlistener
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        ActionListenerInvocationHandler actionListenerInvocationHandler = new ActionListenerInvocationHandler(defaultActionListener);
        @SuppressWarnings("rawtypes")
        Class[] interfaces = new Class[] { ActionListener.class };
        ActionListener actionListenerProxy = (ActionListener) Proxy.newProxyInstance(contextClassLoader, interfaces, actionListenerInvocationHandler);

        // set proxied actionListener as new default actionlistener
        application.setActionListener(actionListenerProxy);

    }

Upvotes: 0

BalusC
BalusC

Reputation: 1108742

You could use a PhaseListener instead and hook on PhaseId.INVOKE_APPLICATION.

public class MyPhaseListener implements PhaseListener {

    public PhaseId getPhaseId() {
        return PhaseId.INVOKE_APPLICATION;
    }

    public void beforePhase(PhaseEvent event) {
        watch.start();
    }

    public void afterPhase(PhaseEvent event) {
        watch.stop();
    }

}

Register it as <phase-listener> instead.

Note, I understand that your code is pseudo, but for the sake of completeness I would like to warn you that you need to realize that the very same single instance of the listener is shared among all threads/requests. You'd rather like to store the watch in the request map and definitely not as an instance variable.

Upvotes: 3

Related Questions