February 21, 2007

How to get Single Managed Objects from a specified remote destination

Flex Campaign for Java Developers has launched on Monday, Feb. 13. This is good news for java developer who would like to make Flex app work with backend database.Christophe Coenraets  's 30 minute test drive  provides many examples about how to manipulate data with FDS. You can download the test drive server here.

Once you extracted fds-tomcat.zip , you can see flex samples that integrate with ActiveMQ , Spring 2 , Hibernate 3.2 , and HSQLDB 1.8 .  Sample 8 under testdrive folder demonstrated how to retrieve/create/update/delete data using FDS. The flex client code displays the collection managed objects retrieved from backend. However, users may have difficulty to get Single Managed Objects from flex app. It may not be clear how the identity is passed to the method. I was asked to create a sample to get Single Managed Objects from a specified remote destination. I am putting a simplified version of the example here to demonstrate how to do that.

1. Create java Assembler

1). Create your bean (MyBean.java)
First you create your bean with the data field you needed, and set/get method for each of them. 
2). Create MyService.java for your methods
For simplicity,  I have only defined two methods:
     getProducts()  -- retrieve all data, returns a list
     getProduct(int myid) --- retrieve data with myid as identity,  returns a single Object.
3). Create an Assembler (MyAssembler.java)
        public Collection fill(List fillArgs) {
                      MyService service = new MyService();
                      return service.getProducts();
        }
public Object getItem(Map identity) {
                      MyService service = new MyService();
                      MyBean mybean =  service.getProduct(((Integer) identity.get("myid")).intValue());
                      System.out.println("mybean.myid="+mybean.myid);
                      return mybean;
        }

As we can see, fill() method calls getProducts(), and getItem() calls getProduct() from MyService.
Note,  MyBean mybean =  service.getProduct(((Integer) identity.get("myid")).intValue());
Here myid is the identity field.  It must match the identity property in your destination configuration:
<identity property="myid"/>

2. Compile your java code.
As we mentioned before, MyAssembler extends AbstractAssembler which is defined in flex-messaging.jar. So when you compile the java code, you must include flex-messaging.jar in your classpath.  This jar is included in your fds flex app’s \WEB-INF\lib directory.  For example:
C:\flex\jrun4\servers\default\samples2\WEB-INF\classes>javac -classpath ./;C:\fds-tomcat\webapps\ROOT\WEB-INF\lib\flex-messaging.jar -Xlint myfds/*.java

3. Mapping client-side objects to Java objects
This is a very important step, but easy to forget. Here are some key points from doc:
1). To represent a server-side Java object in a client application, you use the [RemoteClass(alias=" ")] metadata tag to create an ActionScript object that maps directly to the Java object. You specify the fully qualified class name of the Java class as the value of alias. This is the same technique that you use to map to Java objects when using RemoteObject components.
2).You can use the [RemoteClass] metadata tag without an alias if you do not map to a Java object on the server, but you do send back your object type from the server. Your ActionScript object is serialized to a Map object when it is sent to the server, but the object returned from the server to the clients is your original ActionScript type.
3). To create a managed association between client-side and server-side objects, you also use the [Managed] metadata tag or explicitly implement the mx.data.IManaged interface. The [Managed] metadata tag ensures that the managed Contact object supports the proper change events to propagate changes between the client-based object and the server-based object.
See MyBeam.as for code details.

4. configure destination in data-management-config.xml
add the following into your data-management-config.xml, parallel to other destination definition.
<destination id="myfds">
        <adapter ref="java-dao" />
        <properties>
            <source>myfds.MyAssembler</source>
            <scope>application</scope>
            <metadata>
                <identity property="myid"/>
            </metadata>
            <network>
                <session-timeout>20</session-timeout>
                <paging enabled="false" pageSize="10" />
                <throttle-inbound policy="ERROR" max-frequency="500"/>
                <throttle-outbound policy="REPLACE" max-frequency="500"/>
            </network>
        </properties>
    </destination>
 
Note: source should point to the full qualified class name of your assembler.
         Identity should match the identity you are passing in Assembler:
         MyBean mybean =  service.getProduct(((Integer) identity.get("myid")).intValue());

5. create flex page to display the data (myDataSerivce.mxml)
1).  Retrieve data
        <mx:ArrayCollection id="mydata1"/>
        <mx:DataService id="ds1" destination="myfds" result="getData(event)"/>
        <mx:Button  id="bt1" label="Get data from fill method" click="ds1.fill(mydata1)"/> 
        <mx:Button  id="bt2" label="Get data from getItem method" click="ds1.getItem({myid:2})"/>
--- We first define a DataService ds1 which is mapped to destination named myfds. This should match <destination  id="myfds"> in data-management-config.xml.
---We call ds1.fill(mydata1) to populates an ArrayCollection mydata1, and use it as dataProvider of the datagrid.
    <mx:DataGrid dataProvider="{mydata1}"  width="300" height="100%" >
---We use ds1.getItem({myid:2}) to get single Object based on the dentity myid  we passed into getItem.
2). We use a result handler getData(event) to handle the data we retrieved from DataService. This will make sure that your event is trigged after the data has returned.   Now let’s look at resulte handler getData():
   private function getData(event:ResultEvent):void{
           if(myButton == "bt1"){
                       prodId.text = event.result[1].prodID;
                       firstName.text = event.result[1].firstName;
                       lastName.text = event.result[1].lastName;
             }else{
                        prodId.text = event.result.prodID;
                        firstName.text = event.result.firstName;
                        lastName.text = event.result.lastName;    
              }
   }
Because fill() method returns an ArrayCollection, getItem() returns a single object, so we have to treat it differently. To figure out which method is called by the user, we add EventListener to the button, and record the button id.
     private function init():void{
              bt1.addEventListener(MouseEvent.CLICK,clickHandler);
              bt2.addEventListener(MouseEvent.CLICK,clickHandler);
      }
      private function clickHandler(event:MouseEvent):void{
               myButton = event.currentTarget.id;
       }   
Now, when you click different button, different data will be displayed in "User Details" TitleWindow.

6. To test the sample:

1).  Unzip singleManagedObject.zip to your machine . Put myfds folder under your flex app’s WEB-INF/classes directory. Put myDataService.mxml and MyBean.as under you flex app’s root directory.
2). Double check the step 4 mentioned above. Restart your flex app server if needed.
3). Run myDataService.mxml, click on the two buttons and see different data displayed.


7. Troubleshooting

If there are no erros client side, but can't get data to retrun from the server side, check the following:

1). double check step 4 mentioned above, make sure your destinatio id, sourceand identity property are defined correctly. Also, make sure your database connection is valid and qurey statement are correctly retrieving data.

2). Trun on server side debug bysetting log level to debug in services-config.xml:

<target class="flex.messaging.log.ConsoleTarget" level="Debug">

Posted by lin at 05:50 PM | Comments (251)

August 25, 2006

How to access database from flex 2

We often hear people asking about how to config datasource in flex to connect to a database. It is a miss conception to think that you need to config a datasource in flex and can use that datasource to connect to a database. Then how does flex app access data from a database? Flex does not connect to DB directly, but thru Java or other means. The doc has all the details regarding this topic, and has a sample of using PHP. But it may not be clear for people who are not familiar with J2EE to connect using java. Here is the steps to create access to database using Java.

Basically, there are three steps you need to accomplish in order to communicating with database from flex.

1. Write java code to communicate to the database. We usually call this an Assembler. In here, you provide the information about what database and jdbc driver you are connecting to, and create a connection to it.

2. Then config a destination which point to the Assembler in data-management.xml. Any flex app uses this destination will use this Assembler, and connecting to the same database.

3. In your flex code, you reference the data service like this:
dsEmployee = new DataService("crm.company");
here crm.company is a destination defined in step 2.

Now, Let us look at the sample code included in the samples.war to see how it is done. Let us look at the crm sample in dataservice.

1. Open dataservice\crm\companyapp.mxml, In there you can see the following code:

dsCompany = new DataService("crm.company");
// if hibernate is used, change the destination of the data service
// dsCompany = new DataService("crm.company.hibernate");
dsCompany.addEventListener(ResultEvent.RESULT, companyResultHandler);
dsCompany.addEventListener(FaultEvent.FAULT, faultHandler);
dsCompany.addEventListener(DataConflictEvent.CONFLICT, conflictHandler);
dsCompany.autoCommit = false;

Here we are using a dataService named "crm.company" for our app.

2. Open WEF-INF\flex\data-management.xml, we can see crm.company is defined as:

<destination id="crm.company;">
<adapter ref="java-dao" />
<properties>
<source>samples.crm.CompanyAssembler</source>
<scope>application</scope>
....


This destination point to java class amples.crm.CompanyAssembler. This is our Assembler.

3. Let us see how the Assembler is constructed.
--- go to WEB-INF\src\samples\crm\CompanyAssembler.java, we can see :
CompanyDAO dao = new CompanyDAO(); //using CompanyDAO class
--- go to CompanyDAO.java, we can see
c = ConnectionHelper.getConnection(); //using ConnectionHelper class
--- go to ConnectionHelper.java, we can see the code to connect to database:

private ConnectionHelper()
{
try
{
Class.forName("org.hsqldb.jdbcDriver");
// Obtain a path to WEB-INF/classes/samples/crm
String str = URLDecoder.decode(getClass().getClassLoader().getResource("samples/crm").toString(),"UTF-8");
// Create HSQLDB JDBC URL pointing to WEB-INF/db/crm/crm (where the last crm is the datanase name)
url = "jdbc:hsqldb:" + str.substring(0, str.indexOf("classes/samples/crm")) + "db/crm/crm";
}
catch (Exception e)
{
e.printStackTrace();
}
}

This is the core of the Assembler. This is where you tell flex which database to connect.

The most important information you need to provide are the driver name and the URL. Each database has different driver name and URL, you have to make sure you are using the correct name and format. Here is a list of driver name and example of URL:


#MySql
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test

#Oracle
#driver=oracle.jdbc.OracleDriver
#url=jdbc:oracle:thin:@test:1521

#Sybase Enterprise
#driver=com.sybase.jdbc3.jdbc.SybDriver
#url=jdbc:sybase:Tds:localhost:2048/test

#Sybase Anywhere
#driver=com.sybase.jdbc3.jdbc.SybDriver
#url=jdbc:sybase:Tds:localhost:2638/test

#Informix
#driver=com.informix.jdbc.IfxDriver
#url=jdbc:informix-qli://localhost:50000/test:informixserver=testserver

#MS SQL
#driver=com.microsoft.jdbc.sqlserver.SQLServerDriver
#url please see http://msdn2.microsoft.com/en-us/library/ms378428.aspx

Posted by lin at 06:44 PM | Comments (132)