Showing posts with label flex. Show all posts
Showing posts with label flex. Show all posts

Tuesday, September 22, 2009

Fill parameters in LCDS Assembler methods

Last few days we spent debugging some nasty bug in the code that uses LiveCycle managed collections. We were adding/removing items to/from collections on the server side. We saw that server sent a message to the client, client did receive the message, but then it ignored it and didn't update the collection (data grid). After digging into the client side logs we found the reason of such a misbehaviour.

If you have two destinations that share the same channel

<service id="data-service" class="flex.data.DataService">
<destination id="MyFirstDestination">
<channels>
<channel ref="my-rtmp-channel"/>
</channels>
<properties>
<source>example.MyFirstAssembler</source>
</properties>
</destination>
<destination id="MySecondDestination">
<channels>
<channel ref="my-rtmp-channel"/>
</channels>
<properties>
<source>example.MySecondAssembler</source>
</properties>
</destination>
</service>

LCDS uses fillParameters as a key in the managed collections cache. That means fillParameters must be immutable.

public class MyFirstAssembler extends flex.data.assemblers.AbstractAssembler {

@Override
public int refreshFill(List fillParameters, Object newItem, boolean isCreate, Object oldItem, List changes) {
// Never change fillParameters!
}

@Override
public Collection fill(List fillParameters) {
// Never change fillParameters!
}
}

Adobe documentation says nothing about this, so keep this rule in mind when working with LCDS managed collections.

Resources

• Flex log viewer

Thursday, August 20, 2009

Measuring LiveCycle Performance: Message Size

The method of measuring performance provided by LCDS works only in situations when producer and consumer of messages are both on the Flex side. For Data Services that means you can obtain some metrics only for initial collection fill:

Original message size(B): 499
Response message size(B): 17687
Total time (s): -1250809384.8
Network Roundtrip time (s): -1250809384.868
Server processing time (s): 0.068
Server adapter time (s): 0.014
Server non-adapter time (s): 0.054

If you want to know message size and response time for messages pushed from Java server to Flex client, this method doesn't help* in the current version of LCDS (2.6.1). Adobe promised to add this feature in the future release but for now you have to use other methods. Here is what I use to measure message size.

1. JMX. By default LCDS exposes some useful metrics through JMX:



2. Flex log. If you enable log in the services-config.xml, you will see something like this in the output console for every data push:

Thread[1563082333@qtp0-0,5,main] registering write interest for Connection '1752654181'.
Thread[my-rtmp-SocketServer-Reactor1,5,main] unregistering write interest for Connection '1752654181'.
Thread[my-rtmp-SocketServer-Reactor1Writer,5,main] Connection '1752654181' starting a write.
Thread[my-rtmp-SocketServer-Reactor1Writer,5,main] chunk output stream writing message; ack state: 3
...
Thread[my-rtmp-SocketServer-Reactor1Writer,5,main] Connection '1752654181' finished a write. 233 bytes were written.

3. If you don't have access to the server, you can use any network protocol analyzer (WireShark is really good) on the client side to monitor size of packets received from the server.

* Actually, there is one undocumented feature that can be used with the described method to measure size of "create" messages, but Adobe does not recommend to use it.

Resources

Part 1: Measuring LiveCycle Performance: Errors

Thursday, July 23, 2009

Double in ActionScript, Java, and MS SQL

ActionScript 3

• There are three numeric data types in AS3: int, uint, and Number.
• They are not primitives because they can be instantiated using constructors.
• They are not "real" objects because they cannot be null, and they have default values:

myNumber:Number;
myNumber.toString(); // No NPE thrown

• Default value for type Number is NaN (not zero).

Java

• BlazeDS converts AS3 Number type to Java Double.
• NaN is idempotent of conversion:

NaN (Java) -> NaN (AS3) -> NaN (Java)

• null is not! Keep it in mind when you work with BlazeDS:

null (Java) -> 0 (AS3) -> 0.0 (Java)

If Java NaN doesn't have special meaning in your application, you can use it as a "replacement" for null in Java-Flex communication.

MS SQL

• Doesn't support NaN value for numeric columns.
• All NaN values should be replaced by null before saving entity in the database, otherwise you will get exception:

com.microsoft.sqlserver.jdbc.SQLServerException: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 24 (""): The supplied value is not a valid instance of data type real. Check the source data for invalid values. An example of an invalid value is data of numeric type with scale greater than precision.

In my current project I'm using all three languages, and I have to convert NaN to null back and forth for every object:

NaN (AS3) <-> NaN (Java) <-> null (Java) <-> null (MS SQL)

So I created small utility class that replaces all JavaBean properties of particular type from one value to another:

ExtendedPropertyUtils.replacePropertyValue(myBean, Double.NaN, null);
ExtendedPropertyUtils.replacePropertyValue(myBean, null, Double.NaN);

Feel free to use it if you have the same problem.

Resources

• Feature request to Adobe to introduce nullable Number type.
• Other solutions for similar issues in BlazeDS.

Tuesday, June 30, 2009

Measuring LiveCycle Performance: Errors

There are several ways to measure LiveCycle performance. One of them is to call appropriate method on MessagePerformanceUtils class. This approach is pretty straightforward but sometimes you might get an error:

Destination '...' either does not exist or the destination has no channels defined (and the application does not define any default channels.)

That means your are using statically configured channels and you don't package services configuration into the SWF file. To fix it, in the Flex Builder add config files folder to Flex source path and specify 'services' compiler argument:

 

Although it solves the problem, this approach is not suitable for real project as you don't want to compile SWF file with hard coded services configuration. Instead of that you would create dynamic channels on the client side, and configure them using IoC framework (i.e. Parsley, Prana or your own). And if you do that you will most likely get the following error:

Error: Message is missing MPI headers. Verify that all participants have it enabled

The reason of that is: you configured MPI headers only on the server side, but not on the Flex side. To fix it, you need to set recordMessageTimes and recordMessageSizes properties of Channel class to true. The problem is that those properties are read-only, so you cannot assign them to any value directly. But here is a trick: you can use applySettings() method:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.messaging.Channel;
import mx.messaging.ChannelSet;
import mx.messaging.channels.RTMPChannel;
import mx.messaging.events.MessageEvent;
import mx.messaging.messages.MessagePerformanceUtils;

private function init():void {
ds.channelSet = createChannelSet();
}

private function createChannelSet():ChannelSet {
var channels:Array = new Array();
channels.push(createRtmpChannel());

var result:ChannelSet = new ChannelSet();
result.channels = channels;
return result;
}

private function createRtmpChannel():Channel {
var result:Channel = ... // get it from IoC
result.applySettings(customSettings());
return result;
}

private function customSettings():XML {
return <channel-definition>
<properties>
<record-message-times>true</record-message-times>
<record-message-sizes>true</record-message-sizes>
</properties>
</channel-definition>;
}


private function messageHandler(event:MessageEvent):void {
var performanceUtils:MessagePerformanceUtils = new MessagePerformanceUtils(event.message);
statistics.text = performanceUtils.prettyPrint();
}
]]>
</mx:Script>

<mx:DataService id="ds" destination="MyDestination" result="messageHandler(event)" />
<mx:ArrayCollection id="domainObjects" />
<mx:TextArea id="statistics" />
</mx:Application>

Resources

• Check out example sources from GitHub.