JSF 2 equivalent of IBM's hx:scriptCollector pre- and postRender - jsf

I am migrating an old JSF application from WebSphere to JBoss: the old version uses an IBM implementation of JSF. My question concerns the following component:
<hx:scriptCollector id="aScriptCollector"
preRender="#{aBean.onPageLoadBegin}" postRender="#{aBean.onPageLoadEnd}">
To reproduce the preRender behavior in JSF 2 I use a binding for the form (s. here). My questions:
1) Do you know a trick for simulating postRender in JSF 2?
2) Do you think is the trick I am using for preRender "clean"?
Thanks a lot for your help!
Bye

Closest what you can get to achieve exactly the same hooks is
<f:view beforePhase="#{bean.beforePhase}" afterPhase="#{bean.afterPhase}">
with
public void beforePhase(PhaseEvent event) {
if (event.getPhaseId == PhaseId. RENDER_RESPONSE) {
// ...
}
}
public void afterPhase(PhaseEvent event) {
if (event.getPhaseId == PhaseId. RENDER_RESPONSE) {
// ...
}
}
The preRender can be achieved in an easier manner, put this anywhere in your view:
<f:event type="preRenderView" listener="#{bean.preRenderView}" />
with
public void preRenderView(ComponentSystemEvent event) {
// ...
}
(the argument is optional, you can omit it if never used)
There's no such thing as postRenderView, but you can easily create custom events. E.g.
#NamedEvent(shortName="postRenderView")
public class PostRenderViewEvent extends ComponentSystemEvent {
public PostRenderViewEvent(UIComponent component) {
super(component);
}
}
and
public class PostRenderViewListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().publishEvent(context, PostRenderViewEvent.class, context.getViewRoot());
}
}
which you register in faces-config.xml as
<lifecycle>
<phase-listener>com.example.PostRenderViewListener</phase-listener>
</lifecycle>
then you can finally use
<f:event type="postRenderView" listener="#{bean.postRenderView}" />
with
public void postRenderView(ComponentSystemEvent event) {
// ...
}

Related

Disable phaselistener in production mode

I have set up few things quite useful for our app. Useful in development mode, but not in production. For exemple, I registered this phaselistener
<lifecycle>
<phase-listener>org.primefaces.component.lifecycle.LifecyclePhaseListener</phase-listener>
</lifecycle>
In production mode, there is no need to have this, so I was wondering how you guys with JSF app are you disabling things by taking account the PROJECT_STAGE state in production mode?
PS: In xhtml pages, I'm aware it is easy as I can decide to not render component by using #{facesContext.application.projectStage eq 'Development'}
You can register the PhaseListener programatically instead of via faces-config.xml, for example:
#ManagedBean(eager = true)
#ApplicationScoped
public class ApplicationBean {
#PostConstruct
private void initialize() {
LifecycleFactory factory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
Lifecycle lifecycle = factory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
if (ProjectStage.Development.equals(getProjectStage())) {
lifecycle.addPhaseListener(new PhaseListenerImpl());
}
}
private ProjectStage getProjectStage() {
FacesContext fc = FacesContext.getCurrentInstance();
Application appl = fc.getApplication();
return appl.getProjectStage();
}
}
(cf. http://javaevangelist.blogspot.de/2012/05/jsf-2-tip-of-day-programmatic.html)
Using system event listener (See https://www.tutorialspoint.com/jsf/jsf_applicationevents_tag.htm):
public class PostConstructApplicationListener implements SystemEventListener {
#Override
public void processEvent(SystemEvent event) {
if (event instanceof PostConstructApplicationEvent) {
setupLifeCycleListener();
}
}
/**
* Add <code>org.primefaces.component.lifecycle.LifecyclePhaseListener</code> in case PROJECT_STAGE is not set to "Production"
*/
private void setupLifeCycleListener() {
if (Faces.getApplication().getProjectStage() != ProjectStage.Production) {
LifecycleFactory factory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
Lifecycle lifecycle = factory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
lifecycle.addPhaseListener(new LifecyclePhaseListener());
}
}
#Override
public boolean isListenerForSource(Object source) {
return source instanceof Application;
}
}
You can do so by extending the Primefaces LifecyclePhaseListener and returning null from getPhaseId() method if not in development mode.
public class LifecycleForDevelopmentPhaseListener extends LifecyclePhaseListener {
#Override
public PhaseId getPhaseId() {
if (!FacesContext.getCurrentInstance().isProjectStage(ProjectStage.Development)) {
return null;
}
return super.getPhaseId();
}
}
This will bypass the phase listener

How to thread-safely share an attribute between the beforePhase() and the afterPhase() methods of a PhaseListener?

I need to share an attribute between the beforePhase() and the afterPhase() methods of my PhaseListener, for a same JSF request.
Is the following snippet thread-safe?
public class MyPhaseListener implements PhaseListener {
private MyObject o = null;
#Override
public void beforePhase(PhaseEvent event) {
if (condition) {
o = new MyObject();
}
}
#Override
public void afterPhase(PhaseEvent event) {
if (o != null) {
o.process();
o = null;
}
}
#Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
}
If not, what are other solutions?
This is definitely not threadsafe. There's only one phase listener instance applicationwide which is shared across multiple requests. Basically, a phase listener is like an #ApplicationScoped managed bean.
Just set it as a context attribute.
public class MyPhaseListener implements PhaseListener {
#Override
public void beforePhase(PhaseEvent event) {
if (condition) {
event.getFacesContext().setAttribute("o", new MyObject());
}
}
#Override
public void afterPhase(PhaseEvent event) {
MyObject o = (MyObject) event.getFacesContext().getAttribute("o");
if (o != null) {
o.process();
}
}
#Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
}
You could use ThreadLocal for this, but it tends to have issues in environments having different classloaders, to name it: memory leak. Be sure to check for that in the given environment...
Also, you should make it sure that if the processing can be interrupted (e.g. exception...) between the beforePhase() and afterPhase() methods, the ThreadLocal should be handled appropriately...
This is what it would look like:
public class MyPhaseListener implements PhaseListener {
//if null is a valid value, no initial setting is needed
private ThreadLocal<Object> myStateObject = new ThreadLocal<Object> ();
#Override
public void beforePhase(PhaseEvent event) {
//might be needed, to guarrantee no residue from an aborted processing is in there
myState.set(null);
if (condition) {
myState.set(<Object representing the state>);
}
}
#Override
public void afterPhase(PhaseEvent event) {
try {
Object stateObject = myState.get();
if (stateObejct!=null) {
//do what you have to
}
} finally {
//to be sure
myState.remove();
}
}
}
In this article the author uses ThreadLocal too...
Also, this article is also a great eye-opener, explaining why not to share mutable instance-level information:
One thing to remember though, is that PhaseListener instances are application-wide Singletons that are referenced by the JSF Lifecycle, which itself is an application-wide Singleton.
EDIT just saw Boolean got updated to Object, adjusted example

Can't keep faces message after navigation from preRender

in my preRender code for a page i add faces message then make navigation to another page as follows:
if(error){
addMessageToComponent(null,"AN ERROR HAS OCCURRED");
FacesContext.getCurrentInstance().getExternalContext().getFlash()
.setKeepMessages(true);
navigateActionListener("myoutcome");
}
and the util methods for adding message and navigation are:
public static String getClientId(String componentId)
{
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
UIComponent c = findComponent(root, componentId);
return c.getClientId(context);
}
public static UIComponent findComponent(UIComponent c, String id)
{
if (id.equals(c.getId())) { return c; }
Iterator<UIComponent> kids = c.getFacetsAndChildren();
while (kids.hasNext())
{
UIComponent found = findComponent(kids.next(), id);
if (found != null) { return found; }
}
return null;
}
/**
* #param componentId
* : the id for the jsf/primefaces component without formId:
* prefix. <br>
* if you use null then the message will be added to the
* h:messages component.
**/
public static void addMessageToComponent(String componentId, String message)
{
if (componentId != null)
componentId = GeneralUtils.getClientId(componentId);
FacesContext.getCurrentInstance().addMessage(componentId,
new FacesMessage(message));
}
public static void navigateActionListener(String outcome)
{
FacesContext context = FacesContext.getCurrentInstance();
NavigationHandler navigator = context.getApplication()
.getNavigationHandler();
navigator.handleNavigation(context, null, outcome);
}
but messages are not saved and so it doesn't appear after redirect.
please advise how to fix that.
The preRenderView event runs in the very beginning of the RENDER_RESPONSE phase. It's too late to instruct the Flash scope to keep the messages. You can do this at the latest during the INVOKE_APPLICATION phase.
Since there's no standard JSF component system event for this, you'd need to homebrew one:
#NamedEvent(shortName="postInvokeAction")
public class PostInvokeActionEvent extends ComponentSystemEvent {
public PostInvokeActionEvent(UIComponent component) {
super(component);
}
}
To publish this, you need a PhaseListener:
public class PostInvokeActionListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.INVOKE_APPLICATION;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().publishEvent(context, PostInvokeActionEvent.class, context.getViewRoot());
}
}
After registering it as follows in faces-config.xml:
<lifecycle>
<phase-listener>com.example.PostInvokeActionListener</phase-listener>
</lifecycle>
You'll be able to use the new event as follows:
<f:event type="postInvokeAction" listener="#{bean.init}" />
You only need to make sure that you've at least a <f:viewParam>, otherwise JSF won't enter the invoked phase at all.
The JSF utility library OmniFaces already supports this event and the preInvokeAction event out the box. See also the showcase page which also demonstrates setting a facesmessage for redirect.

Interceptor in JSF

I want to know if there is an interceptor in JSF (like we use in Spring), and how to do we implement it?
You could implement a PhaseListener for this. You could program them to listen on a specific JSF phase which you specify in the overridden getPhaseId() method. You can intercept on the before and after phase events by beforePhase() and afterPhase() methods.
The below example listens on the render response phase:
public class RequestInterceptor implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
#Override
public void beforePhase(PhaseEvent event) {
// Do your job here which should run before the render response phase.
}
#Override
public void afterPhase(PhaseEvent event) {
// Do your job here which should run after the render response phase.
}
}
To get it to run, you need to register it as a <phase-listener> in the <life-cycle> section of the faces-config.xml file. You can have multiple <phase-listener>s.
<lifecycle>
<phase-listener>com.example.RequestInterceptor</phase-listener>
</lifecycle>
You can specify PhaseId.ANY_PHASE in getPhaseId() to let the phase listener run on every single JSF phase (note that not necessarily all of them will always be executed, that depends on the request type). You can if necessary get the current phase ID in the before and after phase methods by PhaseEvent#getPhaseId().
public class PhaseDebugger implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
#Override
public void beforePhase(PhaseEvent event) {
System.out.println("Before phase " + event.getPhaseId());
}
#Override
public void afterPhase(PhaseEvent event) {
System.out.println("After phase " + event.getPhaseId());
}
}
Alternatively, a Filter should work equally good if you want a more global hook (and thus you're not exactly interested in JSF requests/responses and you do not need anything from the FacesContext).
#WebFilter("/*")
public class RequestInterceptor implements Filter {
#Override
public void init(FilterConfig config) {
// Initialize global variables if necessary.
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
// Do your job here which should run before the request processing.
chain.doFilter(request, response);
// Do your job here which should run after the request processing.
}
#Override
public void destroy() {
// Cleanup global variables if necessary.
}
}

How to implement a PhaseListener which runs at end of lifecycle?

How can I implement a PhaseListener which runs at end of the JSF lifecycle?
You need to implement the PhaseListener interface and hook on beforePhase() of the PhaseId_RENDER_RESPONSE. The render response is the last phase of the JSF lifecycle.
public class MyPhaseListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
#Override
public void beforePhase(PhaseEvent event) {
// Do your job here which should run right before the RENDER_RESPONSE.
}
#Override
public void afterPhase(PhaseEvent event) {
// Do your job here which should run right after the RENDER_RESPONSE.
}
}
To get it to run, register it as follows in faces-config.xml:
<lifecycle>
<phase-listener>com.example.MyPhaseListener</phase-listener>
</lifecycle>
Update the above phase listener is indeed applicaiton-wide. To have a phase listener for a specific view, use the beforePhase and/or afterPhase attributes of the <f:view>.
E.g.
<f:view beforePhase="#{bean.beforePhase}">
...
</f:view>
with
public void beforePhase(PhaseEvent event) {
if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
// Do here your job which should run right before the RENDER_RESPONSE.
}
}
A more JSF 2.0 way is by the way using the <f:event type="preRenderView">:
<f:event type="preRenderView" listener="#{bean.preRenderView}" />
with
public void preRenderView() {
// Do here your job which should run right before the RENDER_RESPONSE.
}
In jsf 2 you can use <f:phaseListener type="my.MyPhaseListener"> to hook MyPhaseListener for some facelet. MyPhaseListener should implement PhaseListener and override
afterPhase - with code to be run after end of phase
beforePhase - with code to be run before phase started
getPhaseId - PhaseId enum specifying name of phase for which the listener to be invoked
(PhaseId.RENDER_RESPONSE as last phase of lifecycle)

Resources