JSF: <f:event> with custom events - jsf

I can't figure out if it is possible to use custom events using f:event. The book by Ed Burns suggests to ad the #NamedEvent annotation to the Event class and to use:
<f:event type="com.foo.bar.myEvent" listener="#{listener} />
but the event seems never to be instantiated.
From my point of view this makes sense, since the component does not know anything about the event, e.g. when to publish, so this might be useful for custom component authors only.
On the other hand, standard components should be able to publish the the event if derived from e.g. PostAddToViewEvent. Anyway, custom events seem to be never published by standard components.
Am I missing something? Is there a convenient way to use custom events with standard components?
Here is what I wanted to do:
<h:inputText id="input">
<f:event type="foo.bar.MyCustomEvent" />
</h:inputText>
public class MyCustomEvent extends javax.faces.event.PostAddToViewEvent {
}

yes you can for this you have to override some method in jsf render or component class
public class MyComponent extends HtmlInputText or public class MyRenderer extends TextRenderer
#Override
public void decode(FacesContext context, UIComponent component) {
super.decode(context, component);
String sourceName = context.getExternalContext().getRequestParameterMap().get("javax.faces.source");
if(sourceName != null && sourceName.equals(component.getClientId())){
component.queueEvent(new MyEvent(component));
}
}
but in MyEvent class you have to override some methods
#Override
public PhaseId getPhaseId() {
return PhaseId.INVOKE_APPLICATION;
}
which will define in which face this event will process (by default it is ANY_PHASE and event trigger in same phase in which it registered)
#Override
public boolean isAppropriateListener(FacesListener listener) {
return false;
}
if you have appropiate listener it must return true
if you have appropiate listener for MyEvent then JSF will call that listener's processAction(ActionEvent event) method when it will trigger event, otherwise it will call broadcast method in component class which has to be override by developer
#Override
public void broadcast(FacesEvent event) throws AbortProcessingException {
super.broadcast(event);
if(event instanceof MyEvent){
try {
processMyEvent(event);
} catch (Exception e) {
// TODO: handle exception
}
}
}
Even you can register any event by your own by using component queueEvent(FacesEvent event) method, it will regiester event and it get the phase in which it will trigger by getPhaseId() method in MyEvent class if getPhaseId() method is not overrided by devloper then it will trigger in same phase in which it registered

Related

Set property of another bean after action method is invoked

Mojarra 2.2
I have two beans`.
public class MyBean1{
private String myProperty1;
//GET, SET, CTOR
public void doAction(){
//assign something to myProperty1
}
}
public class MyBean2{
private String myProperty2;
//GET, SET, CTOR
}
Now, I need to assign the value of the property MyBean1::myProperty1 to MyBean2::myProperty2 after the doAction() method invocation by clicking a button:
<h:commandButton action="#{myBean1.doAction}">
<f:setPropertyActionListener target="#{myBean2.myProperty2}"
value="#{myBean1.myProperty1}" />
</h:commandButton>
But it doesn't work. And I figured out that it doesn't work due to the following reason:
Clicking a button causes ActionEvent to be broadcasted. It performs by this method (javax.faces.component.UICommand):
public void broadcast(FacesEvent event) throws AbortProcessingException {
super.broadcast(event); //1 <-------------------- HERE
if (event instanceof ActionEvent) {
FacesContext context = getFacesContext();
MethodBinding mb = getActionListener();
if (mb != null) {
mb.invoke(context, new Object[] { event });
}
ActionListener listener =
context.getApplication().getActionListener();
if (listener != null) {
listener.processAction((ActionEvent) event);
}
}
}
I've noticed that the ActionEvent broadcasting by clickng the button is being handled by two listeners and one that comes from super.broadcast(event) invokation at //1 is com.sun.faces.facelets.tag.jsf.core.SetPropertyActionListenerHandler.SetPropertyListener.
The handling is performed before invokation of the action method.
Of course, I can embed the action method invokation into the MyBean1:getMyProperty1() getter, so the myProperty1 field will be properly intialized, but it seems quite wierd to me. What is the right way to achieve that?

AbortProcessingException leaves stack trace in server log, how to disable this?

There is a commandButton on a page that asynchronously invokes some service, and when user clicks a button I want to verify service availability by checking special config from database. This config can be updated every minute. So if the service is not available, action of commandButton should not be executed.
So, as per Differences between action and actionListener, I'm checking service availability in actionListener and throw AbortProcessingException in case service is not available. So action will be skipped.
This works fine, but it leaves a stack trace in server's log. I don't want such behavior. Is it possible to handle the exception in such way that this will not leave mark in server logs, just like when ValidatorException is thrown? I'm using OmniFaces FullAjaxExceptionHandler, if that's relevant.
You can suppress the logging with a custom exception handler. As you're currently using OmniFaces FullAjaxExceptionHandler, you'd better extend it. In the handle() method, check if there's an exception and if it's an instance of AbortProcessingException. If so, then just ignore it and return directly from the exception handler.
public class YourExceptionHandler extends FullAjaxExceptionHandler {
public YourExceptionHandler(ExceptionHandler wrapped) {
super(wrapped);
}
#Override
public void handle() throws FacesException {
Iterator<ExceptionQueuedEvent> events = getUnhandledExceptionQueuedEvents().iterator();
if (events.hasNext() && events.next().getContext().getException() instanceof AbortProcessingException) {
return; // Ignore (and don't log).
}
super.handle(); // Continue to FullAjaxExceptionHandler.
}
}
Create a factory around it:
public class YourExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory wrapped;
public YourExceptionHandlerFactory(ExceptionHandlerFactory wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getExceptionHandler() {
return new YourExceptionHandler(getWrapped().getExceptionHandler());
}
#Override
public ExceptionHandlerFactory getWrapped() {
return wrapped;
}
}
In order to get this to run, register it as factory in faces-config.xml the usual way, replacing the FullAjaxExceptionHandlerFactory:
<factory>
<exception-handler-factory>com.example.YourExceptionHandlerFactory</exception-handler-factory>
</factory>

Insert/Add JSF component at render time

Is it possible to add a child component during render? If not what would be the best practice to add a child component dynamically in a JSF 1.2 Environment? Thanks
The better place where you can do that is in a PhaseListener implementation.
For instance the next code snippet samples how you can add a new component in to the view root:
public class ViewModifierPhaseListener implements
javax.faces.event.PhaseListener {
#Override
public void afterPhase(PhaseEvent event) {
}
// Just sampling add component on ViewRoot
#Override
public void beforePhase(PhaseEvent event) {
// Gets the target component from ViewRoot
UIViewRoot viewRoot = event.getFacesContext().getViewRoot();
UIComponent parent = viewRoot.findComponent("parentComponentId");
// UIComponents to create depend on JSF implementation,
// Try to use the available factories when suplied by the implementation
UIComponent child = Factory.getComponent("ComponentClassName");
// Customize the component, for instance it has to be disabled
child.getAttributes().put("disabled", true);
// Adds the fresh created component to the parent
parent.getChildren().add(child);
}
#Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
}
Please note that getPhaseId returns the RENDER_RESPONSE phase because in that phase is where you have the components tree complete.
Your phase listener definition has to be set in the faces-config.xml's lifecycle element like this:
<lifecycle>
<phase-listener>your.package.ViewModifierPhaseListener</phase-listener>
</lifecycle>
Or if you work with facelets you could define it in the template of the pages you want to be affected by your listener. This helps you to discriminate when to execute your PhaseListener.
<f:phaseListener type="your.package.ViewModifierPhaseListener"/>

JSF 2.0 How to create state-saving UIComponents?

I tried simply:
public class UIDemoComponent extends UIComponentBase {
private String someVariable; // this gets always cleared, getters/setters omitted
public UIDemoComponent() {
super(); // place breakpoint here
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
HtmlForm form = new HtmlForm();
form.setStyle("padding: 10px; background-color: blue;");
getChildren().add(form);
AjaxBehavior behavior = new AjaxBehavior();
behavior.setRender(Arrays.asList("#form"));
form.addClientBehavior("click", behavior);
}
}
I have registered a tag handler and succesfully inserted the component into page. However, when I click the blue form that is rendered, JSF re-creates the component (breakpoint in the constructor is caught). The effect of this is that any instance variables are lost. How is one supposed to save data into components if they always get re-created?
I tried overriding and inspecting the state staving mechanisms of StateHolder and PartialStateHolder wihout luck:
#Override
public Object saveState(FacesContext context) {
return super.saveState(context); // breakpoint
}
#Override
public void restoreState(FacesContext context, Object state) {
super.restoreState(context, state); // breakpoint
}
JSF is executing the saveState when page and components are created, but restoreState is never called. Actually, when the AJAX request is being processed, a new instamnce of UIDemoComponent is created but saveState method is called again, instead of restoreState.
How to create such a state-saving component (that retains the instance fields over AJAX requests)?
Seems like JSF is running some pre-checks on state object and not executing restoreState at all if custom fields are not entered. Only after actually inserting custom values into state object, the restoreState gets called.
For example:
#Override
public Object saveState(FacesContext context) {
Object[] rtrn = new Object[2];
rtrn[0] = super.saveState(context);
rtrn[1] = "dummy";
return rtrn;
}
After this, the restoreState gets called and property fields can be restored as wanted.

What is the 'official' SEAM way of listening to JSF-SEAM phases?

A simple question on the title.
My case is that I want to listen to "before RENDER_RESPONSE" phase, and alter some components internal state.
Is PhaseListener the "right way" to do this in SEAM applications?
If you want alter JSF component internal state, rely on JSF phase listener. Seam way of declaring JSF phase listener is shown bellow
#Name("applicationPhaseListener")
#Scope(ScopeType.APPLICATION)
public class ApplicationPhaseListener {
/**
* Called TRANSPARENTLY by Seam
*/
#Observer("org.jboss.seam.beforePhase")
public void beforePhase(PhaseEvent event) {
}
/**
* Called TRANSPARENTLY by Seam
*/
#Observer("org.jboss.seam.afterPhase")
public void afterPhase(PhaseEvent event) {
}
}
But if you want to alter Seam contextual component state, use
#Name("applicationPhaseListener")
public class ApplicationPhaseListener {
#Observer("applicationListener")
public void applicationListener() {
}
}
You can
Call your event programatically
Events.instance().raiseEvent("applicationListener");
By using #RaiseEvent annotation which is placed aboved some action method
#RaiseEvent("applicationListener")
public void doSomething() {
}
pages.xml
<page id="<PAGE_ID_GOES_HERE>">
<raise-event type="applicationListener"/>
</page>

Resources