Can EL call a nonpublic implementor of a public interface? - jsf

Java:
public class MyBean {
...
public Handler getHandler(){
return new Handler(){
public void handle(ActionEvent e){...}
}
}
...
}
public interface Handler{
void handle(ActionEvent e);
}
xhtml:
<h:commandButton ... actionListener="#{myBean.handler.handle}"/>
I'm in a tomcat 6.0 environment. This is a common pattern in java, but it seems not to work with EL method bindings. I get an exception:
javax.faces.event.MethodExpressionActionListener processAction SEVERE): Received 'java.lang.IllegalAccessException' when invoking action listener '#{myBean.handler.handle}' for component 'j_id115'
javax.faces.event.MethodExpressionActionListener processAction SEVERE): java.lang.IllegalAccessException: Class org.apache.el.parser.AstValue can not access a member of class MyBean$1 with modifiers "public"
...

Yes it can, but you did a few things wrong.
First of all the #handle()-method has to be declared as public, because it is an implementation of the public method of your interface.
public class MyBean {
...
public Handler getHandler(){
return new Handler(){
public void handle(){...}
};
}
}
Second point is, that you are calling the Handler as your actionListener, but what you want is to call the #handle()-method:
<h:commandButton actionListener="#{myBean.handler.handle}"/>
You also should omit the ActionEvent from the method-signature in your interface (and implementation)
public interface Handler {
public void handle();
}

This was more subtle than I thought...
From java, there is no problem calling the public method in the inner class:
MyBean myBean = getMyBean();
Handler handler = myBean.getHandler();
handler.handle(event); // OK
Using reflection, it depends on how it's done. The method can be invoked as declared (1):
Method getHandlerMethod = MyBean.class.getMethod("getHandler");
Method handleMethod = getHandlerMethod.getReturnType().getMethod("handle", ActionEvent.class);
handleMethod.invoke(handler, event); // OK, invoking declared method works
Or it can be invoked as defined in the inner class (2):
Method handleMethod = handler.getClass().getMethod("handle", ActionEvent.class);
handleMethod.invoke(handler, event) // throws IllegalAccessException
Obviously, there's a third option, and it works (3):
Method handleMethod = handler.getClass().getDeclaredMethod("handle", ActionEvent.class);
handleMethod.invoke(handler, event) // OK
Unfortunately, my JSF environment (Tomcat 6.0 with JSF mojarra 1.2 and icefaces 1.8.2) implements approach (2) instead of (3) and therefore my example doesn't work.

It should work if you specify the handle() method as ActionListener e.g.
<h:commandButton ... actionListener="#{myBean.handler.handle}"/>
To specify an implementation class of ActionListener interface you can use the "f:actionListener" tag e.g.
<h:commandButton>
<f:actionListener type="com.bla.SomeActionListenerImplementation" />
</h:commandButton>
But this wouldn't work in your case because your MyBean class doesn't implement the ActionListener interface...

Related

Extending PartialViewContext with a RequestScoped bean in JSF

In a JSF project, we wrote our own PartialViewContext to listen to some events fired by pages beans:
#RequestScoped
public class OurPartialViewContext extends PartialViewContextWrapper
{
...
// called by cdi
#SuppressWarnings("unused")
private void listenForUpdate(#Observes OurRefreshEvent event)
{
...
And we wrote the matching factory, injecting it:
public class OurPartialViewContextFactory extends PartialViewContextFactory
{
#Inject
private OurPartialViewContext customPartialViewContext;
...
Problem is that in the newest versions of JSF, the empty constructor for PartialViewContextWrapper is deprecated, asking us to use another constructor with the wrapped object in parameter.
Currently, our PartialViewContext needs to be tied to the request scope, in order to be modified during the request by the observed events and to be used by a custom PartialResponseWriter we also wrote.
So our PartialViewContext currently both:
must have an empty constructor, as it is a #RequestScoped bean;
should not have an empty constructor, as it is deprecated for PartialViewContextWrapper which it inherits from.
How could we find a solution there?
We tried to remove it from the scope and build it in the Factory with a simple new OurPartialViewContext(), but then the #Observes methods are never called.
You are required to pass the wrapped instance into the constructor and to use getWrapped() over all place in delegate methods. Otherwise your application will most probably not work when you install other JSF libraries which also ship with their own PartialViewContext implementation such as OmniFaces and PrimeFaces. You would be effectively completely skipping the functionality of their PartialViewContext implementation. This mistake was previously observed in too many custom implementations of factory-provided classes. Hence the urge to mark the default constructor as #Deprecated so that the developers are forced to use the proper design pattern.
Your specific issue can be solved by simply refactoring the listenForUpdate() method into a fullworthy request scoped CDI bean, which you then inject in the factory who in turn ultimately passes it into the constructor of your PartialViewContext implementation.
Thus, so:
#RequestScoped
public class OurEventObserver {
public void listenForUpdate(#Observes OurRefreshEvent event) {
// ...
}
}
public class OurPartialViewContextFactory extends PartialViewContextFactory {
#Inject
private OurEventObserver observer;
public OurPartialViewContextFactory(PartialViewContextFactory wrapped) {
super(wrapped);
}
#Override
public PartialViewContext getPartialViewContext(FacesContext context) {
PartialViewContext wrapped = getWrapped().getPartialViewContext(context);
return new OurPartialViewContext(wrapped, observer);
}
}
public class OurPartialViewContext extends PartialViewContextWrapper {
private OurEventObserver observer;
public OurPartialViewContext(PartialViewContext wrapped, OurEventObserver observer) {
super(wrapped);
this.observer = observer;
}
// ...
}
Inside any of the overridden methods of OurPartialViewContext you can simply access the state of the observer, provided that the listenForUpdate() modifies some instance variables representing the state.

JSF 2.2 : a #NoneScoped bean, injected into another broader scoped Managed Bean is NULL in the #PostConstruct

I believe this question may have been answered in some other threads, but so far I still cannot manage to make it work with my configuration.
As far as I understand, a #NoneScoped bean, injected into another one, will live as long as the scope of the Acceptor Bean.
And so far, it it true... except that it seems the Bean is not yet available during the #PostConstruct method of the Acceptor Bean.
For example, let's say we have an base abstract bean BaseScopedBean with the following ManagedProperty injection:
public abstract class BaseScopedBean implements IBaseBean {
#ManagedProperty(value = "#{resourceBundleProvider}")
private ResourceBundleProvider resourceBundleProvider;
public void setResourceBundleProvider(ResourceBundleProvider resourceBundleProvider) {
this.resourceBundleProvider = resourceBundleProvider;
}
public ResourceBundleProvider getResourceBundleProvider() {
return this.resourceBundleProvider;
}
}
where ResourceBundleProvider looks just like this:
#ManagedBean ( name = "resourceBundleProvider")
#NoneScoped
public class ResourceBundleProvider {
public ResourceBundle getBundle(String bundleName) {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().getResourceBundle(context, bundleName);
}
public String getValue(String bundleName, String key) {
try {
return getBundle(bundleName).getString(key);
} catch (MissingResourceException e) {
return '!' + key + '!';
}
}
public String getValue(String bundleName, String key, Object... params) {
try {
return MessageFormat.format(getBundle(bundleName).getString(key), params);
} catch (MissingResourceException e) {
return '!' + key + '!';
}
}
}
And then, we define an #ApplicationScoped bean that extends BaseScopedBean, and tries to access the resourceBundleProvider during a #PostConstruct operation
#ManagedBean
#ApplicationScoped
public class MenuBean extends BaseScopedBean {
#PostConstruct
public void init() {
System.out.println(getResourceBundleProvider());
}
}
The System.out.println(resourceBundleProvider) in the #PostConstruct prints NULL
However, accessing the resourceBundleProvider later on, in a method invoked from a Facelet EL expression, returns a valid created instance, for example.
The question: Is this the expected behaviour ? I believe the resourceBundleProvider managed property should be already available in the #PostConstruct.
I'm using WildFly 8.2.0.Final with Apache Myfaces 2.2.7 , not the original Mojarra implementation.
Any ideas ?
Thanks a lot in advance !!
It turns out that , so far, with Apache MyFaces 2.2.7 ( and presumably 2.2.8 ) this seems to be a bug... that was previously fixed in 2.1.x versions !!
With Mojarra ( at least 2.2.12 ), the behavior is the expected one.

Using CDI in java application

I'm writing Java SE application that uses CDI.
I have bean definition:
public class BeanA {
#PostConstruct
public void init() {
System.out.println("INIT");
}
public void receive(#Observes String test) {
System.out.println("received: " + test);
}
}
Requirements:
- I need to have many instances of BeanA in application
- I'd like to use Event CDI mechanism to communicate with that objects
When I use #Dependent scope, then #PostConstruct of BeanA is called everytime a new message is received. When I use #Singleton or #ApplicationScope then I can't have many objects of BeanA type.
What is solution to my problem?

Pass a method expression to a custom component

I'm looking for a minimal example on how to pass a method expression to a custom component. I tried the following, but the responsible setter of my component is never called.
Bean for my view:
public String bar(){
//do stuff
return "";
}
My view:
<my:comp foo="#{bean.bar}" />
Bean for my component:
private static final String FOO = "foo";
public void setFoo(MethodExpression me){
//never called
getStateHelper().put(FOO, me);
}
public MethodExpression getFoo(){
//actually gets called
return (MethodExpression) getStateHelper().get(FOO);
}
Inside my component renderer, I call component.getFoo() and get a NPE.
You need to implement the ActionSource2 interface:
#FacesComponent(MyComponent.COMPONENT_TYPE)
public class Mycomponent extends UIComponentBase implements ActionSource2 {
// ...
}
Or, easier, to extend from UICommand class (which are also used by <h:commandXxx> components) which has all of those ActionSource2 methods already implemented the right way so that you don't need to repeat the job:
#FacesComponent(MyComponent.COMPONENT_TYPE)
public class Mycomponent extends UICommand {
// ...
}
Either way, you can use action="#{some method expression}" and even attach actionlisteners on it. Note that you can't rename the attribute name action. It really has to be action.
An open source example of such a custom component is the OmniFaces <o:commandScript>. The source code is available here.

JSF: <f:event> with custom events

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

Resources