I have figured out how to log when a request is an ajax request and which page it is from, in a filter.
What I would really like to do is log what the ajax request is actually for. Such as the name of the method being called by the ajax (eg "findAddress" in this call:<p:ajax process="contactDetails" update="@form" listener="#{aboutYouController.findAddress}" ....
)
How can I do this? My app has many ajax requests and I want to log which are being triggered.
public class TrackingFilter implements Filter {
private static Logger LOG = Logger.getLogger(TrackingFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
String pageHit = req.getRequestURI().substring(req.getContextPath().length()+1).replace(".xhtml", "");
if(!pageHit.contains("javax.faces.resource")){ // if is a url we want to log
if ("partial/ajax".equals(req.getHeader("Faces-Request"))) {
LOG.trace("ajax on URI: " + req.getRequestURI());
}
This information is only available in the JSF component tree. The JSF component tree is only available after view build time. A view is only built when the request has been served by
FacesServlet
. Thus, a servlet filter is way too early as it runs before any servlet.You'd better run the code after the restore view phase of a postback. The JSF component tree is guaranteed to be available during that moment. You can use
FacesContext#isPostback()
to check if the current request is a postback. You can usePartialViewContext#isAjaxRequest()
to check if the current request is an ajax request. You can use the predefinedjavax.faces.source
request parameter to obtain the client ID of the source component of the ajax request. You can use the predefinedjavax.faces.behavior.event
request parameter to obtain the ajax event name (e.g.change
,click
,action
, etc).Obtaining the associated behavior listeners is in turn a story apart. This is easy on
ActionSource2
components (e.g.<h|p:commandButton action="#{...}">
) as theMethodExpression
is just available byActionSource2#getActionExpression()
. However, this isn't easy onBehaviorBase
taghandlers (e.g.<f|p:ajax listener="#{...}">
) as this API doesn't have any method likegetBehaviorListeners()
. There are only methods to add and remove them, but not to obtain a list of them. So some nasty reflection trickery is necessary to access theprivate
field with those listeners whose name is JSF implementation specific. In Mojarra it'slisteners
and in MyFaces it's_behaviorListeners
. Both are fortunately assignable fromList
and it's the only field of that type, so we could just check for that. Once having hand of theBehaviorListener
instance, then you still need to do another reflection trickery to obtain theMethodExpression
field of that instance. Yuck.All in all, here's how the trickery look like in flavor of a
PhaseListener
listening onafterPhase
ofRESTORE_VIEW
:In order to get it to run, register as below in
faces-config.xml
:Above is tested and compatible with Mojarra and PrimeFaces and theoretically also compatible with MyFaces.
Update: in case you're using JSF utility library OmniFaces, or are open to, since version 2.4 you can use the new
Components#getCurrentActionSource()
utility method to find out the current action source component andComponents#getActionExpressionsAndListeners()
to get a list of all action methods and listeners registered on a given component. This is also useable on regular (non-ajax) requests. With that, the abovePhaseListener
example can be reduced as below: