The
JSF/CDI application scope lives as long as
the web application lives.
An
application scope extends the
session scope with the shared state across all users' interactions with a web
application; this scope lives as long as the web application lives. Since the
beans in the application scope lives until the application shuts down (or they
are programmatically removed), we can say that this scope lives most. More precisely,
objects settled on the application scope can be accessed from any page that is
part of the application (for example, JSF, JSP, and XHTML).
The
application scope is present in JSF and CDI and functions in the same way. It
can be used for non-rich AJAX and non-AJAX requests.
The
application scope should be used only for data that is safe to be shared. Since
an application scoped bean is shared by all users, you need to be sure that the
bean has an immutable state or you need to synchronize
access.
Usually,
application scope objects are used as counters, but they can be used for many other
tasks, such as initializations and navigations. For example, the application
scope can be used to count how many users are online or to share that
information with all users. Practically, it can be used to share data among all
sessions, such as constants, common settings, and tracking variables.
Application Scope Annotations
JSF: The JSF request scope annotation is @ApplicationScoped (javax.faces.bean.ApplicationScoped). A bean
with this scope should be annotated with @ManagedBean (javax.faces.bean.ManagedBean).
CDI: The CDI request scope annotation is @ApplicationScoped (javax.enterprise.context.ApplicationScoped). A bean
with this scope should be annotated with @Named (javax.inject.Named).
Simple Example
// index.xhtml
<h:body>
<h4>Same view after submit
(index.xhtml):</h4>
<h:form>
<h:commandButton value="Count"
action="#{countBean.countActionVoid()}"/>
</h:form>
Current value: #{countBean.count}
<h4>Forward to another view after submit
(count.xhtml):</h4>
<h:form>
<h:commandButton value="Count"
action="#{countBean.countActionAndForward()}"/>
</h:form>
Current value: #{countBean.count}
<h4>Redirect to another view after
submit (count.xhtml):</h4>
<h:form>
<h:commandButton value="Count"
action="#{countBean.countActionAndRedirect()}"/>
</h:form>
Current value: #{countBean.count}
<h4>AJAX :</h4>
<h:form>
<h:commandButton value="Count"
action="#{countBean.countActionVoid()}">
<f:ajax
render="currentValueId"/>
</h:commandButton>
</h:form>
<h:outputText id="currentValueId"
value="Current value: #{countBean.count}"/>
</h:body>
// count.xhtml
<h:body>
Current
value: #{countBean.count}
</h:body>
// CountBean.java
import
java.util.logging.Logger;
// for JSF
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ApplicationScoped;
// for CDI
import javax.inject.Named;
import javax.enterprise.context. ApplicationScoped;
// JSF vs CDI
@ManagedBean @Named
@ApplicationScoped @ApplicationScoped
public class
CountBean {
private static final Logger LOG =
Logger.getLogger(CountBean.class.getName());
private int count;
public CountBean() {
LOG.info("CountBean#Initializing counter
...");
count = 0;
}
public void countActionVoid() {
LOG.info("CountBean#countActionVoid() -
Increasing counter ...");
count++;
}
public String countActionAndForward() {
LOG.info("CountBean#countActionAndForward() - Increasing counter
...");
count++;
return "count";
}
public String countActionAndRedirect() {
LOG.info("CountBean#countActionAndRedirect() - Increasing counter
...");
count++;
return
"count?faces-redirect=true;";
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
The
complete application is available here.
The CountBean constructor is called for creating a
new instance only once per application start.
This means that count is initialized
with 0 only once per application start. Subsequent requests (GET, POST,
POST-REDIRECT-GET) will use this instance. For testing the application scope,
you need to open multiple browsers or use multiple machines. The count will be
available/shared across all sessions. The application scope doesn't lose the
object's state during application run.
Basically
you have to pay attention when you provide data from an application scoped bean
to multiple
sessions
beans (for example, using injection), since the data shared by all sessions can
be modified by each session separately. This can lead to inconsistent data
across multiple users; therefore, be sure that the exposed application data
isn't modified in sessions.
Implements Serializable
JSF
and CDI managed beans shouldn't be declared Serializable (implements Serializable). But, you have to keep in mind that
there is a tiny chance that the system will give them the same id, so make them
Serializable is not a
bad practice.
Application Scope Programmatic Access
Programmatically
you can interact with application scope like this:
-
access the application scope map
// JSF 2.0-2.2
FacesContext
context = FacesContext.getCurrentInstance();
Map<String,
Object> applicationMap = context.getExternalContext().getApplicationMap();
// JSF 2.3
@Inject
@ApplicationMap
private
Map<String, Object> applicationMap;
// OmniFaces
Map<String,
Object> applicationMap = Faces.getApplicationMap();
-
set a application scoped attribute
// JSF 2.0 -
2.3
applicationMap.put(name, value);
// OmniFaces
Faces.setApplicationAttribute(name, value);
-
get a application scoped attribute
// JSF 2.0-2.3
Object value = applicationMap.get(name);
// OmniFaces
<T> value = Faces.getApplicationAttribute(name);
-
remove a application scoped attribute
// JSF 2.0-2.3
Object value = applicationMap.remove(name);
// OmniFaces
<T> value = Faces.removeApplicationAttribute(name);
! In JSF pages, you can use the implicit object, #{applicationScope} (e.g. get
CountBean instance:
#{applicationScope.countBean}).
Among
others, the application map will contain instances of managed beans that are
declared under the application scope (@ApplicationScoped (JSF/CDI)).
In
case of JSF managed beans (not CDI managed beans - in this case, the keys are
pretty complex), you can easily identify such beans by their names which
becomes keys in the application map. Therefore you will be able to locate an
instance of this JSF managed bean in the application map under the key, countBean. If you
specify the bean name via @ManagedBean(name="some_name"), then some_name will be the key in the application map. So,
via the application map, you can access a property of a application scoped JSF
managed bean, like this:
String count =
((CountBean)(Faces.getApplicationAttribute("countBean/some_name"))).getCount();
Is
perfectly legal to do this also (this refers to the current bean):
@ManagedBean(name="some_name")
...
String
bean_name = getClass().getAnnotation(ManagedBean.class).name();
int count =
((CountBean)(Faces.getApplicationAttribute(bean_name))).getCount();
Now,
you can easily intuit how to work with managed beans stored in the application
map.
Using @PostConstruct
Typically,
in a managed bean, we need to write a method annotated with @PostConstruct for
accomplishing initializations tasks based on injected artifacts. With other words, the @PostConstruct
annotation is used on a method that needs to be executed after dependency
injection is done to perform any initialization. When the initialization
doesn't involve injected artifacts the constructor can be used for
initializations. For application scoped beans the method annotated with @PostConstruct will be
called only when the application starts. With other words, a method annotated
with @PostConstruct will be
called only when the application scoped bean is instantiated. Subsequent
requests willuse this instance. Usually, this happens when the application
starts; therefore, place inside this method the initialization tasks specific
to the application in the context of this bean.
JSF
managed bean example:
import
javax.faces.bean.ManagedBean;
import
javax.faces.bean.ApplicationScoped;
@ManagedBean
@ApplicationScoped
public class
InitBean {
private int init;
public InitBean() {
init = 5;
}
public int getInit() {
return init;
}
public void setInit(int init) {
this.init = init;
}
}
import
javax.faces.bean.ManagedBean;
import
javax.faces.bean.ApplicationScoped;
@ManagedBean
@ApplicationScoped
public class
CountBean {
@ManagedProperty("#{initBean}")
private InitBean initBean;
@PostConstruct
public void init(){
LOG.info("CountBean#Initializing counter with @PostConstruct
...");
count = initBean.getInit();
}
public void setInitBean(InitBean initBean) {
this.initBean = initBean;
}
...
}
CDI
managed bean example:
import
javax.enterprise.context.ApplicationScoped;
import
javax.inject.Named;
@Named
@ApplicationScoped
public class
InitBean {
private int init;
public InitBean() {
init = 5;
}
public int getInit() {
return init;
}
public void setInit(int init) {
this.init = init;
}
}
import
javax.inject.Inject;
import
javax.enterprise.context.ApplicationScoped;
import
javax.inject.Named;
@Named
@ApplicationScoped
public class
CountBean {
@Inject
private InitBean initBean;
@PostConstruct
public void init(){
LOG.info("CountBean#Initializing counter with @PostConstruct
...");
count = initBean.getInit();
}
...
}
Injection and application scoped beans
JSF: For JSF managed beans, injection is
accomplished via @ManagedProperty. For
example:
CDI: For CDI managed beans, injection is
accomplished via @Inject. For example:
JSF & CDI mixed: CDI can
be injected in JSF (vice versa is not true!)
JSF
Managed Beans Restrictions:
! As a general rule in JSF, don't use objects that have shorter
lifespan than the objects you are
calling
it from. In other words, use objects whose lifespan is the same as, or longer
than, the object being injected into. Breaking this rule will end up in a JSF
exception. Base on this rule in a JSF application scoped managed bean you can
inject only application managed beans.
JSF
managed beans can be injected in other JSF managed beans.
CDI
Managed Beans Restrictions:
! When you are using an object that has a shorter lifespan than the
object you are calling it from (for example, injecting a request scoped bean
into a session scoped bean), CDI classifies the use case as a mismatched
injection and fixes the issue via CDI proxies. For each request, the CDI proxy
re-establishes the connection to a live instance of the request scoped bean.
CDI
managed beans can be injected in JSF managed beans.
Configuring JSF application scoped
managed beans programmatically
Starting
with JSF 2.2, we can programmatically reproduce
the content of faces-config.xml. For application scoped managed beans, the
relevant snippet of code is:
@Override
public void
populateApplicationConfiguration (Document toPopulate) {
String ns =
toPopulate.getDocumentElement().getNamespaceURI();
Element managedbeanEl =
toPopulate.createElementNS(ns, "managed-bean");
Element managedbeannameEl =
toPopulate.createElementNS(ns, "managed-bean-name");
managedbeannameEl.appendChild(toPopulate.createTextNode("countBean"));
managedbeanEl.appendChild(managedbeannameEl);
Element managedbeanclassEl =
toPopulate.createElementNS(ns, "managed-bean-class");
managedbeanclassEl.appendChild(toPopulate.createTextNode("beans.CountBean"));
managedbeanEl.appendChild(managedbeanclassEl);
Element managedbeanscopeEl = toPopulate.
createElementNS(ns, "managed-bean-scope");
managedbeanscopeEl.appendChild(toPopulate.
createTextNode("application"));
managedbeanEl.appendChild(managedbeanscopeEl);
...
// programmatic create managed-property
...
toPopulate.getDocumentElement().appendChild(managedbeanEl);
}
A
complete application can be seen in Mastering
JSF 2.2 book .
Configuring JSF request scoped managed
beans in XML file
With
XML configuration, you can use the old JSF 1.x mechanism to define the managed
bean in a normal faces-config.xml file. For
example:
...
<managed-bean>
<managed-bean-name>countBean</managed-bean-name>
<managed-bean-class>beans.CountBean</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
...
<!-- managed bean properties --> via
<managed-property/>
...
</managed-bean>
...
Managed
beans should be defined in a separate XML file because the faces-config.xml is used
to set the application level configurations. Basically, if you prefer this
approach, create a new XML file and put the managed beans detail inside.
Finally, declare the XML file via javax.faces.CONFIG_FILES context
parameter in web.xml file.
...
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>WEB-INF/my-manage-beans.xml</param-value>
</context-param>
NICE POST.
RăspundețiȘtergeresccm training
sccm online training