Friday, September 29, 2006

Simplified Binding Example for Flex 2

Finally figured out a way to get my code to render properly in my blog. There seems to be some confusion on how to communicate between components. This example kinda covers most of the basics.

Child Component:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Metadata>
[Event(name="childEvent", type="flash.events.Event")]
</mx:Metadata>
<mx:Script>
<![CDATA[
[Bindable]
public var testLabelText:String;
private function clickHandler():void {
var eventObj:Event = new
Event("childEvent");
dispatchEvent(eventObj); }
]]>
</mx:Script>
<mx:Binding source="test.text" destination="testLabelText" />
<mx:TextInput id="test" text="{testLabelText}"/>
<mx:Button x="167" y="0" label="Button" click="clickHandler()"/>
</mx:Canvas>

Parent Application:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:ns1="*">
<ns1:Child x="388" y="105" id="childLabel" testLabelText="{parentLabel.text}"
childEvent="status.text='child button clicked'"/>
<mx:TextInput x="388" y="131" text="{childLabel.testLabelText}" id="parentLabel"/>
<mx:Label x="388" y="161" id="status"/>
</mx:Application>

Thursday, September 28, 2006

FDS2: default refreshFill() behaviour

To test this I used the CRM sample that ships with FDS.

Problem: An arraycollection that had previously been fill()'ed using query parameters would not update if CreateItem() was called. If no query parameters were used it worked fine. UpdateItem() and DeleteItem() worked fine as well.

Solution: If you DO NOT implement your own refreshFill() in your custom Assembler class, then the default behavior of refreshFill() might be the cause of the problem.

The refreshFill() method proactively queries on whether or not data needs to be pushed out to clients. It checks to see if the newly created record would fall within the query parameters of the LAST Fill() requested.

Example: Let's say you retrieve all Roles for System ID 12 - this creates a unique fill situation for your flex app/session. DataService1.fill(roles, "12")
Let's say Jim, another user, is currently viewing all Roles for System ID 8 - he too has his own unique fill situation. DataService1.fill(roles,"8")

Now you go and add a new Role for System ID 8. Here's what happens:
FDS recognizes the fact that you are not currently looking at a list that would include this record... so you get no update (DO_NOT_EXECUTE_FILL)
FDS also 'knows' that Jim is looking at a list that should be updated, so it pushes out an update to him, EXECUTE_FILL

This keeps updates pushed out to a minimum I guess.

To test: in CRM project modify the CompanyAssembler by commenting out the RefreshFill() method completely. Run CompanyApp.mxml in debug mode with a breakpoint in companyResultHandler(). Change industry to filter by health care, add a new record with a different industry. When breakpoint kicks in, check the companies arraycollection... you should NOT see your recently added company.

Wednesday, September 27, 2006

Creating & Using localized flex libraries with Flexbuilder

Part One - Create SWC Library

1. Create a stand alone folder called Resources in your flexbuilder projects folder. Subsequently create subfolders in Resources for each locale (i.e. en_US, fr_FR). Here you will store your resource files: en_US\Main.properties, fr_FR\Main.properties, etc.
2. Start Flexbuilder. Create a flex library project called testlib. Add a source path (Project>>> Properties >>> Flex Library Build Path >>> Source Path >>> Add Folder) ${DOCUMENTS}\Resources\{locale}
3. Add a new folder to project called locale and link it (File >> New >>> Folder. At bottom click Advanced >> check "Link to Folder in the file System") to ${DOCUMENTS}\Resources
4. Create a class (code below) and then make sure it's checked in Project>>> Properties >>> Flex Library Build Path >>> Classes.
5. Create two folders under the bin folder called en_US and fr_FR
6. Change the compile arguments (Project >>> Properties >>> Flex Library Compiler >>> Additional Compiler Arguments.) to -locale fr_FR -include-resource-bundles Main
7. Hit OK then copy the compiled swc file to the fr_FR subfolder
8. Change the compile arguments to -locale en_US -include-resource-bundles Main
9. Hit OK then copied the compiled swc file to the en_US subfolder
=====================================
package com.abc.shared
{
import mx.resources.ResourceBundle;
[Bindable]
public class Company
{
public var NAME_MONKEY:String;
[ResourceBundle('Main')]
private static var bundle:ResourceBundle;
public function Company(){
NAME_MONKEY = bundle.getString("monkey");
}
}
}
=======================================
en_US\Main.properties contains:
monkey=Billy
=======================================
fr_FR\Main.properties contains:
monkey=Pierre
=======================================
Part Two - Add Library to Simple Flex App
1. Created a new Basic flex application
2. Added ${DOCUMENTS}\testlib\bin\{locale}\testlib.swc to the projects Library Path
3. Added some code to my application.mxml file (highlights below)
4. Compiled and ran it (default would be en_US)
5. Changed the project compiler argument to -locale fr_FR
6. Compiled and ran it again
7. In each case the application displayed the correct string
=======================================
import com.abc.shared.Company;
[Bindable]
private var company:Company = new Company();
...
mx:Label text="{company.NAME_MONKEY}"
=======================================
Part Three - add Library to a localized Flex App
1. In this case the application and the library projects both point to the same resource folder. I basically repeated steps 2 and 3 of part 1 for my Basic Flex App Project, using the same folders and files.
2. I then added another string to the Main.properties file(s), namely hello=Bonjour and hello=Good Day
3. In my application I put up two labels, one pulling directly from the resource file, the other referencing the class in the swc library and displaying company.NAME_MONKEY.
4. I added the path ${DOCUMENTS}\testlib\bin\en_US\testlib.swc to project library path of the project as I did in step 2 of part 2. (notice i did not use {locale}.
5. When i ran the app i was surprised to discover that even if I ran it with the compile argument of -local fr_FR it still pulled the correct string out of the properties file (pierre), even tho i was pointing only to the en_US version of the swc!
=======================================
mx:Label text="@Resource(key='hello', bundle='Main')"
mx:Label text="{company.NAME_MONKEY}"
=======================================
Conclusion
After spending a good chunk of time I finally got Flexbuilder to play along. Here's some things to watch out for.
1. If you change the output path of the flex library to bin/en_US instead of to bin and then manually copying it to en_US, that works. If however you then change it for the fr_FR version, Flexbuilder will nuke the previous bin/en_US folder and the files within
2. If you create your Main.properties and locale folders directly in the project, you get a duplicate symbol error. By placing the files outside the project and then adding a source path reference this problem goes away. However, Flexbuilder does not handle {locale} on the fly, so you will needed a linked folder to access the files during development.
3. Occasionally you might get another error stating it can't load or find main_properties. I find going into project properties and 'altering' the compiler arguements forces a recompile and makes this error go away.
4. As per my previous blog, if you plan on using foreign characters, make sure the text encoding of the resource files is set to UTF-8.

That concludes my locale adventure thru the world of flexbuilder.

Tuesday, September 26, 2006

ResourceBundle files in localized Flex apps

Problem: You are creating a flex app in flexbuilder that will be available in two languages, English and French.

You create the following:
\locale\en_US\Flexapp.properties
\locale\fr_FR\Flexapp.properties

You throw a string in each:
after=after
after=après

You compile the application and it defaults to english and comes up fine
You change the compiler arguments in project properties to -locale fr_FR and run it and instead of an è you get either a blank or a square

Solution: The text encoding type of the Flexapp.properties file in flexbuilder defaults to ISO-8859-I. Cut out all the text in the file, save it. Go to the properites of the file and change the encoding to UTF-8. Then paste your text back in and save. Now when you compile and run the letters should come up fine.

Default generator class in Hibernate Code Generator

I am using mysql, hibernate tools and eclipse 3.2.

I am generating all my java and xml code using the wizard.

The default class generator is "assigned".. for mysql and flex data services, this doesn't work so well... we need "native"

.. one way to work around this is to create a reveng.xml file where you explicitely list each table and then add a primary_key attribute that specifies the generator class. The other way is to manually edit all the hbm.xml files after each run of the wizard. Both not great.

Instead... change the default class generator for your project.

Here's how:

Add the hibernate-tools.jar to your eclipse java project.

Create the following class in your java project (i used the default package):

import org.hibernate.cfg.reveng.*;
public class MyRevEngStrategy extends DefaultReverseEngineeringStrategy {
public String getTableIdentifierStrategyName(org.hibernate.cfg.reveng.TableIdentifier t) {return "native"; }
}


in your Hibernate Code Generator, modify the reveng.strategy.
in the popup textbox type in MyRevEngStrategy, it should find it.

Close and run. Worked for me.

Wednesday, September 06, 2006

Flex to .Net via Flex Data Services - it is Possible!

When I first looked at Flex Data Services, I was a little disappointed to find that there was no built-in way to interface with .Net. Sure you can consume webservices directly from a Flex application however, you don't get the added conflict resolution, data push to client, etc stuff that comes with FDS. After toying around for a while I figured it had to be possible to access webservices through FDS, and eventually I did get it working.

Now there's no way I can cover all this in one blog entry. So I am going to try and split it up nicely:

Part one will only deal with coding the webservice in .Net in such a way that makes it interop-friendly. I will build a service that provides the basic CRUD and list functionality for a contacts table hosted in SQL server.

Part two will cover Apache Axis and the wsdl2java tool, and we will look at what code gets generated and hurdles other .Net folks might run into. Once we have our basic java console application up and running, and successfully consuming the .Net webservice, we'll move on to part 3.

In part three I will build a custom Assembler (extending the flex.data.assemblers.AbstractAssembler class. Then we'll create a destination for our new .Net webservice Assembler class.

In the fourth and final part of the series, I will build a Flex application with full list, modify, delete and add functionality, and takes advantage of the data management features of Flex Data Services 2.

Why Hibernate with Flex Data Services 2?

FDS 2 does not come without a learning curve, especially for those of us new to the Java world. I've been trying to find a way to speed things up, and at first I kind of ignored the whole Hibernate side of the equation. But eventually I got curious and I have to say, after tinkering around extensively... I'm pretty excited.

Hibernate has some pretty cool plugin tools for Eclipse/Flexbuilder . With these tools you can reverse-engineer all your hibernate xml mapping files and even the java object classes. What that means is that you can just focus on getting your db design done, and the tools will do the rest. Flex Data Services comes with the HibernateAssembler class, and with it they've done all the java coding for you. At least enough to get you started. What's even better is that in the fds2\resources\camples\assemblers, Adobe has thrown in the source code, so down the line you can modify and enhance it as needed.

In the end, all you need is a database with an Employee table, the hibernate.cfg.xml, the samples/crm/Employee.java and the samples/crm/Employee.hbm.xml files for the HibernateAssembler to work. Create the destinations in your data-management-config.xml and away you go - no Java required!

Pointing Hibernate to MySql

Continuing on from my last post, we'll now try getting the crm sampel working with MySQL and Hibernate. Again, I'm going to try and keep this simple. Here's the steps.


1. Recreate the db in mysql. If you look in samples\web-inf\db\crm you will find a file called crm.script which has the sql statements you will need.

2. Make sure you have the mysql-jdbc-connector libraries. Put the .jar file in the samples\web-inf\lib folder.

3. Edit the HsqlServlet.java file in samples\web-inf\src\samples\crm as follows:

  • change the connection.driver_class to com.mysql.jdbc.Driver
  • change the connection.url to jdbc:mysql://127.0.0.1:3306/crm (assuming defaults)
  • change the connection.username and connection.password
  • change the dialect to org.hibernate.dialect.MySQLDialect
  • comment out the hbm2ddl.auto line

4. Compile the HsqlServlet.java file into samples\web-inf\classes.

5. Run FDS. Keep an eye out on the console for any errors.

6. Open the companyapp.mxml in a browser.

Tuesday, September 05, 2006

Flex Data Services - CRM Sample using Hibernate

I've played around a great deal with the sample applications that shipped with Flex Data Services. In particular I was intrigued in getting the CRM example to work with Hibernate. I'm not going to get too deep into the particulars. Here's just a good overview of what needed to be done or what purpose was served, by file.

samples\web-inf\web.xml
In this file there's a section of code that needs to be uncommented. This enables a servlet that runs when jrun boots up, which creates a file called hibernate.cfg.xml and puts it in the samples\web-inf\classes\samples folder.

samples\web-inf\classes\samples\hibernate.cfg.xml
Once you uncomment the web.xml and start up FDS once, this file should exist. In here you will see some basic configuration stuff that basically creates a jdbc connection to an hsqldb database (a java sql engine that runs within the context of the app server). Also you will not two mapping files, explained next.

samples\web-inf\classes\samples\crm\employee.hbm.xml
samples\web-inf\classes\samples\crm\company.hbm.xml

These two files serve to map sql tables & columns to java classes and properties, found in samples.crm.Employee and samples.crm.Company, respectively. You can find the source code for these files (if you're new to the java world like me) in samples\web-inf\src\(...)

samples\web-inf\flex\data-management-config.xml
In this file there are 2 sections that need to be uncommented out. You can leave the other destinations in place and just uncomment the destinations crm.employee.hibernate and crm.company.hibernate. Once you've saved these changes it's a good idea to restart FDS.

samples\dataservice\crm\companyapp.mxml
First go ahead and launch this in a browser, make sure FDS is running. The default address would be http://localhost:8700/samples/dataservice/crm/companyapp.mxml and make sure everything is still working fine. At this point the application is still using the custom company and employee assembler classes, not hibernate. If everything works fine, go ahead and edit the file. You'll need to find all the comments that pertain to Hibernate and then comment out the line preceding it, while uncommenting the line the line that follows, as per the comment instructions. Save your changes.

Now go ahead and reload the companyapp.mxml page in the browser. Keep the FDS console open and if everything went well, you can watch as hibernate generates and logs query statements.