Reputation: 2333
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
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
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