Using JSF and Flex Components Together
Join the DZone community and get the full member experience.
Join For FreeExadel Fiji extends JavaServer Faces (JSF) by allowing the use of Flex components within a JSF page. Fiji stands for “Flex and JSF Integration.” When using Fiji components, developers use Flex with the same component-based approach to building user interfaces that they are familiar with from JSF. This means that Flex components are bound to standard JSF managed beans.
Fiji is a framework that provides the following:
- A library of ready-to-use Flex charting components for JSF
- A universal wrapper that allows the use of any Flex component within a JSF page
- Most importantly, with Fiji, you can bind Flex components to the same JSF-managed beans properties or methods (or Seam components or Spring beans) used by the rest of your JSF application.
The current version ships with the following ready-to-use charts:
- Column chart
- Stacked column chart
- Bar chart
- Stacked bar chart
- Line chart
For example, here is a screenshot of a Flex component inside a JSF page
[img_assist|nid=5013|title=|desc=|link=none|align=undefined|width=341|height=398]
You can see all Fiji components in action at http://livedemo.exadel.com/fiji-demo
Now that I have given you a short introduction to Fiji, you are probably wondering what problem Fiji is trying to solve. Let's see.
Why Fiji?
Let's start with the basics. JavaServer Faces (JSF) is a standard (Java EE) framework for building Web user interfaces out of components. Today, we are interested in building Rich Internet Applications, so RichFaces was created takes JSF a step further by allowing the easier building of AJAX-based applications in JSF. (In case you don't know, RichFaces offers a large number of ready-to-use AJAX components.)
While JSF has been constantly growing, one area where it's still lacking is in displaying rich and interactive charts, graphs, and other similar visuals. Even with AJAX, any serious charting is extremely difficult, not to mention the testing that would be required for different browsers. It really comes down to two things. First, the technologies used behind JSF such as HTML, JavaScript, and CSS are not meant for displaying rich and interactive visual data (possible, but very difficult). Second, people have much higher expectations from a browser. The browser alone was never meant to display such visual data. It was primarily meant to display text and static images. So, in the end, it's not really JSF’s fault, we are simply asking the underlying technologies (HTML, JavaScript, CSS, the browser) to do too much.
Of course, there is hope. We can use a Flash player to display rich and interactive charts.
Adobe Flash
With Flex, complex rich applications can be compiled and then run inside a Flash player. A Flash player is a virtual machine that installs as a plug-in into any browser. Because the application (built with Flex) is running inside a virtual machine, it provides a far more superior environment for rich and interactive charts and graphs than the browser alone (which just interprets HTML, JavaScript, etc.).
The obvious next question is: So, why not use Flex and JSF components together right now? While it's possible to use Flex and JSF components inside the same page, the two technologies are not aware of each other. This means that JSF components are bound to JSF managed beans (or Seam components or Spring beans) while Flex is bound to completely different objects. This kind of integration is equivalent to inserting an image into a JSF page – in other words, not much integration.
Back to Fiji
This is a good place to revisit Fiji. Fiji is the perfect fit for helping with the above problem. Not only does Fiji provide ready-to-use chart components and a universal wrapper to use any Flex component, but, with Fiji, you can bind Flex components to standard JSF managed beans. As a JSF developer, you just use the standard and the familiar JSF component-centric approach, while now also being able to use Flex components.
So, where does it make sense to use Fiji. One scenario is if you have an existing JSF application and you would like to insert richer content based on Flash while still using your existing model (JSF managed beans, Seam components or Spring beans). This content can be charts, graphs or any other rich content.
Another situation where Fiji would make sense is where you have an existing JSF application, but want to use a Flash-based user interface (instead of HTML/JavaScript). By using Fiji, you can still use the existing model part of your application.
However, to be honest, it wouldn't make sense to use Fiji if you have an existing Flash-only application and are using a service like GraniteDS, BlazeDS, or Exadel Flamingo.
Now, I think we are ready for examples.
Using Fiji
There are three basic steps to get started.
- Download project
- Download JAR file and add them to project template
- Open the project in your favorite IDE
Download Project template
Download and unzip the project template. <link>
Download Fiji JAR files
- Go to http://exadel.com/web/portal/fiji and click on Download to download Fiji distribution
- Unzip the downloaded file
- In the next step you are going to copy JAR files from lib folder to fiji/web/WEB-INF/lib directory
1. If running Tomcat 5.5, copy all files
2. If you are running Tomcat 6, copy all files except:
¦ el-api.1.0.jar
¦ el-impl-1.0.jar
Import Project
Import the project into your favorite IDE. Here is how to do it in JBoss Tools (same steps for JBoss Developer Studio).
- Select File/Import
- Select Other
- Select JSF Project
- Point to the web.xml file location in the project
- Click Next
- Keep all setting as they are (I'm assuming you have a server defined)
- Click Finish
To tell you the truth, installing Fiji is very simple. It's basically a RichFaces project (has to be RichFaces version 3.2.2 or higher) plus the JAR files for Fiji:
- fiji-api-1.x.x.jar
- fiji-ui-1.x.x.jar
- flamingo-service-jsf-1.6.x.jar - to use HttpService via <fiji:endpoint> component. Covered later in article.
Besides using HttpService, if you would like to use DataServices (AMF format), you need two additional JAR files:
- amf-serializer-1.6.x.jar
That's it. Nothing else is needed, not even changes to web.xml.
Using Bar Chart
Let's start with a simple example using bar chart. The first step is to create the data for the chart. Because we can easily bind charts to JSF managed beans, that's where we are going to create our data.
The Java bean looks like this:
public class BarChartBean { public BarChartBean() { } private Integer[] data; public Integer[] getData() { return data; } @PostConstruct public void init () { data = new Integer[5]; data[0] = 5; data[1] = 2; data[2] = -3; data[3] = 10; data[4] = 9; } }
To make it a JSF managed bean we need to register it in a JSF configuration file:
<managed-bean> <managed-bean-name>barChartBean</managed-bean-name> <managed-bean-class>fiji.BarChartBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean>
So far this is just basic JSF. Let's now create the page.
Open start.xhtml. Look at the top, the page already contains a namespace for the Fiji tag library. Insert the following between <body> tags:
<h:form> <fiji:barChart id="barChart" value="#{barChartBean.data}" title="Bar chart" width="500" height="300"> <fiji:chartData type="name" value="Just some numbers" /> </fiji:barChart> </h:form>
<fiji:barChart> is just like a standard JSF component. The value attribute is bound to chart data inside the managed bean. <fiji:chartData type=”name”..> sets the chart name.
Save everything and run the page. You should get the following:
[img_assist|nid=5014|title=|desc=|link=none|align=undefined|width=672|height=394]
Let's try another example, this time using column chart. I'm sure most of you still remember the Summer Olympics, so we'll take the medal count and display it in the chart.
Using the column chart
You can reuse the same page or create a new page if you would like. If you create a new page, make sure to include the namespace for Fiji.
We are going to take the top three countries (USA, China, and Russia) and display their gold, silver and bronze medal count. As before, let's start with data for the chart.
The Java bean looks like this:
public class MedalsBean { private Map<String, Integer[]> medalsData = new LinkedHashMap<String,Integer[]>(); private ArrayList<String> colors = new ArrayList<String>(); private ArrayList<String> names = new ArrayList<String>(); private Integer[] medalsChina = new Integer[]{51, 21, 28}; private Integer[] medalsUSA = new Integer[]{36, 38, 36}; private Integer[] medalsRussia = new Integer[]{23, 21, 28}; @PostConstruct private void init() { medalsData.put("China", medalsChina); medalsData.put("USA", medalsUSA); medalsData.put("Russia", medalsRussia); names.add("Gold"); names.add("Silver"); names.add("Bronze"); colors.add("#DAA520"); colors.add("#C0C0C0"); colors.add("#B87333"); } public Map<String, Integer[]> getMedalsData() { return medalsData; } public ArrayList<String> getColors() { return colors; } public ArrayList<String> getNames() { return names; } public Integer[] getMedalsChina() { return medalsChina; } public Integer[] getMedalsUSA() { return medalsUSA; } public Integer[] getMedalsRussia() { return medalsRussia; } }
Because we now display three bars per country (one for each medal), we use a Map object and inside it put an array of integers representing the medal count.
To make it a JSF managed bean, we need to register is in a JSF configuration file:
<managed-bean> <managed-bean-name>medalsBean</managed-bean-name> <managed-bean-class>fiji.MedalsBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean>
Now we are ready to build the page. The page is not much different than in the first example:
<h:form> <fiji:columnChart id="goldMedals" value="#{medalsBean.medalsData}" legendPosition="top" barColors="#{medalsBean.colors}" width="250" height="300"> <fiji:chartData type="name" value="#{medalsBean.names}" /> </fiji:columnChart> </h:form>
Running the page will produce the following:
[img_assist|nid=5015|title=fiji3|desc=|link=none|align=undefined|width=339|height=404]
While the integration with JSF is neat (we can easily use JSF managed beans as a data model for Flex components), we can even take it a step further. It's possible to click on a Flex chart, send an AJAX request and do a partial page update. Let's see how it's done.
Event handlers
Fiji components have event handlers such as onitemclick which are very similar to the standard DHTML event handlers such as onkeyup. Going back to our example, let's say we want to click on any bar and display the medal information outside the chart. When a bar is clicked, an AJAX request will be sent to the server, properties will be set, and then rerendered.
The changes to the managed bean are minimal. All we do is add three properties:
private String countrySelected; private String medalTypeSelected; private String medalCountSelected; // getter and setters methods
Don't forget to generate getters and setters.
This is how our page now looks.
<h:form> <fiji:columnChart id="goldMedals" value="#{medalsBean.medalsData}" legendPosition="top" barColors="#{medalsBean.colors}" width="250" height="300"> <fiji:chartData type="name" value="#{medalsBean.names}" /> <a4j:support event="onitemclick" reRender="info"> <a4j:actionparam name="param1" value="event.x" assignTo="#{medalsBean.countrySelected}" noEscape="true"/> <a4j:actionparam name="param2" value="event.y" assignTo="#{medalsBean.medalCountSelected}" noEscape="true"/> <a4j:actionparam name="param3" value="event.name" assignTo="#{medalsBean.medalTypeSelected}" noEscape="true"/> </a4j:support> </fiji:columnChart> </h:form> <h:panelGrid id="info"> <h:outputText value="#{medalsBean2.countrySelected}" style="text-decoration: underline;"/> <h:outputText value="#{medalsBean2.medalTypeSelected} #{medalsBean2.medalCountSelected}"/> </h:panelGrid>
We use the most popular tag from RichFaces, <a4j:support> to attach an event (onitemclick) to the parent component, <fiji:columnChart>. When a bar is clicked on a chart, an Ajax request is sent to the server passing three arguments defined by the <a4j:actionparam> tag. The <a4j:actionparam> tag automatically takes the value from value attribute and assigns it to property defined in the assignTo attribute. The following three arguments are passed:
event.x – is the x-axis data. In our case, it is the country name
event.y – is the y-axis data. In our case, it is the medal count
event.name – is the the active bar. In our case, it will be the medal type selected
I have also set noEscape=”true” for each <a4j:actionparam>. This is necessary in order for the value to be treated as JavaScript code instead of being enclosed in single quotes and just passed to the server as a parameter.
Finally, we rerender the information inside the panel grid. As you can see, we are using standard RichFaces techniques to send and rerender information even though we are mixing in Flex components.
Using the universal wrapper
So far I have shown you how to use charting components that ship with Fiji. Obviously you might want to use other, existing Flex components or maybe even build your own. To use any other Flex components, we use the universal wrapper or <fiji:swf> component.
We will use one of the charts provided by amCharts.com.
Note: We can use their charts for free. The only limitation is that a small URL will appear pointing to their Web site. I don't think it's such a limitation for such nice charts.
The SWF file for the chart as well as chart data are included in the template under web/ampie directory. You can also download them from here:
SWF file – ampie.swf <link>
Settings file – ampie_settings.xml <link>
Data file – ampie_data.xml <link>
The page looks like this:
<fiji:swf src="/ampie/ampie.swf" bgcolor="#FFFFFF" width="420" height="300"> <f:param name="path" value="/ampie" /> <f:param name="settings_file" value="#{facesContext.externalContext.requestContextPath}/ampie/ampie_settings.xml" /> <f:param name="data_file" value="#{facesContext.externalContext.requestContextPath}/ampie/ampie_data.xml" /> <f:param name="preloader_color" value="#999999" /> </fiji:swf>
Using the src attribute of <fiji:swf>, we point to a Flash component. The Flash component can also be loaded as a resource by using src=“resource:///file.swf”. This particular Flash component (ampie.swf) requires some data. The data is being passed using the standard JSF attribute, <f:param>. When a Flash object is rendered on a page, it has a special flashVars attribute that provides arguments to the Flash module. In our case, the <f:param> tags with their information are rendered as flashVar arguments to the Flash component. This is how the charts gets its data.
Running the page will produce the following:
[img_assist|nid=5016|title=|desc=|link=none|align=undefined|width=539|height=384]
In case you need to update the data for the chart, one possible way is to rerender the Flash component using the standard RichFaces approach.
Using <fiji:endpoint>
As a final example, I'm going to show you how to use <fiji:swf> together with <fiji:endPoint>. This will allow us to send a parameter from a Flash component to a JSF managed bean.
Let's start with the Flash part. This is the Flex application we want to use in our JSF page:
hello.mxml:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> <mx:Script> <![CDATA[ import mx.rpc.events.ResultEvent; import mx.rpc.http.HTTPService; private var userRequest:HTTPService; private function send():void { userRequest = new HTTPService(); userRequest.url = Application.application.parameters.fiji; userRequest.addEventListener("result", httpResult); var param:Object = {}; param["hello"] = username.text; userRequest.send(param); } public function httpResult(event:ResultEvent):void { if (event.result) { helloLabel.text = event.result.value; } } ]]> </mx:Script> <mx:Form x="22" y="10" width="280"> <mx:Label id="helloLabel" styleName="text" text="{Application.application.parameters.text}" /> <mx:HBox> <mx:Label id="helloPerson" text="Username"/> <mx:TextInput id="username"/> </mx:HBox> <mx:Button label="Submit" click="send()"/> </mx:Form> </mx:Application>
It's a pretty simple MXML file which you can compile either in Flex Builder or just from the command line after installing Flex SDK
>mxmlc hello.mxml
I have also provided the SWF file inside the template (and here hello.swf <link>) in case you want to skip the compilation step.
The MXML file will produce the following:
[img_assist|nid=5017|title=|desc=|link=none|align=undefined|width=267|height=110]
Now, let's look at the JSF page:
<h:form> <fiji:swf src="/hello.swf" bgcolor="#FFFFFF" width="350" height="200"> <f:param name="text" value="Hello Fiji World"/> <fiji:endpoint name="fiji" decoder="fiji.HelloDecoder" encoder="fiji.HelloEncoder" service="#{serviceBean.greeting}"/> </h:form>
<fiji:swf> is used to include the Flash component as we did before. The service attribute points to a JSF managed bean with a method called greeting:
public Object greeting(Object value){ if(value != null){ return "What's up, "+value+"?"; } else { return "What's up, unknown?"; } }
It also has to be registered in a JSF configuration file:
<managed-bean> <managed-bean-name>serviceBean</managed-bean-name> <managed-bean-class>fiji.ServiceBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean>
I will cover HelloDecoder and HelloEncoder later.
When running this page, we will get something very similar to the previous image; however, now it's inside a JSF page and, if we enter a value for Username and click Submit, we will get the following.
[img_assist|nid=5018|title=|desc=|link=none|align=undefined|width=271|height=118]
The question is, how are we able to send a request to the JSF managed bean from the Flash component? It's done via the <fiji:endpoint> component:
<fiji:endpoint name="fiji" decoder="com.exadel.fiji.demo.simple.HelloDecoder" encoder="com.exadel.fiji.demo.simple.HelloEncoder" service="#{serviceBean.greeting}"/>
We define an endpoint with the name of fiji. If you look back at the MXML file, we have the following:
private function send():void { userRequest = new HTTPService(); userRequest.url = Application.application.parameters.fiji; userRequest.addEventListener("result", httpResult); var param:Object = {}; param["hello"] = username.text; userRequest.send(param); }
Inside the Flex component, the HTTP URL points to Application.application.parameters.fiji which in turn points to the Fiji endpoint defined in the JSF page by <fiji:endpoint> tag. When the Submit button inside the Flash is clicked, the URL is resolved. This is a standard JSF request and that's how we invoke the serviceBean.greeting method inside a JSF managed bean. The URL is again passed via the flashVars attribute when the page is rendered.
Finally, the initial label value is set via
{Application.application.parameters.text}
Its value is set via this <f:param> tag:
<f:param name="text" value="Hello Fiji World"/>
Decoders and Encoders
First, why do we even need these decoders and encoders? Well, when using the Flex HttpService, the data is being sent to the server and returned in XML format. The JSF managed bean that we are using doesn't know anything about XML. It simply returns a string value. So, in order to convert the request into a format that the managed bean understands, we use a decoder. When we invoke a method, the value returned has to be encoded in order for the Flash module to understand it. I know, it sounds complicated, but it's really not.
The decoder shown below will be called by Fiji and the value returned will be passed to the greeting(Object) method inside a JSF managed bean. The decoder will just pull the value from the XML sent by the request.
public class HelloDecoder implements FlexDecoder , Serializable { private static final String ENDPOINT_PARAM = "hello"; public Object decodeRequest(FacesContext context, UIComponent component) { return context.getExternalContext().getRequestParameterMap().get(ENDPOINT_PARAM); } }
When the greeting(Object) method inside the JSF managed bean returns a value, this encoder will be called to convert the response back to XML format.
public class HelloEncoder implements FlexEncoder { private static final String VALUE = "value"; public void encodeObject(FacesContext context, UIComponent component, Object object) throws IOException { ResponseWriter responseWriter = context.getResponseWriter(); responseWriter.startElement(VALUE, component); responseWriter.writeText(object, null); responseWriter.endElement(VALUE); } }
You can implement different encode and decode methods in case your Flash module requires the XML request/response to be in a specific format.
Summary
This article showed how you can use JSF and Flex component together while using standard JSF managed beans to provide data for the Flash components. That's what makes this framework unique. As a developer, you can continue using the standard component-centric JSF approach, but now you are able to inject even richer content using Flash.
To learn more, please visit the Fiji home page: http://exadel.com/web/portal/fiji
About the Author
Max Katz is a Senior Systems Engineer at Exadel. He has been helping customers jump-start their RIA development as well as providing mentoring, consulting, and training. Max is a recognized subject matter expert in the JSF developer community. He has provided JSF/RichFaces training for the past three years, presented at many conferences, and written several published articles on JSF-related topics. Max also leads Exadel's RIA strategy and writes about RIA technologies in his blog, https://exadel.com/newsroom/blog/. He is an author of Practical RichFaces book (Apress). Max holds a BS in computer science from the University of California, Davis.
Opinions expressed by DZone contributors are their own.
Comments