|
|
Search on blog
Petition by Java EE Guardians
sâmbătă, 31 octombrie 2015
JSF 2.2 Create a custom Hello World component in 30 seconds
Let's jump directly to the cool stuff and say that in JSF 2.0 a custom
component was made available to page authors by configuring it in a Facelet tag
library (*taglib.xml).
Moreover, when the component is mapped in a JAR, a special entry in web.xml is needed to point
to the *taglib.xml
file. As of JSF 2.2, we don't need these files anymore. A JSF 2.2 simple custom
component contains a single class, and it may look like the following code:
@FacesComponent(value =
"components.HelloWorldComponent", createTag = true)
public class HelloWorldComponent
extends UIComponentBase {
@Override
public String getFamily() {
return "hello.world.component";
}
@Override
public void encodeBegin(FacesContext context)
throws IOException {
ResponseWriter writer =
context.getResponseWriter();
writer.write("Hello World!");
}
}
Most of the hard work is accomplished by the @FacesComponent annotation (javax.faces.component.FacesComponent).
All we need to do is set the createTag
element to true,
and JSF should create the tag for us. Further, we can easily exploit our custom
components, as shown in the following code:
<?xml
version='1.0' encoding='UTF-8' ?>
<!DOCTYPE
html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:t="http://xmlns.jcp.org/jsf/component">
<h:head>
<title></title>
</h:head>
<h:body>
<t:helloWorldComponent/>
</h:body>
</html>
Note
Notice that the default namespace of the component is http://xmlns.jcp.org/jsf/component.
This is true for
all components that don't have an explicit namespace.
The entire list of elements supported by JSF 2.2 @FacesComponent is as
follows:
• createTag:
This can be set to true
or false. When
it is set to true,
JSF will generate the tag for us (to be more specific, JSF will create, at
runtime, a Facelet tag handler that extends ComponentHandler). This element can be used
only in JSF 2.2.
• tagName:
This allows us to indicate the tag name. When createTag is set to true, JSF will use this name for the
generated tag. This element can only be used in JSF 2.2.
• namespace:
This allows us to indicate the tag namespace. When createTag is set to true, JSF will use this namespace for
the generated tag. When namespace is not specified, JSF will use the http://xmlns.jcp.org/jsf/
component namespace. This element can be used only in JSF 2.2.
• value:
This element comes from JSF 2.0 and indicates the component type. The component
type can be used as the argument of the Application.createComponent(java.lang.String) method
for creating instances of the Component
class. As of JSF 2.2, if the value
element is missing or is null,
JSF will obtain it by calling the getSimpleName() method on the class to which @FacesComponent is
attached and lowercasing the first character.
vineri, 30 octombrie 2015
JSF Scopes Tutorial - JSF/CDI Session Scope
The session
scope lives across multiple HTTP request-response cycles (theoretical
unlimited).
The request scope is very useful in any web application when you need a
single interaction per HTTP request-response cycle. However, when you need
objects visible for any HTTP request-response cycle that belongs to a user
session, then you need a session scope;
in this case, the bean lives as long as the HTTP session lives. The session
scope allows you to create and bind objects to a session. It gets created upon the first HTTP request involving this bean in the session and gets
destroyed when the HTTP session is invalidated. The session 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.
Session Scope Annotations
JSF: The JSF request scope
annotation is @SessionScoped (javax.faces.bean.SessionScoped). A bean with this scope
should be annotated with @ManagedBean
(javax.faces.bean.ManagedBean).
The default scope is @RequestScope.
CDI: The CDI request scope
annotation is @SessionScoped (javax.enterprise.context.SessionScoped). A bean with this
scope should be annotated with @Named
(javax.inject.Named).
For CDI managed beans (@Named),
the default scope is the @Dependent
pseudo-scope.
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;
import java.io.Serializable;
// for JSF
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
// for CDI
import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
// JSF vs CDI
@ManagedBean @Named
@SessionScoped @SessionScoped
public class
CountBean implements
Serializable {
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.
So, when navigating via AJAX, via forward mechanism back in the same
view (or another view) or redirect mechanism the count value will be increased by 1. This reveals two
aspects:
- The CountBean
constructor is called for creating a new instance once per user session. This means that count is initialized with 0 only once. Further
requests fired in the current user session will use this CountBean instance. We
say that there is a CountBean
instance per user.
- The session scope doesn't lose the object's state while forwarding or
redirecting. The object's state is available until the session is destroyed
(e.g. session timeout, invalidate, etc).
Basically you have to pay attention when you submit data to a session
scoped bean. The submitted data will "live" as long as the current
user session. So, a good practice will tell you to not store in session large
amount of data, especially if memory is a critical resource.
Implements Serializable
JSF and CDI managed beans should be declared Serializable (implements Serializable). This is
needed because container may persist (serialize) session data to hard disk. This
allows container to manage critical situations as heavy loading, or simply
share data with other servers in a cluster or to revive sessions during server
restart.
Session Scope Programmatic Access
Programmatically you can interact with session scope like this:
- access the session scope map
// JSF 2.0-2.2
FacesContext
context = FacesContext.getCurrentInstance();
Map<String,
Object> requestMap = context.getExternalContext().getSessionMap();
// JSF 2.3
@Inject
@SessionMap
private Map<String,
Object> sessionMap;
Map<String,
Object> requestmap = Faces.getSessionMap();
- set a session scoped attribute
// JSF 2.0 -
2.3
sessionMap.put(name, value);
Faces.setSessionAttribute(name, value);
- get a session scoped attribute
// JSF 2.0-2.3
Object value = sessionMap.get(name);
<T> value = Faces.getSessionAttribute(name);
- remove a session scoped attribute
// JSF 2.0-2.3
Object value = sessionMap.remove(name);
<T> value = Faces.removeSessionAttribute(name);
! In JSF pages, you can use the implicit
object, #{sessionScope}
(e.g. get CountBean
instance: #{sessionScope.countBean}).
Among others, the session map will contain instances of managed beans
that are declared under the session scope (@SessionScoped (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 session map. Therefore you will be able to locate an
instance of this JSF managed bean in the session 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 session map. So, via
the session map, you can access a property of a session scoped JSF managed
bean, like this:
String count =
((CountBean)(Faces.getSessionAttribute("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.getSessionAttribute(bean_name))).getCount();
Now, you can easily intuit how to work with managed beans stored in the
session 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 session scoped beans the
method annotated with @PostConstruct
will be called only once, after the session scoped bean instance was created.
JSF managed bean example:
import
java.io.Serializable;
import
javax.faces.bean.ManagedBean;
import
javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class
InitBean implements Serializable{
private int init;
public InitBean() {
init = 5;
}
public int getInit() {
return init;
}
public void setInit(int init) {
this.init = init;
}
}
import
java.io.Serializable;
import
javax.faces.bean.ManagedBean;
import
javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class
CountBean implements Serializable {
@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
java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import
javax.inject.Named;
@Named
@SessionScoped
public class
InitBean implements Serializable {
private int init;
public InitBean() {
init = 5;
}
public int getInit() {
return init;
}
public void setInit(int init) {
this.init = init;
}
}
import
java.io.Serializable;
import
javax.inject.Inject;
import javax.enterprise.context.SessionScoped;
import
javax.inject.Named;
@Named
@SessionScoped
public class
CountBean implements Serializable {
@Inject
private InitBean initBean;
@PostConstruct
public void init(){
LOG.info("CountBean#Initializing counter with @PostConstruct
...");
count = initBean.getInit();
}
...
}
Injection and session scoped
beans
JSF: For JSF managed beans,
injection is accomplished via @ManagedProperty.
For example:
JSF & CDI mixed: CDI can
be injected in JSF (vice versa is not true!)
! 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 session scoped managed bean
you can inject session and application managed beans, but not request or view 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 session scoped
managed beans programmatically
Starting with JSF 2.2, we can programmatically
reproduce the content of faces-config.xml.
For session 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("session"));
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 session 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>session</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>
...
See you in the next post about JSF/CDI view scope.
joi, 29 octombrie 2015
Programmatically caching PrimeFaces charts via OmniFaces Cache component
In this post, you will see how to combine PrimeFaces and OmniFaces to
obtain cacheable charts. In order to keep the things simple, we will use a
PrimeFaces Line
Chart. For this kind of chart we can use <p:chart/> tag in page and a simple
managed bean. So, in page we can have:
<p:chart
id="someChartId" type="line"
model="#{chartView.lineModel}"
style="height:300px;width:600px;"/>
The ChartView
can be written as below:
@Named
@ViewScoped
public class
ChartView implements Serializable {
private LineChartModel lineModel;
@PostConstruct
public void init() {
createLineModels();
}
private void createLineModels() {
lineModel
= initLinearModel();
lineModel.setTitle("Linear
Chart");
lineModel.setLegendPosition("e");
lineModel.setZoom(true);
Axis yAxis = lineModel.getAxis(AxisType.Y);
yAxis.setMin(0);
yAxis.setMax(10);
}
private LineChartModel initLinearModel() {
LineChartModel model = new LineChartModel();
LineChartSeries series1 = new
LineChartSeries();
series1.setLabel("Series 1");
Random rnd = new Random();
series1.set(rnd.nextInt(10), rnd.nextInt(10));
series1.set(rnd.nextInt(10), rnd.nextInt(10));
series1.set(rnd.nextInt(10), rnd.nextInt(10));
series1.set(rnd.nextInt(10), rnd.nextInt(10));
series1.set(rnd.nextInt(10), rnd.nextInt(10));
LineChartSeries series2 = new
LineChartSeries();
series2.setLabel("Series 2");
series2.set(rnd.nextInt(10),
rnd.nextInt(10));
series2.set(rnd.nextInt(10),
rnd.nextInt(10));
series2.set(rnd.nextInt(10), rnd.nextInt(10));
series2.set(rnd.nextInt(10),
rnd.nextInt(10));
model.addSeries(series1);
model.addSeries(series2);
return
model;
}
public LineChartModel getLineModel() {
return
lineModel;
}
}
This code will produce a simple line chart as in figure below:
Now, let's suppose that, during the application run, this chart is periodically updated or re-created (we will simulate this via random series values and a Refresh button). Each time this is happening, we will lose the current chart. But, it may be useful to cache (like save) some of those charts, and have the possibility to load them later during the current session (for charts that belongs to certain users)/application (for charts common to all users).
In order to accomplish this task, we can use the OmniFaces Cache
component. Basically, this component is very well described in OmniFaces Showcase and Mastering
OmniFaces book, but the major ideas are:
- Cache
component is exposed to JSF page authors via <o:cache> tag.
- Cache encapusaltes a server-side caching mechanism for the markup produced by the Render
Response phase.
- Cache
takes action in Render Response phase.
- The cached markup is stored under a key generated by OmniFaces or
indicating via the optional key
attribute of <o:cache>.
- Caching can be disabled per request via the optional disabled flag attribute
of <o:cache>.
- A cached entry can be re-cached via reset flag attribute of the <o:cache>.
- By default, cached data is stored in session scope (application scope
is also supported).
Per example, from JSF page author perspective, we can indicate that
we want to re-cache a piece of markup under the key foo, like below:
<o:cache
id="cacheId" key="foo" disabled="false"
reset="true">
... // the markup produced for this snippet of
code will be cached
</o:cache>
Obviously, the disabled
attribute can be skipped in this example, since that is its implicit
value. If key
is also skipped, then OmniFaces will generate one. If reset is skipped, that the markup will not be re-cached.
Since we want to have the possibility to decide which charts are cached
and load/delete a certain chart from cache we cannot simply do only this:
<o:cache
id="cacheId">
<p:chart id="someChartId"
type="line"
model="#{chartView.lineModel}"
style="height:300px;width:600px;"/>
</o:cache>
Basically, this will cache the first chart, and, at each postback, will
serve this chart from cache.
So, a quick approach will consist in juggling with <o:cache>
attributes programmatically. As I said above, Cache takes action in the Render Response
phase. This means that we can control from our ChartView bean the Cache component before the caching is
actually happening. The centerpiece of this implementation will consist in the
below private
method which allows us to programmatically configure the Cache component:
private void configureCache(String
key, boolean disabled, boolean reset) {
Cache cache =
Components.findComponent("cacheId");
cache.setDisabled(disabled);
cache.setReset(reset);
cache.setKey(key);
}
Now, we will add one be one the UIs needed to control the caching.
First we add a button labeled, Refresh.
Practically, each time we press this button, a new chart will be generated (new
data). This is for simulating the chart update.
<h:commandButton
action="#{chartView.redrawAction()}" value="Refresh"/>
The
redrawAction() ensures that the new chart is not cached, so caching is
disabled and key is not relevant:
public void
redrawAction() {
configureCache("none", true, false);
createLineModels();
}
Further, we add a button labeled, Save. When this button is pressed, the
current chart is cached under a key of type, key_random-number (in real cases, you may want to allow the user to provide the key as the chart title).
The key will be
expose to the user in a list representing the saved charts:
<h:commandButton
action="#{chartView.saveChart()}" value="Save"/>
The saveChart()
method enables caching and generates a new key. The key is stored in a list:
private
List<String> keys;
...
public void
saveChart() {
String key = "key_" + new
Random().nextInt(1000);
configureCache(key, false, true);
keys.add(key);
}
Next, we list the cached keys and a button labeled, Load. The user can
select a key and click the Load
button to load a cached chart:
<h:selectOneMenu
value="#{chartView.selected}">
<f:selectItem itemLabel="Select a
chart ..." noSelectionOption="true"/>
<f:selectItems
value="#{chartView.keys}" var="t"
itemLabel="#{t}" itemValue="#{t}"/>
</h:selectOneMenu>
<h:commandButton
value="Load Chart" action="#{chartView.loadChart()}"
disabled="#{chartView.keys.size()
eq 0}"/>
The loadChart()
is:
public void
loadChart() {
if (selected != null) {
configureCache(selected, false, false);
}
}
Finally, we add a button labeled, Delete, which will delete from cache the
selected chart:
<h:commandButton
value="Delete Chart" action="#{chartView.deleteChart()}"
disabled="#{chartView.keys.size()
eq 0}"/> |
And, deleteChart()
is:
public void
deleteChart() {
if (selected != null) {
CacheFactory.getCache(Faces.getContext(),
"session").remove(selected);
keys.remove(selected);
configureCache("none", true, false);
resetLineModels();
}
}
private void
resetLineModels(){
lineModel.getSeries().clear();
}
Notice here how we can programmatically delete by key an entry from
cache using the CacheFactory.
Here it is a suggestive screenshot:
The complete application is available here.
Abonați-vă la:
Postări
(
Atom
)
|
[JSF Page Author Beginner's Guide] |
Postări populare
-
If you like this article, I think you are going to like JSF 2.3 Tutorial as well.
-
Starting with JSF 2.3 the JSF "native" managed bean annotations are officially deprecated. So, there is no doubt now that CDI is ...
-
[OmniFaces utilities] The getRequestURL() method returns the HTTP request URL with query string, regardless of any forward. This is the f...
-
Let's checkout a common practice for declaring constants in Java using the public static final declaration: public class Circle { ...
-
[OmniFaces utilities] The getActionExpressionsAndListeners() method returns a list of all action expressions and listeners associated wit...
-
[OmniFaces utilities] The getCurrentActionSource() method returns the source of the currently invoked action, or null if there is none, ...
-
Before you read this post please read: Use a CDI alternative as a mock implementation for a stateless session bean So, since you are fa...
-
Starting with JSF 2.3, more exactly with m07 , we can take advantage of using the auto detection of convertors based on 1st UISelectMany it...
-
[OmniFaces utilities] The isSerializable() method returns true if the given object is serializable. Method: Usage: Example 1 - te...
-
[OmniFaces utilities] The reverseArray() function returns a copy of the array with items in reversed order. Function: Usage: Let...
OmniFaces/JSF Fans
-
▼
2015
(
516
)
-
▼
octombrie
(
45
)
- Mastering OmniFaces - The Hidden Table of Content
- JSF 2.2 Create a custom Hello World component in 3...
- JSF Scopes Tutorial - JSF/CDI Session Scope
- Programmatically caching PrimeFaces charts via Omn...
- The OmniFaces project is a Duke's Choice 2015 awar...
- OmniFaces 2.2 ViewParam with default value
- [OmniFaces utilities (2.0)] Joins all elements of ...
- Destroy view scoped beans when the browser unload ...
- OmniFaces 2.2 RC1 released!
- [JSF Page Author Beginner's Guide] JSF <graphicIma...
- [OmniFaces utilities (2.2)] Stream a specified ran...
- JSF Scopes Tutorial - JSF/CDI Request Scope
- Exploit JavaScript closures in JSF composite compo...
- [JSF Page Author Beginner's Guide] JSF <selectMany...
- Working with context parameters (initialization pa...
- JSF Programmatic Navigation Tips
- What are FacesContext and ExternalContext?
- [OmniFaces utilities (2.0)] Convert an Iterable<E>...
- JSF ELContext and ELResolver for Novices
- JSF 2.2 programmatic faces-config.xml
- [JSF Page Author Beginner's Guide] JSF <selectOneL...
- JSF 2.2 new namespaces
- [JSF Page Author Beginner's Guide] JSF <selectOneM...
- Logging duration of createView, buildView and rend...
- [JSF Page Author Beginner's Guide] JSF <selectOneR...
- JSF 2.0-2.3 Progress of Iterable/Map support in UI...
- [OmniFaces utilities (2.0)] Convert a Iterable<E> ...
- [OmniFaces utilities (2.0)] Convert a Map<K, V> to...
- [OmniFaces utilities (2.0)] Convert a Set<E> to a ...
- [JSF Page Author Beginner's Guide] JSF <commandLin...
- Just tested JSF 2.3 PostRenderViewEvent under Paya...
- What's new in OmniFaces 2.2 ?
- [JSF Page Author Beginner's Guide] JSF <outputLink...
- JSF PostRestoreStateEvent component system event d...
- JAX-RS consume a RESTful web service from JSF
- [OmniFaces utilities 2.0] Check if the string repr...
- Mastering OmniFaces - First OmniFaces book available!
- JSF and Template Method design pattern - part III ...
- 9 Steps to run a JSF+PrimeFaces+OmniFaces sample a...
- JSF and Template Method design pattern - part II (...
- JSF Tip: Passing objects to managed beans
- [OmniFaces utilities 2.0] Create an integer array ...
- JSF and Template Method design pattern - part I (p...
- Set a component renderer from page
- Access a component by using a reference to it
- ► septembrie ( 38 )
-
▼
octombrie
(
45
)
JSF/OmniFaces Resources |