Using BlazeDS to Connect Flex to A Java Class That Returns Data From A Database
Introduction
This is part 4 in my series on using Flex, BlazeDS, and Java. In this part I explain how I used Java to provide my Flex application with a collection of objects that were populated by records from a database. BlazeDS provides the plumbing that enables Flex to communicate with the Java classes on the back end. If you've not yet read the previous posts in this series, you should review them first.
Steps to Set Up This Example
You can view this example online. Right click on the application to view the source code.
I created a series of Java classes that connected to my database, executed a query, and iterated over the result set. For each record I created a Speaker object (Speaker is a separate class with firstName, lastName, and title instance fields). Each Speaker object is added to an ArrayList, which is returned to the caller.
The Java class that my Flex applicaton communicates with is SpeakerService.
package dbservice;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import dbservice.dao.SpeakersDAO;
public class SpeakerService {
public SpeakerService() {
}
public ArrayList<Speaker> getSpeakers(int conferenceID) {
ArrayList<Speaker> speakers = new ArrayList<Speaker>();
SpeakersDAO aDAO = new SpeakersDAO() ;
ResultSet resultSet ;
try {
resultSet = aDAO.getAcceptedSpeakers(conferenceID) ;
while (resultSet.next()) {
Speaker aSpeaker = new Speaker(
resultSet.getString("firstName") ,
resultSet.getString("lastName"),
resultSet.getString("title") );
speakers.add(aSpeaker);
} //end while
} catch(SQLException ex) {
System.out.println(ex.getMessage() ) ;
} //end try-catch block
finally {
aDAO.closeResources();
}
return speakers;
}
}
Note that this class is in package dbservice and has a public method named getSpeakers which returns an ArrayList of Speaker objects.
I compiled all my Java classes and placed package dbservice into the [tomcat-home]\webapps\blazeds\WEB-INF\classes\ folder.
Here is one very important step, which caused me an hour or so of frustrating trouble shooting: be sure to copy any jar packages to the [tomcat-home]\webapps\blazeds\WEB-INF\lib folder. Initially I forgot that step and the busy icon on my Flex application just merrily spun away without end. In my case I needed to copy sqljdbc.jar to the blazeDS lib folder so that the JDBC connector class for SQL Server is available to my class in package dbservice which uses it.
Next I added this destination node to the remote-config.xml file (located in ([tomcat-home]\webapps\blazeds\WEB-INF\flex).
<destination id="speakerservice">
<properties>
<source>dbservice.SpeakerService</source>
</properties>
<adapter ref="java-object"/>
</destination>
Note the value of the id attribute ("speakerservice") and the source tag's value ("dbservice.SpeakerService"). The id value is what the Flex application's mx:RemoteObject tags needs for Flex to connect to the SpeakerService class (which is located in package dbservice).
Be sure to stop Tomcat if its running and then restart Tomcat after doing the above steps.
Create a Flex application by following the steps given in my first tutorial in this series. My Flex application is simple, the user clicks on a ComboBox to select a conference and then clicks the Get Speakers button to call the remote object (SpeakerService class) method getSpeakers, passing it the conference id value. The ArrayCollection is received from the Java class and is used as the data provider for a DataGrid component.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" borderColor="#F3F6F8" backgroundGradientAlphas="[1.0, 1.0]" backgroundGradientColors="[#F9F0F0, #F9F0F0]">
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.utils.ObjectUtil;
import mx.controls.Alert;
import mx.utils.StringUtil;
import mx.collections.ArrayCollection;
[Bindable]
public var conferencesAryCol:ArrayCollection = new ArrayCollection(
[ {label:"2008 Predoctoral Conference", data:147},
{label:"2008 Families Conference", data:148},
{label:"2008 Spring Conference", data:149} ]);
//holds all the records in ArrayList returned by the Java class
[Bindable]
private var speakersAryCol:ArrayCollection;
/*ensure our Speaker.as class is
compiled into the SWF
otherwise the array of Speaker objects
returned by the Java class will
be treated as generic objects
*/
private var aSpeaker:Speaker;
/*Called automatically when the result is returned by the
Java class method getSpeakers
*/
public function resultGetSpeakers(event:ResultEvent):void {
//used for debugging - shows details about result
//returned by the Java class method
// Alert.show( ObjectUtil.toString(event.result) );
//place the query results into an ArrayCollection
speakersAryCol = event.result as ArrayCollection;
totalLbl.text = "Number of speakers returned by the Java class: " + speakersAryCol.length;
} //end function resultGetSpeakers
/*
automatically called if the Java class method call causes an error
*/
private function faultGetSpeakers(event:FaultEvent):void{
Alert.show( ObjectUtil.toString(event.fault) );
}//end function faultGetSpeakers
]]>
</mx:Script>
<!--setup the connection to the Java class-->
<mx:RemoteObject
id="SpeakerService"
destination="speakerservice"
showBusyCursor="true"
>
<mx:method name="getSpeakers"
result="resultGetSpeakers(event)"
fault="faultGetSpeakers(event)"/>
</mx:RemoteObject>
<mx:Panel x="10" y="10" width="90%" height="90%" layout="vertical" title="Test of Using Java To Get Records From A Database" borderColor="#F0ED06" fontFamily="Arial" fontWeight="bold" fontSize="13" backgroundColor="#B6F5F0">
<mx:Text width="350" text="Select a conference and then click the button to get the speakers for that conference." fontWeight="bold"/>
<mx:Spacer height="20"/>
<mx:HBox width="100%">
<mx:ComboBox id="conferencesCB" dataProvider="{conferencesAryCol}" width="250" />
<mx:Spacer width="20" />
<mx:Button x="28" y="40" label="Get Speakers" click="SpeakerService.getSpeakers(conferencesCB.selectedItem.data)"/>
</mx:HBox>
<mx:Spacer height="20" />
<mx:DataGrid id="speakersDG" dataProvider="{speakersAryCol}"
fontFamily="Verdana" fontSize="10" variableRowHeight="true" height="60%">
<mx:columns>
<mx:DataGridColumn dataField="firstName" width="80" headerText="First Name"/>
<mx:DataGridColumn dataField="lastName" width="80" headerText="Last Name" />
<mx:DataGridColumn dataField="title" width="320" headerText="Title" wordWrap="true"/>
</mx:columns>
</mx:DataGrid>
<mx:Label id="totalLbl" fontFamily="Verdana" fontSize="12" />
</mx:Panel>
</mx:Application>
In addition I have an ActionScript class in my Flex project named Speakers. This class is mapped to my Java class in package dbservice. This enables the Flex application to automatically type the object in the ArrayCollection as type Speaker instead of generic Object.
package
{
[RemoteClass(alias="dbservice.Speaker")]
public class Speaker
{
public var firstName:String = "";
public var lastName:String = "";
public var title:String = "";
public function Speaker()
{
}
}
}
If you uncomment this line in function resultGetSpeakers: // Alert.show( ObjectUtil.toString(event.result) ); you will get an Alert pop up that shows the contents of the ArrayCollection received from the Java class. If you examine the output in the Alert pop up you'll see that Flex has correctly typed each object in the ArrayCollection as type Speaker.
Summary
As I noted in my previous post, using BlazeDS to enable Flex and Java to work together provides me much of the functionality I had when using ColdFusion 8 on the back end and Flex's built in support for communicating with ColdFusion components (CFCs). Once I figured out that I had to copy the sqljdbc.jar to the lib folder everything went smoothly.
I do need to research how to have my Java class generate an exception that is returned to Flex so that when something goes wrong on the Java end, Flex receives back an error message and the fault event handler is executed. I think I just need to rethrow the Exception in each catch block until the Exception is eventually sent back to the Flex application. I did a blog article on handling ColdFusion exceptions in Flex so I hope using Java on the back end instead of ColdFusion will be similar for exception handling.
On the java side, your method needs to declare that it throws something
public Boolean doSomething(int value) throws Exception
{
try
{
.....
}
catch (Exception err)
{
throw new Exception("Some custom error message");
//or you can throw the originating error err.getLocalizedMessage()
}
}
On the Flex side in the fault handling for the remoting call
Alert.show(event.fault.rootCause.message);
The only thing I haven't figured out yet is how to throw an error code along with the message.
The ArrayList contains 2 Object from the same type. It looks like when the objectmapping takes place it uses the initial values from the Class.
I have a problem when retrieving the exception threw by a service.
I have a java custom exception which is as followed :
public class MyException extends Exception {
public String messageLog;
public String messageEncoded;
public MyException () {
}
public MyException (String message) {
super(message);
}
Here are the setter/getter of the 2 attributes ...
}
I have too a MyException in actionScript :
package MyPackage
{
[RemoteClass(alias="MyPackage.MyException")]
[Bindable]
public class MyException{
public var messageLog: String;
public var messageEncoded: String;
}
}
And I d'like to retrieve this exception in the fault method of the flex service.
private function onFault( event:FaultEvent) : void {
var myException:MyException = event.fault.rootCause as MyException;
}
but, myException is always NULL whereas event.fault.rootCause has values in it.
Do you have an idea to resolve my problem ?
Thank you for your answers !!
Bye
Thanks a lot for your wonderful blogs. For 2 days i was trying to connect to my java classes but as i have not my SQL jar file in the lib folder i was not getting any results. Thanks for all the wonderful tips.
-Chris
I am working on flex using java. I installed blazeds on my machine.
I am using RemoteObject to call one method getxmlData().
I am getting xml data from function as a string. I want to assign that xml data to a datagrid. But i am not getting a proper result :(
Basically i want such a method that returns me a XML formatted data so that i assign that data to datagrid.
Will u please tell me is that possible..........?
If yes please send me a example because its very urgent for me
Thanx in advance...... :)
Regards,
Pradip Jadhav
Any help to resolve the issue is greatly appreciated.
Where should be copied all java classes after compiling ?
Also, should i keep the current configuration ( dbservice/connection , dbservice/dao etc) in the new location ?