Archive for the ‘Java’ Category
Flex, Blaze DS and storing session data
Posted by cornel | Filed under Flex, Java, LCDS/Blaze DS
One of the biggest difference between the classical web applications (HTML/JS) and the rich ones (Java applets, Flex, Silverlight) is how can you store the state of the application.
With HTML applications we are used to using different hacks to store the state – either appending all the necessary information to the requests or storing the data on the server (and using a cookie or a session id to link the user and the information). Some even use a “dirty hack” and keep a small window open where you can store/retrieve all this information by using Javascript (yes, I have seen this unusual approach in several applications).
With Flex applications (and also with the other technologies mentioned before) the things are much simpler. You do not have several pages which are loaded from the server, instead you have an application that is loaded in the browser and can store the data using its internal data structures. The application will make requests to the server but only to exchange data (and in some situations to load modules), not to load whole pages. To make an analogy – it’s like having a HTML application composed of only one page that uses AJAX and JavaScript to load and build other pages (This is doable, I have seen a project where the guys were using this approach because of some strange requests from the client, but it was a nightmare to maintain and develop the application).
So you are able to store user preferences, user actions (for example the products added to a shopping cart) and other data directly into the client – the Flex application. It is more natural (in my opinion) than storing the data on the server and it is also much cheaper than buying expensive hardware for the middleware – you are using the client machine to store data so you are not overloading the server (think about having 100000 connected clients and session replication and you will understand what I’m talking about).
However there are several situations when you have to store a minimal amount of information on the server – the user identifier for example. After a successfully login authentication I have to store the user ID into the server session. After that this ID is used to check all the requests and to filter data that is sent to the client. Below I show you how to do that in Flex and what things you need to take into account. I’m using Blaze DS on the server side.
The FlexContext class can be used to access the FlexSession class which has several useful methods to set/get/remove attributes, to configure the timeout and session listeners, and so on. Here is some code that shows how to use them.
UserRoles userRoles = userHome.logIn(userName, passwd);
if (userRoles == null)
throw LoginFailedException("Invalid login");
FlexContext.getFlexSession().setAttribute("userId", userRoles.getUserId());
public Collection fill(List fillArgs, int startIndex, int numItems){
Integer userId = (Integer) FlexContext.getFlexSession().getAttribute("userId");
if (userId==null)
throw AuthenticationException("No logged user");
ASObject map = (ASObject)fillArgs.get(1);
Integer companyId = (Integer)map.get("companyId");
return new CompanyService().findProducts(
companyId,
userId);
}
Note: this is just a simple example how to use FlexSession. For user authentication and securing destinations read the LCDS developer guide, specifically the chapter titled “Securing destination”.
Important things to take into account when working with session data (taken from the developer’s guide):
- when using RTMP channel after a browser refresh the content of the FlexSession is lost; also the user will have to log in again
- you will have different FlexSession objects for HTTP, RTMP and NIOHTTP channels – so consider working with only one type of channel
- if you logged in using a RTMP channel you must log in again when switching to another type of channel
- FlexSession is not cluster aware – if a client connects to a different server in the cluster, the client receives a new FlexSession and the client will have to login again
LiveCycle Data Services 2.6 – new features (2)
Posted by cornel | Filed under Flex, Java, LCDS/Blaze DS
This is the second post describing several new features in the 2.6 version of LiveCycle Data Services– I will present several features that are related to managed associations.
In the current version several new parameters were introduced: read-only, load-on-demand, page-size, paged-collection and paged-updates.
The first one should be used for bidirectional relationships and it has the same meaning as setting inverse=”true” for a Hibernate association (the assembler will use only the other side of the relation in order to take modifications into account).
The other ones are used to improve the performance of managed associations. In the previous version you could improve it in several cases by using the “lazy” parameter. When set to true and applied to a one-to-one, one-to-many, or many-to-many association the client, by default, will load by default only the ID of the objects – the whole object will be loaded only when it is accessed for the first time. This can improve performance, but if you have a large collection you will still have to load a lot of information from the database to obtain all the ID’s and send them to the client
The new parameters remove this limitation. When you set load-on-demand for an association no values are sent to the client until the first time the association is accessed (it has the same effect as the lazy parameter for Hibernate managed collection). The page-size and paged-collection parameters are used to implement page-by-page fetching from the database/server – very important for large sets. The last one, paged-updates, controls the way in which the modifications from the client are sent to the server. If the value is false the entire collection is sent when some objects are removed or added, if true only the ID’s of these objects are send to the server. This parameter is automatically set to true if also page-size is set to true.
The effect of these parameters can be noticed especially when working with large collection – I performed some tests with a collection that has about 50000 elements (loaded in a datagrid) and it worked smoothly.
LiveCycle Data Services 2.6 – new features
Posted by cornel | Filed under Flex, Java, LCDS/Blaze DS
I plan to write some posts describing what’s new in LiveCycle Data Services 2.6 compared with the previous version (2.5.1) and also to provide some examples. There are some important new features that affect both productivity and performance
Today I will describe two of them: hibernate annotations and the automatic creation of associated destination
First, you can now use hibernate annotations because a new type of assembler was introduced – HibernateAnnotationsAssembler. This assembler extends HibernateAssembler and it has only one supplementary method – createHibernateConfiguration, which knows to create a Configuration class from the annotations.
In order to use the annotations the following steps should be followed:
a)Declare annotations for the persisted objects
b)Change Hibernate configuration files in order to use <mapping class=”class name”/> instead of <mapping resource=”resource name” />
c)Change the assembler for a destination from HibernateAssembler to HibernateAnnotationsAssembler
d)Add the following entry to the destination configuration:
<item-class>class name</item-class>
The second one: Now it is possible to create automatically associated destinations as long as the destinations you are directly using for are defined in the data-management-config.xml. Also you can skip the identity tag for a destination and then it will be automatically generated taking into account the corresponding annotations. The big advantage of this is that now you can create and manage your destinations faster. The disadvantage is that if you do not want to configure any metadata you can lose performance (for example if you do not specify load-on–demand for a large set).
Another important point is that you cannot use the automatically created destinations for fill operations – if you want to do that you will have to add an explicit destination declaration for them into data-management-config.xml.
Below is an example of data-management-config.xml which uses the new way of declaring destinations:
<?xml version="1.0" encoding="UTF-8"?>
<service id="data-service"
class="flex.data.DataService">
<adapters>
<adapter-definition id="java-dao" class="flex.data.adapters.JavaAdapter" default="true"/>
</adapters>
<default-channels>
<channel ref="my-rtmp" />
</default-channels>
<destination id="test.Company" channels="my-rtmp">
<properties>
<source>flex.data.assemblers.HibernateAnnotationsAssembler</source>
<scope>application</scope>
<item-class>test.Company</item-class>
<metadata>
<one-to-many property="products" destination="test.Product" read-only="true" page-size="20" paged-collection="true" lazy="false" />
</metadata>
<server>
<hibernate-entity>test.Company</hibernate-entity>
</server>
</properties>
</destination>
</service>
I do not declare the identity of the test.Company destination – also I do not declare the destination test.Product. The last one will be automatically generated taking into consideration the hibernate annotations.
Note: it is possible completely omit the metadata section but I wanted to configure several parameters (lazy, paged-collection) in order to improve performance.
AMF – Problems when serializing between Java and ActionScript
Posted by cornel | Filed under Flex, Java, LCDS/Blaze DS
Below are some tips and tricks when dealing with serialization between Java and ActionScript. I’ve spent some time and encountered some frustrations (especially when I was too tired) trying to understand why the value is not properly sent over the wire so I decided to document all of my mistakes. Over the time I will edit this post to add new insights
- if something seems wrong turn on debugging in services-config.xml
- a property must have a public getter and setter in order to be serialized. I know that is strange (why should I have a setter when it’s not needed?) but that’s it. I do not like it all because sometimes it breaks encapsulation
- you should take care to map the ActionScript class with the corresponding Java class using the metadata. For example [RemoteClass(alias="com.foo.model.MyClass")]
- verify that the ActionScript object is included in the SWF file. If your project does not have a reference to the AS file then it will not be included in the resulting SWF so the Java class will be serialized to a generic object
- you cannot serialize maps that have integers as keys See this bug
- when serializing Hibernate entities be sure that all of them are initialized or use some kind of Open Session in View pattern – or better build a value object to contain only the data you really need.
- a NULL number in Java is converted to 0 in ActionScript
- a Long number from Java cannot be properly converted to Number in ActionScript – you will lose precision, so you should send it packed in a different way
- take care on timezone when serializing dates because it can have different values on client and server. The date object does not contains information related to it so if you need the timezone you should send it separately
ActionScript – Compressing strings
Posted by cornel | Filed under Flex, Java
Let’s suppose that you have an AIR application and you want to send some objects to a remote server. The AMF3 format used to package the data is very compact and it will save you a lot of bandwidth but if you have large strings in your objects you can save additional bandwidth by using compression. Below is a sample code that shows how to transform the strings into a ByteArray, apply compression and send them over the wire using a remote call. The Java method uncompresses the byte array and recreates the String objects.
The AS code:
1: <mx:RemoteObject id="SendData" destination="SendData"/><mx:Script>
2: <![CDATA[
3: import test.Test;
4: import flash.utils.*;
5: private function send():void{
6: var testStrings:Array=["test data1","test data2","test data3","test data4","test data5","test data6"];
7: var bytes:ByteArray = new ByteArray();
8: for (var i:int = 0; i < testStrings.length; i++)
9: bytes.writeUTF((testStrings[i]));
10: bytes.compress(CompressionAlgorithm.ZLIB);
11: SendData.sendData(bytes);
12: }
13: ]]>
14: </mx:Script>
The Java code:
1: public void sendData(byte[] bytes) throws Exception{
2:
3: Inflater decompresser = new Inflater();
4: decompresser.setInput(bytes, 0, bytes.length);
5: byte[] result = new byte[bytes.lenght];
6: int resultLength = decompresser.inflate(result);
7: decompresser.end();
8:
9: DataInputStream ddd = new DataInputStream(new ByteArrayInputStream(result,0,resultLength));
10: try{
11: while(true){
12: System.out.println(ddd.readUTF());
13: }
14: }catch(EOFException e){
15:
16: }
17: }
LCDS Data Management – Implementing cascade delete
Posted by cornel | Filed under Flex, Java, LCDS/Blaze DS
I believe that it is almost impossible to build a domain model without dealing with the common 1-n relation. There are a lot of things to take into consideration when modeling that – so many that one can write a large book chapter. I do not intend to do that in my post – but because I’ve spent some time with a use case regarding LCDS data management I thought would be a good idea to share what I have found.
Yesterday I wrote (using Hibernate) a bidirectional 1-n relation having cascade=”all-delete-orphan”, I created the destinations for LCDS and I thought that the cascade delete would work smoothly on the client – specifically, that the delete operation would be called for all the children’s. However it seems that this operation is not yet implemented – instead of deleting them LCDS tried to set the parent to null, which obviously does not work.
Below is a code snippet describing the relation:
1: public class Company{
2:
3: public var id:int;
4: public var name:String;
5: public var products:ArrayCollection;
6:
7: }
I tried to call delete on the company and on commit I received the error: “not-null property references a null or transient value: test.Product.company”
1: var companyDataService = new DataService("Company");
2: . . . . . . . . . . . . . . . . . . . . . . . . . .
3: . . . . . . . . . . . . . . . . . . . . . . . . . .
4: companyDataService.deleteItem(companyGrid.selectedItem);
5: companyDataService.commit();
What is the workaround? Instead of directly deleting the parent first we should delete all the children, as shown below:
1: var companyDataService = new DataService("Company");
2: var productDataService = new DataService("Product");
3: . . . . . . . . . . . . . . . . . . . . . . . . . .
4: . . . . . . . . . . . . . . . . . . . . . . . . . .
5: function deleteCompany(company:Company):void{
6:
7: var companyProducts:ArrayCollection = company.products;
8: for (var i:int = companyProducts.length-1; i >= 0; i--)
9: productDataService.deleteItem(companyProducts.getItemAt(i));
10:
11: companyDataService.deleteItem(companyGrid.selectedItem);
12: companyDataService.commit();
13: }
Both destinations share the same datastore so it’s enough to call commit on the company DataService.
LiveCycle Data Services – Datastore and transactions
Posted by cornel | Filed under Flex, Java, LCDS/Blaze DS
There are situations when you need all the data entered from the UI or read from a file or socket to be consistent from a business point of view. For example if you have a reservation system you should not create the customer if the reservation operation fails; otherwise you will have inconsistent data in the database.
With LiveCycle Data Services you can control the dependency between changed data by using the DataStore object. If several DataServices components are using the same DataStore the changes are going to be enrolled in the same transaction, otherwise the data will be saved independently.
By default if there is a dependency between several DataServices (managed associations or hierarchies) the DataStore object is the same for all of them. Otherwise, if you want to have the data persisted in the same transaction you will have to manually assign the same DataStore for the DataService components.
The following code shows what happens when you are using two DataServices having a different DataStore and, after that, what happens when the DataStore object is shared. I have used P6Spy to intercept the SQL commands.
The ActionScript code
1: var company:Company = new Company();
2: company.name="company";
3: companyDataService.createItem(company);
4:
5: var customer:Customer = new Customer();
6: customer.name="customer";
7: customerDataService.createItem(customer);
8:
9:
10: companyDataService.commit();
11: customerDataService.commit();
The resulting SQL is displayed below – the data is saved in two transactions
1: statement|select max(id) from test.company|select max(id) from test.company
2: statement|insert into test.company (name, id) values (?, ?)|insert into test.company (name, id) values ('company', 15)
3: commit||
4: <another sql statements>
5: statement|select max(id) from test.customer|select max(id) from test.customer
6: statement|insert into test.customer (name, id) values (?, ?)|insert into test.customer (name, id) values ('customer', 7)
7: commit||
8:
I will add the following line of code (before creating the objects):
1: customerDataService.dataStore=companyDataService.dataStore;
Now the data is saved in the same transaction
1: statement|select max(id) from test.company|select max(id) from test.company
2: statement|insert into test.company (name, townId, id) values (?, ?, ?)|insert into test.company (name, townId, id) values ('company', '', 17)
3: statement|select max(id) from test.customer|select max(id) from test.customer
4: statement|insert into test.customer (name, townId, id) values (?, ?, ?)|insert into test.customer (name, townId, id) values ('customer', '',![]()
5: commit
For further information I strongly recommend reading the LCDS 2.6 Developer Guide – there is a section about the DataStore object
Java – Thread dump
Posted by cornel | Filed under Java
I found a very usefully tool especially for Java developers working on Windows. It is called StackTrace and it has several features which allows you to inspect and debug the JVM. The most interesting one for me is the ability to execute a thread dump. In Unix one can obtain that with SIGQUIT but I was previously unable to do that in Windows.
Why would someone need this? Sometimes your application does not respond to any commands (mostly because of deadlocks or some threads are waiting for I/O operations); in order to properly identify what is the cause you can create a thread dump and take a look on thread call stacks.
With StackTrace I found that is very easy to generate the thread dump – install Java Web Start if you do not have it and run the application. Enter the PID of the Java process (you can find it on task manager) and click on thread dump.
It also has a BeanShell console with additional commands.
LCDS Data management – Handling Java Exceptions
Posted by cornel | Filed under Flex, Java, LCDS/Blaze DS
I’m working on a small project using Flex and LCDS data management and yesterday I encountered a common use case: I wrote a method that was responsible for creating one user but only if several conditions were fulfilled. For example the username should not exist in the database, the postal code should be a valid one and the maximum length of the email should be 200 characters. It is not possible to perform the validation only on the client (because of security and data consistency problem). So my method checks this conditions and is throwing exceptions for any illegal case.
The Flex client should receive these exceptions and inform the user. I will present some code samples below in case that somebody else is interested in how to do that.
On the server part I overwrite the HibernateAssembler (create and update methods)
1:
2: public class UserAssembler extends HibernateAssembler{
3:
4: @Override
5: public void createItem(Object arg0) {
6: //Session session = hibernateManager.getSession(true);
7: User user = (User)arg0;
8: new UserService().createUser(
9: user.getName(),
10: user.getUsername(),
11: user.getPassword(),
12: user.getEmail());
13: }
14:
15: @Override
16: public void updateItem(Object arg0, Object arg1, List arg2) {
17: User user = (User)arg0;
18: new UserService().updateUser(
19: user.getId(),
20: user.getName(),
21: user.getUsername(),
22: user.getEmail());
23: }
24:
25:
26: }
The createUser and update user methods will throw an IllegalArgumentException with the proper message when the business validation fails.
1: if (findUser(userName) != null) {
2: throw new IllegalArgumentException("The user having the username " + userName + " already exists");
3: }
On the client side I have to add a fault handler to the destination. This handler is called every time when the dataservice fails on commit. For the sake of simplicity I’m assuming that it can fail only because the server validation was not successful.
1: userDataService = new DataService("User");//create the destination
2: userDataService.fill(usersArray,"com.test.User.mainFill",[]);//fill it
3: userDataService.addEventListener(DataServiceFaultEvent.FAULT,faultHandler);
The fault handler is defined below:
1: private function faultHandler(eventataServiceFaultEvent):void{
2: Alert.show(event.message.rootCause.cause.message);
3: userDataService.revertChanges(); //revert a
4: }
Assuming that the user wanted to create the username TEST and this already exists in database the following message will be displayed when the validation fails: “the user having the username TEST already exists”
How does it work? The Throwable object is serialized as any other object and is sent to the flex client packed in the event object. We can access it by “event.message.rootCause”. and we can extract the error message with “cause.message”.
Of course there are some situations when the error message can be more complex – in this case we can implement our custom exception handler and add more properties to it. Example:
1: public class UserValidationException extends Exception{
2:
3: private String email;
4: private String name;
5: private String errorMessage;
6:
7: public UserValidationException(String email, String name, String errorMessage) {
8: this.email = email;
9: this.name = name;
10: this.errorMessage = errorMessage;
11: }
12:
13: public String getEmail() {
14: return email;
15: }
16:
17: public String getName() {
18: return name;
19: }
20:
21: public String getErrorMessage() {
22: return errorMessage;
23: }
24:
25: }
1: if (findUser(userName) != null) {
2: throw new UserValidationException(email,username,"Username already exists");
3: }
On the client we can access our custom properties by event.message.rootCause.email, event.message.rootCause.userName, event.message.rootCause.errorMessage.
LCDS Data management – adding items to a destination
Posted by cornel | Filed under Java, LCDS/Blaze DS
Last week I received an interesting question: what should we do if we intend to create several items which are connected to a destination (and propagate the changes to all registered clients)?
A common use case could be:
- You have a datagrid showing all the matches from a sport competition. You can create a new match (by adding a new line in grid) or you can modify the existing ones
- And more important: you can execute a service call from the server which will add new matches, insert them into the database and propagate them on the other clients
In the first case, you create the items on the client and send the data to the server using the Flex Data Services API – so the clients are the producers. For the second you are creating the items directly on the server – so both the clients and the server are the producers. Since I have not yet seen an example showing that, I thought it would be a good idea to present one.
Below is a code snippet showing the code:
1: DataDestination dt = DataDestination.getDataDestination("Test");
2:
3: DataMessage fillMessage = InternalMessage.createMessage();
4: fillMessage.setOperation(DataMessage.CREATE_OPERATION);
5:
6: ASObject test = new ASObject();
7: test.put("name", "Test created at:"+System.nanoTime());
8: test.setType("com.test.Test");
9: BeanProxy proxy = new BeanProxy(test);
10:
11: fillMessage.setBody(proxy);
12: DataServiceTransaction tx = null;
13:
14: tx = DataServiceTransaction.begin(false);
15: Collection<Object> result = (Collection<Object>)dt.getAdapter().invoke(fillMessage);
16:
17: tx.updateItem("Test",result.toArray()[0],null,null);
18:
19: tx.commit();
You can download the files here (it is a Flex project archive). It uses MySQL – the script to create the test table is in database/create.sql