JSF 2.0 Injecting managed bean with a different scope - jsf

I have a controller that is stateless which takes care of processing forms. This is defined as ApplicationScoped. On my page I have a form associated to a backing bean defined as a ViewScoped.
The error I got when I want to process the form:
serverError: class com.sun.faces.mgbean.ManagedBeanCreationException Unable to create managed bean myController. The following problems were found:
- The scope of the object referenced by expression #{myFormBean}, view, is shorter than the referring managed beans (myController) scope of application
In my form:
Name: <h:inputText value="#{myFormBean.name}" id="name" />
<h:commandButton value="Save Name" action="#{myController.processForm}">
<f:ajax render="nameResult" />
</h:commandButton>
Your name is <h:outputText value="#{myFormBean.name}" id="nameResult"/>
The controller:
#ManagedBean
#ApplicationScoped
public class MyController {
#ManagedProperty("#{myFormBean}")
private MyFormBean myBean;
public void processForm() {
System.out.println(myBean.getName());
// Save current name in session
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
"name", myBean.getName());
}
}
The backing bean:
#ManagedBean
#ViewScoped
public class MyFormBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I could solve that by setting the controller as SessionScoped but it’s not a clean way since the controller is stateless, so I don’t need one controller for each session. One controller for the whole application should be sufficient.
I have a Spring MVC background, that’s why I am confused on how to do things with JSF 2.0

There is a flaw in your design. Your controller is not stateless at all. It has a property which is different for each request/view, namely the myBean. If it was supported, then every new request/view would override the previously set one and the enduser will face the property value of a completely different enduser. This leads to problems in high concurrent situations.
You need to make it request/view scoped instead of application scoped. Still then, I believe that you have to approach it completely different. You're manually setting an attribute in the session scope in the action method instead of setting it as a property of an (injected) session scoped bean. How to solve it properly depends on the functional requirement which is not clear from the question.

I have JSF Managed Beans with different scopes referencing each other, and I have found Spring adequately addresses my needs. The key to success was the Spring AOP that proxifies bean references and gives me more flexible autowiring. I think it would make sense for you to mix JSF and Spring similarly to achieve your goals.
I don't use the JSF scope declaration annotations to declare my beans. Instead, I use Spring to declare my beans, assign their scopes, and specify that I want the oddly-scoped beans to have aop proxies generated for them (so they can get autowired appropriately whenever they are referenced.) I use the spring el-resolver to make my Spring beans addressable as JSF2 managed beans in EL.
I don't use view scope in my program, I use session scope with request-scoped beans referencing them. But, I suspect my approach could be adapted for your view-scoped beans as well.
I don't use annotations to declare my beans, I use XML for declaring my beans and their scopes. I just find it handy to have all of my bean declarations cataloged in one place. I'm sure there's a pure annotation-based approach to achieve what I've got. I do use the #Autowired annotation in my beans to indicate where references to other beans should be wired in. This keeps my XML configuration short, eliminates the need for getter/setters, and gives me a little more Java-side flexibility than I've been able to get from going pure XML.
Finally, I gave myself a custom "SmartSession" scope. This is essentially just like session scope, except, it re-autowires every time a bean is pulled out of session (this guards against bean replicas appearing unwired in a failover scenario in a cluster.)
I have come to the conculsion that for session- (and I presume view-) scoped beans to work, you need to make the bean Serializable and mark any #Autowired fields as transient. The SmartSession gives me the confidence in that context to be assured that I stay autowired even in exceptional cases. I based my SmartSession custom scope idea off of this answer: Initialize already created objects in Spring as well as internet sources for how to write custom scopes.
Here's some code snippets to hopefully give you some ideas -
Sample Session-scoped bean:
public class UserProfileContainer implements Serializable {
private static final long serialVersionUID = -6765013004669200867L;
private User userProfile;
public void setUserProfile(User userProfile) {
this.userProfile = userProfile;
}
public User getUserProfile() {
return this.userProfile;
}
}
Bean that references my smartSession-scoped bean:
public class KidProfileEditor implements Serializable {
private static final long serialVersionUID = 1552049926125644314L;
private String screenName;
private String password;
private String confirmPassword;
private String firstName;
private String lastName;
private String city;
private String state;
private String notes;
private String country;
#Autowired
private transient UserProfileContainer userProfileContainer;
}
Snippet from my applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:jms="http://www.springframework.org/schema/jms"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-lazy-init="true" >
<!-- BOILERPLATE magic AOP setup tags -->
<context:annotation-config />
<context:component-scan base-package="com.woldrich.kidcompy" />
<aop:aspectj-autoproxy />
<!-- JSF2+Spring custom scope configurations -->
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="safetySession">
<bean class="com.woldrich.kidcompy.faces.util.SpringSafetySessionScope"/>
</entry>
</map>
</property>
</bean>
<bean id="userProfileContainer" class="com.woldrich.kidcompy.auth.UserProfileContainer" scope="safetySession">
<aop:scoped-proxy />
</bean>
<bean id="kidProfileEditor" class="com.woldrich.kidcompy.faces.actionview.KidProfileEditor" scope="request" />
</beans>
web.xml snippet:
<web-app xsi:schemaLocation="http://java.sun.com/xml/ns/javaee /WEB-INF/includes/schema/web-app_2_5.xsd" id="KidCompy" version="2.5" metadata-complete="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee">
<distributable/>
<context-param>
<description>Allows the Spring Context to load multiple application context files</description>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/mainApplicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
</web-app>
faces-config.xml snippet:
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee /WEB-INF/includes/schema/web-facesconfig_2_0.xsd"
version="2.0">
<application>
<el-resolver>
org.springframework.web.jsf.el.SpringBeanFacesELResolver
</el-resolver>
</application>
</faces-config>

Related

Inject within Guice to ManagedBean JSF

i have a problem injecting within a Guice (#Inject) a repository class to a #ManagedBean class (JSF), which is using EntityManager to get info from db. I've read a lot of old articles with strange ways to do it, but nothing worked. Here is the code:
public class InitConfigListener extends GuiceServletContextListener {
#Override
protected Injector getInjector() {
return Guice.createInjector(new ServletModule() {
#Override
protected void configureServlets() {
install(new JpaPersistModule("db-manager"));
filter("/*").through(PersistFilter.class);
}
}, new RepositoryModule());
}
}
public class RepositoryModule extends AbstractModule {
public void configure() {
bind(IBookRepository.class).to(BookRepository.class).asEagerSingleton();
bind(IUserRepository.class).to(UserRepository.class).asEagerSingleton();
}
}
#ManagedBean
#ViewScoped
public class BooksView {
private List<Book> bookList;
private IBookRepository booksRepository;
public BooksView() { }
#Inject
public BooksView(IBookRepository booksRepository) {
this.booksRepository = booksRepository;
}
#PostConstruct
public void initBookList() {
bookList = booksRepository.getAll();
}
public List<Book> getBookList() {
return bookList;
}
public void setBookList(List<Book> bookList) {
this.bookList = bookList;
}
}
EntityManager was injected in repositories. After bindings of repositories, #Inject in class BooksView didn't executed, #PostConstruct was and repository injected was null.
Thanks for help.
Guice works a little bit differently to CDI as BalusC mentioned.
What is happening is that the CDI is executing properly and calling the post constructs on the methods.
JSF Classes are also not Guice managed but CDI Managed so you need to call guice from your front end (which is correct) unless you are using servlet based web fronts, where you use guice-servlet can get around this. Method annotations also require aopalliance.
You can initialize Guice through a filter (example : https://github.com/GedMarc/JWebSwing-Undertow/blob/master/src/main/java/za/co/mmagon/jwebswing/undertow/UndertowJWebSwingHandlerExtension.java) for undertow based containers, or as a ServletContextListener as mentioned here (https://github.com/google/guice/wiki/ServletModule) if you would like to startup before a #PostConstruct in a JSF view.
An interesting thing as well, is in an EE environment, the #PostConstruct timing is different in #Singleton's. In standalone environments you can place the injection builder in this location to load injections for ejb and servlets (from a module in the war)
You can use Guice as long as you configure JSF Appropriately.
In a nutshell, to do this you need to enable two things
- Set a custom application factory
- Configure EL to get the bindings from Guice
Set the application factory via the faces-config.xml file
<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd">
<factory>
<application-factory>com.jwebmp.guicedservlets.jsf.FacesApplicationFactoryWrapper</application-factory>
</factory>
</faces-config>
A servlet context parameter (web.xml) or for undertow/embeddeds a deployment info
com.sun.faces.facesInitializerMappingsAdded=true
deploymentInfo.addServletContextAttribute(RIConstants.FACES_INITIALIZER_MAPPINGS_ADDED, Boolean.TRUE)
.addListener(new ListenerInfo(com.sun.faces.config.ConfigureListener.class));
Then in your module -
Use your choice mechanism of scanning, and bind all #javax.inject.Named and #ManagedBean to keys you are going to use in the Guice EL Resolver replacing the CDI/CustomInject/any form on injection that JSF uses with Guice.
Module Binder Example Here
Guice EL Resolver Example Here
Faces Application Wrapper
Optional View Scope Impl
Below is a bundled implementation which you can simply attach, using the Guiced EE framework for JDK 11 and up in modular JPMS (backwards compatible to 1.8)
Note * because of the invalid ServiceProviders in the javax.faces library, the framework references a local javax.faces which at time of updating this was at 2.3.9 with the providers removed to enable you building your JRE with JLink or simply executing your application in JPMS
https://github.com/GedMarc/Guiced-Servlets-JSF
https://search.maven.org/artifact/com.jwebmp.guicedee.servlets/guiced-servlets-jsf/0.68.0.1/jar
<dependency>
<groupId>com.jwebmp.guicedee.servlets</groupId>
<artifactId>guiced-servlets-jsf</artifactId>
<version>0.68.0.1</version>
</dependency>
Then you can just use #ManagedBean and/or #Named (JSF2.3) appropriately with all injections in pretty much any environment, except Servlet 4 which guice-servlet does not yet support

Xpages, faces-config, managed beans, and scoped variables

I have some properties in my database that will rarely if ever change, but are used pervasively throughout. It seems that this might be something I could do with a managed bean?
<managed-bean>
<managed-bean-name>bu</managed-bean-name>
<managed-bean-class> </managed-bean-class>
<managed-bean-scope>application</managed-bean-scope> </managed-bean>
I want to end up with a list of values in applicationScope.bu.
Do I just write a simple java implementation? Is there an easier or better way?
Your faces-config, to properly implement a managed bean, should include a block like you've put in, but you are missing the <managed-bean-class> value. This is what class will be created in memory (in your case, applicationScope). If your bean class has methods to return the values you're looking for, you're good. I'm a fan of beans (managed or POJO) and use this approach for a few applications.
[Edit]
As Steve points out in his answer, your need for accessibility, and quantity, of your configuration may drive how you store those values. How you interface with them may be different, and I lean heavily towards managed bean use.
For small amounts of values that need to be store for a given application, I like to set those in XSP Properties, then I can reference those via XSPContext.getProperty('xsp.property.name');.
For more complex things, I tend to create a config document, which I use as analogous to a profile document, which I then load from / store to the values.
[/Edit]
If you're looking for a good place to start with managed beans, or a quick reference for sanity checking, I'd recommend checking out Per Henrik Lausten's blog post on the subject.
The short version is, to have a managed bean, you must have:
a Java class
built with private properties (values)
which are exposed by public getter/setter methods
implements Serializable (java.io.Serializable)
contains an argument-less constructor (must take no parameters to be built the first time; not to mean that it can't go find values elsewhere)
an entry in faces-config (much like you've outlined)
binding via
EL with #{beanName.propertyName} (EL uses camel cased property names, converting, so the getter method of getPropertyName() is notated in EL as first shown
SSJS with #{javascript:return beanName.getPropertyName()} (full method invocation
You can then use your managed bean's name (in your case "bu") in either Expression Language or SSJS blocks. In the EL binding, note that the properties use camel casing to be accessed; so a property of myProperty is exposed by getMyProperty but looks like #{bu.myProperty}. You can also use it in SSJS, by the full method name, such as #{javascript:return bu.getMyProperty();}
Example
Here's one I have in my demo app is for a data object. It's in viewScope so it lives with the life cycle of a given XPage.
faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<managed-bean>
<managed-bean-name>gotHouse</managed-bean-name>
<managed-bean-scope>view</managed-bean-scope>
<managed-bean-class>com.westeros.model.HouseModel</managed-bean-class>
</managed-bean>
<!--AUTOGEN-START-BUILDER: Automatically generated by IBM Domino Designer. Do not modify.-->
<!--AUTOGEN-END-BUILDER: End of automatically generated section-->
</faces-config>
HouseModel.java
*I've modified this here for brevity as an example.
package com.westeros.model;
//...
public class HouseModel implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
//...
public HouseModel(){}
public void load(String unid){
// do some load things from the doc's UNID
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
//...
}
house.xsp (House XPage) *excerpted for brevity of example
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<!-- truncating -->
<div
class="form-group">
<xp:label
for="name"
value="Name" />
<xp:inputText
id="name"
value="#{gotHouse.name}" />
</div>
<!-- truncating -->
</xp:view>
You can use managed properties of managed beans.
For example this little bean...
package ch.hasselba.xpages;
import java.io.Serializable;
#SuppressWarnings("serial")
public class MyBean implements Serializable {
private String dbName;
public void setDbName(String dbName) {
this.dbName = dbName;
}
public String getDbName() {
return dbName;
}
}
... can be initialized in the faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<managed-bean>
<managed-bean-name>myBean</managed-bean-name>
<managed-bean-class>ch.hasselba.xpages.MyBean</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
<managed-property>
<property-name>dbName</property-name>
<value>MyName</value>
<property-class>java.lang.String</property-class>
</managed-property>
</managed-bean>
</faces-config>
As soon you acccess the bean, the value is "there". F.e. a label on your XPage...
<xp:label
value="#{myBean.dbName}"
id="label1">
</xp:label>
... will be then displaying MyName.
Managed properties can contain other beans, and access runtime properties (f.e. request parameters) which makes them extremly powerful.
If I understand your question correctly, I don't think the properties of a managed bean would be where you would want to store application properties.
I would just stick them in application scope on the landing page of your application.

View scope: java.io.NotSerializableException: javax.faces.component.html.HtmlInputText

There is an error each time a button calls an action from the backing-bean.
Only applies to beans with a view scope and I haven't found a way to fix it without regression over other modules in the code.
DefaultFacele E Exiting serializeView - Could not serialize state: javax.faces.component.html.HtmlInputText
java.io.NotSerializableException: javax.faces.component.html.HtmlInputText
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
Or also:
com.ibm.ws.webcontainer.servlet.ServletWrapper service SRVE0014E: Uncaught service() exception
root cause Faces Servlet: ServletException: /jspFiles/jsf/Deployments/editQueue.faces No saved view state could be found for the view identifier /jspFiles/jsf/Deployments/editQueue.faces
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:205)
Caused by: javax.faces.application.ViewExpiredException: /jspFiles/jsf/Deployments/editQueue.faces No saved view state could be found for the view identifier: /jspFiles/jsf/Deployments/editQueue.faces
at org.apache.myfaces.lifecycle.RestoreViewExecutor.execute (RestoreViewExecutor.java:128)
faces-config.xml
<managed-bean>
<managed-bean-name>pc_EditQueue</managed-bean-name>
<managed-bean-class>pagecode.jspFiles.jsf.deployments.EditQueue</managed-bean-class>
<managed-bean-scope>view</managed-bean-scope>
<managed-property>
<property-name>queueDeploymentBean</property-name>
<value>#{queueDeploymentBean}</value>
</managed-property>
</managed-bean>
web.xml
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</param-name>
<param-value>true</param-value>
</context-param>
bean:
#ManagedBean
#ViewScoped
public class EditQueue extends PageCodeBase implements Serializable {
private static final long serialVersionUID = -1L;
public String doButtonAddAction() {
// calls manager (long)
FacesUtil.setViewMapValue("queueDeploymentBean", queueDeploymentBean);
return "";
}
I read this suggestion to set SERIALIZE_STATE_IN_SESSION to false and indeed this solution works for this view scope bean. However this fix comes at a high cost: many existing modules in the application don't work anymore so I cannot use this fix there. Some of the regression observed are:
// returns null must be changed with FacesUtil.getSessionMapValue("userId");
getSessionScope().get("userId");`
// returns null must be changed with FacesUtil.getViewMapValue("linkerBean");
linkerBean = (Linker) getManagedBean("linkerBean");`
// NPE so must be changed with FacesContext.getCurrentInstance().addMessage(...)
getFacesContext().addMessage(...)`
So my questions are:
why the NotSerializableException even though the bean implements Serializable ?
is there a way to apply the SERIALIZE_STATE_IN_SESSION param over only a subset of the beans or not ?
is there another solution to have my view scope bean to work (without having to change them to request scope or else) ?
WebSphere 8.0.0.3,
Java 1.6.0,
JSF 2.0,
RichFaces 4.2.3.Final
why the NotSerializableException even though the bean implements Serializable ?
Not only the bean needs to be serializable, but all of its properties (and all their nested properties etc) must also be serializable. The name of the offending non-serializable class can easily be found in the exception message:
java.io.NotSerializableException: javax.faces.component.html.HtmlInputText
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
This suggests that you're binding a <h:inputText> component to the bean like below:
<h:inputText binding="#{bean.fooInput}" ...>
private UIComponent fooInput;
This is indeed illegal when the bean is not in request scope. UIComponent instances are request scoped and may not be shared across multiple requests. Moreover, UIComponent instances are not serializable. Only their state is, but JSF will worry about that all by itself.
You must remove the fooInput property and you need to look for a different solution for the problem for which you incorrectly thought that binding the component to a view scoped bean would be the right solution.
If you intend to access it elsewhere in the view, e.g. #{bean.fooInput.value}, then just bind it to the Facelet scope without the need for a bean property:
<h:inputText binding="#{fooInput}" ...>
It'll be available elsewhere in the same view via #{fooInput.xxx}.
<h:inputText ... required="#{empty fooInput.value}" />
If you intend to set some component attribute programmatically inside the bean, e.g. fooInput.setStyleClass("someClass"), or fooInput.setDisabled(true), then you should be binding the specific attribute in the view instead of the whole component:
<h:inputText ... styleClass="#{bean.styleClass}" />
...
<h:inputText ... disabled="#{bean.disabled}" />
If you are absolutely positive that you need to get a hand of whole UIComponent instance in the bean for whatever reason, then manually grab it in method local scope instead of binding it:
public void someMethod() {
UIViewRoot view = FacesContext.getCurrentInstance().getViewRoot();
UIComponent fooInput = view.findComponent("formId:fooInputId");
// ...
}
But better ask a question or search for an answer how to solve the concrete problem differently without the need to grab a whole component in the backing bean.
See also:
How does the 'binding' attribute work in JSF? When and how should it be used?
As to the ViewExpiredException, this has different grounds which is further elaborated in javax.faces.application.ViewExpiredException: View could not be restored.

Passing data between managed components in JSF

Is it actually possible to pass any data between managed components in JSF? If yes, how to achieve this?
Could anyone provide any sample?
There are several ways. If the managed beans are related to each other, cleanest way would be injection. There are different ways depending on JSF version and whether CDI is available.
CDI
Just use #Inject.
#Named
#SessionScoped
public class Bean1 {
// ...
}
#Named
#RequestScoped
public class Bean2 {
#Inject
private Bean1 bean1; // No getter/setter needed.
}
Other way around can also, the scope doesn't matter because CDI injects under the covers a proxy.
JSF 2.x
Use #ManagedProperty.
#ManagedBean
#SessionScoped
public class Bean1 {
// ...
}
#ManagedBean
#RequestScoped
public class Bean2 {
#ManagedProperty("#{bean1}")
private Bean1 bean1; // Getter/setter required.
}
Other way around is not possible in this specific example because JSF injects the physical instance and not a proxy instance. You can only inject a bean of the same or broader scope into a bean of a particular scope.
JSF 1.x
Use <managed-property> in faces-config.xml.
public class Bean1 {
// ...
}
public class Bean2 {
private Bean1 bean1; // Getter/setter required.
}
<managed-bean>
<managed-bean-name>bean1</managed-bean-name>
<managed-bean-class>com.example.Bean1</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>bean2</managed-bean-name>
<managed-bean-class>com.example.Bean2</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>bean1</property-name>
<value>#{bean1}</value>
</managed-property>
</managed-bean>
See also:
Backing beans (#ManagedBean) or CDI Beans (#Named)?
How to choose the right bean scope?
Get JSF managed bean by name in any Servlet related class
To add to BalusC's answer, if you are using a dependency-injection framework (spring, guice, etc.), or if using JSF 2.0, you can have one managed bean set into the other using just:
#Inject
private Bean2 bean2;
(or the appropriate annotation based on your DI framework)

Notifying one bean from another in ICEFaces

I have an ICEFaces we application. One page has two beans that display different things on the page.
I want to be able to notify one bean when another bean changes something on the bean so that the first bean update its content on the page.
Is that possible in ICEFaces? if so how?
Thanks,
Tam
What you can do is to "inject" bean1 into bean2, so the bean2 will have access to any method present in bean1.
If you are using Spring, this can be easily done when defining the beans:
<bean id="bean1" class="foo.bar.Bean1"/>
<bean id="bean2" class="foo.bar.Bean2">
<property id="bean1" ref="bean1"/>
</bean>
and in Java code of bean2:
public class Bean2 {
private Bean1 bean1 = null;
// The setter will be used by Spring to inject Bean1 in Bean2...
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
...
public void someMethod() {
...
// Now, you can call the bean1 instance to update what you want...
bean1.updateSomething();
}
}
If you are not using Spring:
You can directly access the bean1 instance within bean2 code like that:
Bean1 bean1 = (Bean1) FacesContext.getCurrentInstance().getCurrentInstance()
.getExternalContext().getSessionMap().get("bean1");
As has already been noted, JSF can do simple injection as well. Something like this in your faces-config.xml file:
<managed-bean>
<managed-bean-name>bean1</managed-bean-name>
<managed-bean-class>org.icefaces.sample.Bean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>bean2</managed-bean-name>
<managed-bean-class>org.icefaces.sample.Bean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>bean1</property-name>
<value>#{bean1}</value>
</managed-property>
</managed-bean>
As for updating the user interface when bean values change, that can be triggered through regular client interaction with the page. However, if you are doing a collaborative type application (where one user's change can update values that other user's can see), then ICEfaces has a feature called Ajax Push that you can use. Check their docs for more info.
I was going to post some examples of my own work, but the guys on the ICEFaces blog already have a really good blog post of their own. Take a look.

Resources