Flex Pasta » Java
2 1/2 years ago I published annotations for BlazeDS that allowed for placing tags on Java getters/setters to determine if a value is serialized to the Flex client. Soon I will publish a 3 part BlazeDS extension that does the following(with annotations of course).
- Serialization Profiles with BlazeDS - This was mentioned in a previous post and I will be releasing this soon along with the rest of the annotations code.
- Mate Code Generator - More details to come, but if you are familiar with the Mate Framework, this code generator will create complete EventMaps/DataManagers/Events from Java services(and it runs for the life of the project).
- Model Code Generator - This isn’t the kind where a button is pressed once to create Flex remote object bean classes. I don’t like code generators that only generate the file initially. I also don’t like when a code generator creates generated code that then the developer must extend(ex: Customer extends _Customer). I have created a code generator that does neither but still can be run endless times for the life of the project. Here is an example output file. The developer can add any code to the area marked as such in comments. Every time the generator is run it will clear out the rest of the file and regenerate the properties from the matching Java class.
/* File Generated by FlexPastaDomainGenerator v. 1.0
* Only edit the file in the designated area.
* All other changes will be lost
*/
package com.company.model
{[Bindable]
[RemoteClass(alias=”com.company.model.Address”)]
public class Address
{
public var country:String;
public var lot:String;
public var parcel:String;
public var state:String;
public var streetName:String;
public var streetNumber:String;
public var subdivision:String;
public var uuid:String;
public var zipCode:String;//**********EDIT AREA**********
//**********END EDIT**********
}
}
Looks for more details in the coming weeks!
The problem is common when using BlazeDS and Hibernate: Lazy loading is not possible without inflating a large object graph. The most common solution to the problem is to “break” parts of the model into manageable pieces based on how the application works. So while there should be a many to many relationship between two entities, maybe it is just left off to avoid problems. This can fragment the object model. When the object model becomes fragmented, it causes extra code to be written to make up for the lack of a correct object model.
A different approach to the problem
Another approach to keep the model pure but avoid large data loads is to use a serialization profile. This means keeping the model the the way it should be, but defining profiles on service methods to determine what parts of the graph are inflated during serialization. Most of the time a service provides functionality to one part of an application, or multiple parts that are used in a similar manner. It is more common for an object model to be used differently in different places of an application. Therefore it makes sense to single out service calls to determine object inflation.
How BlazeDS comes into it
A while back I wrote a post regarding annotations for remote objects with BlazeDS. I have expanded the annotated bean proxy to include this concept of a serialization profile. I also created a small demo project to show just how easy serialization profiles could be used.
My ‘Pet’ Project
Take a look at the following image showing the model for the pet project. As you can see, the pet class has 4 one to many relationships.
Now take a basic flex application that has a datagrid with a list of pets. Clicking on a pet results in the details being shown.
This query in hibernate is something like this: getHibernateTemplate().loadAll(Pet.class); The result would be one query for the list of pets plus and then n+ queries for all of the collections as serialization happens. In this case the whole database of records would be loaded. For 3 record this is ok, but for a large number of pets we could be waiting a long time as the entire database is loaded simply because we cannot have lazy collections.
How to use a Serialization Profile
Now let’s show how a serialization profile would help. Here is the small snipet of code for my service method:
@SerializationProfile(profile = “petList”)
public List<Pet> getAllPets()
{
return petDao.getAllPets();
}
@SerializationProfile(profile = “petDetails”)
public Pet getPetById(Integer id)
{
return petDao.getPetById(id);
}
I have used the new annotation tag @SerializationProfile to define the profile for the remote methods. The second step is to set profiles on lazy loaded one to many collections in my Pet class.
@FlexField(profiles = “petDetails”)
@OneToMany(fetch = FetchType.LAZY, mappedBy = “pet”)
public List<VetVisit> getVetVisits()
{
return vetVisits;
}
I can place one to many profiles on each getter method to determine its serialization profile. I only placed one getter example here, but all 4 of my collections on Pet.class have been set the same way. The all have a profile of petDetails. This tells the proxy, hey, I only want to load these collections when the profile method is petDetails. Our method call to getAllPets() now results in one query ran and only a list of pets returned, without all the extras. Once a user clicks on a pet, the getPetById() method is called. This method has a profile of petDetails, so all the collections are inflated upon return to flex.
Summary
By using the new Serialization Profile annotation and bean proxy, we were able to keep our model pure without inflating everything. I was able to inflate the Pet object when demanded by the application and avoid it when not needed. When other parts of the Pet application are written, they can utilize the full power of the object model without fear of various collections inflating themselves. The performance effect(of a serialization profile) is very minimal and the performance hit(while minor) is only the first time an object is serialized.
Do you think this would be helpful on a project?
A hand full of bugs have been field with Adobe’s Flash Player regarding problems uploading with Firefox.
https://bugs.adobe.com/jira/browse/FP-78
https://bugs.adobe.com/jira/browse/FP-1044
https://bugs.adobe.com/jira/browse/FP-419
https://bugs.adobe.com/jira/browse/FP-201
https://bugs.adobe.com/jira/browse/FP-568
There have also been plenty of posts describing the problem.
http://sethonflex.blogspot.com/2007/10/flex-and-filereferenceupload-using.html
http://thanksmister.com/index.php/archive/firefox-flex-urlrequest-and-sessions-issue/
http://stackoverflow.com/questions/351258/how-do-i-make-flex-file-upload-work-on-firefox-and-safari
In Flash 9, there is really no good way around this problem. However, in Flash 10 / Flex 4, an enhancement to flash.net.FileReference makes it possible to read the contents of a file before it is uploaded. Meaning that the file can be uploaded in different ways then can be done in Flash 9. The following example shows how easy file uploading can be and is not tied to SSL, Firefox, IE, Chrome, etc. This method is using Java server side with BlazeDS, but could be modified to work with other setups.
First, look at the application file.
<?xml version=”1.0″ encoding=”utf-8″?>
<s:Applicationxmlns:fx=”http://ns.adobe.com/mxml/2009“ xmlns:s=”library://ns.adobe.com/flex/spark“ xmlns:mx=”library://ns.adobe.com/flex/halo” minWidth=”1024” minHeight=”768“>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Declarations>
<!–
Place non-visual elements (e.g., services, value objects) here –>
<s:RemoteObject id=”upload” destination=”uploadService” endpoint=”http://localhost/Context/messagebroker/amf“/>
</fx:Declarations>
<fx:Script>
<![CDATA[
private var fr:FileReference = new FileReference();
private function test():void
{
fr.browse();
fr.addEventListener(Event.SELECT, selected);
fr.addEventListener(Event.COMPLETE, complete)
}
private function selected(event:Event):void
{
fr.load();
}
private function complete(event:Event):void
{
var byteArray:ByteArray = fr.data;
upload.uploadFile(byteArray);
}
]]>
</fx:Script>
<s:Button label=”Upload” click=”test()”/>
</s:Application>
Notice the fr.load(); This will actually load the file and make it’s contents available to us in the code. The complete handler than takes those contents as a byte array and calls the remote “uploadService” passing in that byte array. Here is the simple Java code for the service method “uploadFile”:
public void uploadFile(byte[] file)
{
// Do something here to save the file
}
It is that easy in Flash 10. By allowing access to the file, the upload process is quick and painless.
I wake up to the phone ringing. I check the clock: 2:30 A.M on January 5th. Groggy and dizzy I answer the phone. It is my brother. I am in Cincinnati(Eastern Time) and he lives in San Fransisco(Pacific Time). “Why are you calling me at 2:30 in the morning”, I ask him. “It’s only 11:30 P.M. here”, he says, “and I am calling to tell you that I am father to a baby boy named Johnny!”. “Congratulations!!”, I tell him. We chat a bit more and then hang up. The whole family is excited. We had all placed bets on which day little Johnny would be born. I had picked January 5th. “Yes!!!”, I say. I have won. Or have I?? Johnny is born on January 5th eastern time, but January 4th pacific time. So who wins? Johnny’s birth date of record is January 4th. But when was he born? The answer to that depends on where I am.
Flex and Timezones
Assume now the nurse is entering the date and time of birth(when the baby is born) in the Flex baby tracker application: Jan. 4, 11:30 PM Pacific. Also assume that the developer has coded the app to use a Date object in Flex, and java.util.Date in the Java remote class and that the application servers sits in Cincinnati(eastern time). When the nurse saves the record, AMF will transfer over the date object as milliseconds since 1970. When it hits the server in eastern time, the date is now stored as 2:30 AM January 5th. If the nurse wanted to record the birth date, then as a developer, I would not want to use a date. In this instance, the date should be January 4th, globally, regardless of location or timezone. A string or integer value should be used. A Flex mx:DateChooser component is dangerous. If the nurse picks a birthdate in a mx:DateChooser, the date would be created locally on the client pc as Jan 4th, 2010 at midnight(00:00:00). When transferred to a server in eastern time, this date would become Jan 4th, 2010 at 3AM. This is not what we want. Someone in Hawaii viewing this baby record would get the date Jan 3rd, 2010 at 10PM. The birth date January 4th should appear globally to anyone the same way.
Solution:
Option 1 - Have the date chooser covert the value to a string like 01-04-2010 and then transfer the value to the server and store it as a string. This would ensure it globally being January 4th.
Option 2 - Option 1 has a problem: How do I do a query for all birthdays in January? A little tricky with a string variable. Option 2 is to covert the date chooser value to an integer in the form yyyymmdd(20100104). The date is transferred and stored in the database as an integer. Doing this makes it easier to query. For example, select * from baby where birthdate > 20100101 and birthdate < 20100131. The data still keeps the integrity for greater than and less than operations.
Have a headache yet? Go take some Tylenol:)
Time travel between Flex, BlazeDS, and Java can be quite complicated to keep the three correctly in sync. Time travel, aka remote objects that carry date members, can mean different things to each piece of software. Java is very time zone aware, where as Flex is very time zone unaware. Since time in Java, BlazeDS, and Flex is defined in milliseconds since 1970, it can mean translation via BlazeDS can result in inconsistencies. How can this be?
Java
When it comes to time zones and daylight savings time, Java is very smart. It uses the Olson Database to determine how to translate a time in milliseconds since 1970 into an actual date/time based on the current timezone. The Olson Database knows when daylight savings time started and ended in each time zone each year. If you remember, prior to 2007, daylight savings time in the United States started the first weekend in April and ended the last weekend in October. But, it an effort to be more energy conscious, the government changed it to the second weekend of March through the first weekend in November. Java knows about daylight savings time and time zones as they existed in the past. Therefore, milliseconds since 1970 for a date of March 15th, 2006 in Java maybe different than on another platform.
BlazeDS
BlazeDS(and the Flex RPC library) translate time via AMF between Flex and Java. It knows nothing about time zones or daylight savings time. When sending a remote object that contains a date, it will be converted to date.getTime() - milliseconds since 1970. BlazeDS is always translating dates in the present and has no understanding of the client and server’s time awareness.
Flex
Flex only knows about daylight savings time now. Meaning, Flex will think March 15th, 2006 was during daylight savings time. But in 2006, daylight savings time didn’t start until April so Java will have a different answer than Flex to milliseconds since 1970? To be fair, it is not Flex or Flash that aren’t time zone savy. It is the user’s browser and computer that are ultimately at fault. The flash player will just pull the information from them. Imagine if a user enters a date/time today(during daylight savings time). At some point in the future, the government changes daylight savings time to start tomorrow. After the change occurs, the date the user entered will be correct in Java(because Java knows of daylight savings time in the past), but will be one hour off when displayed in Flex.
Creating an identical date/time object in both Flex and Java can cause different results for milliseconds since 1970. Take for example a user entering a birth date on a Flex form as March 15th, 2006 and another user entering a birth date as March 15th, 2009. The following screen shots show a simple Flex app with a date chooser and a label showing the time in milliseconds since 1970.
When the user selects March 15, 2009, the date is correctly translated to Java via BlazeDS as March 15th, 2009. Notice that Java says EDT(Eastern Daylight Time).
Now look at the same test for March 15, 2006.
Notice for the March 15th, 2006 date translation is incorrect. The date given to us via BlazeDS will actually be on March 14th at 11pm. Java correctly identifies the time zone EST(Eastern Standard Time). Java is correct. Back in 2006, March 15th was during eastern standard time. Because this date today is in eastern daylight time, Flex incorrectly reports milliseconds since 1970 to a number off by one hour.
Now the incorrect birth date is put in the database as March 14th!! Want to run a query to find all people born on March 15th, 2006? That could be a problem. There are several ways to solve this problem. One might be creating a JustADate class that has year, month, day fields on it so BlazeDS will send it without using getTime(). Another option might be sending date only fields as strings to avoid the getTime() translation. When working with dates in Flex, BlazeDS, and Java, be sure to consider time travel and its inconsistencies!
“Once confined to fantasy and science fiction, time travel is now simply an engineering problem.” -Michio Kaku






