Wednesday, October 22, 2008

LiveCycle Data Services Capicity Planning Guide

Adobe has published a Capacity Planning Guide white paper that gives guidance and test results on how LiveCycle Data Services scales up to to thousands of end users.

Stealing from the introduction:

This document presents the results of software benchmark tests performed by Adobe engineers in 2008. These tests show how LiveCycle Data Services 2.6 software can scale and perform under load using various messaging scenarios that represent real-world situations. The goal of this paper is to provide a starting point for those who need to plan a hardware and software infrastructure that can scale to meet peak demand.

Check it out here: Adobe LiveCycle Data Services 2.6 Capacity Planning Guide

Friday, August 29, 2008

BlazeDS HTML LiveDocs updated

The BlazeDS HTML Livedocs are now updated to match the updated PDF files I posted about a while ago. This brings the BlazeDS document up to date with the release of LCDS 2.6, including a much better organization of the chapters to explain the RPC and messaging concepts.

I think most folks use the HTML version, not the PDF (I know I do), so it was pretty confusing having two different versions of the doc around, with the most popular being the out of date version.

Thanks to the documentation team for responding to our complaints!

Invoking Flash remoting requests using the Java AMF Library

In a follow up to my recent post about the new Java AMF client available in BlazeDS, I was recently asked how to send a 'typical' flash remoting request to ColdFusion.

Here is how you do that in Java:

Object[] args = ...
String sourceName = "my.cfc.path.Component";
RemotingMessage message = new RemotingMessage();
message.setMessageId(flex.messaging.util.UUIDUtils.createUUID());
message.setOperation("myCfcFunctionName");
message.setBody(args);
message.setSource(sourceName);
message.setDestination("ColdFusion");
Object returnValue = amfConnection.call(null, message);

and accessing the body of the response like this.

Object body = ((AcknowledgeMessage) returnValue).getBody();

Not that the "args" variable is an array of the arguments you are passing to the CFC function. It can also be a Java List, or a simple object. See the BlazeDS source for RemotingMessage.java for details. This sends the same kind of message that the mx:RemoteObject tag does in MXML, so in ColdFusion terms you are using the "Flash Remoting Update". If you use the 'raw' AMFConnection API, you would be using the "classic" Flash Remoting. Both will work, but I recommend using the RemotingMessage style.

What does this do for you? Well if you are exclusively using Flex as a client, not much. But if you would like to write Java code to invoke a CFC, this alows you to do that very easily.

Wednesday, August 06, 2008

New Java AMF Client feature in BlazeDS

You may not have noticed, (what, you aren't subscribed to the BlazeDS commits forum?) but a few weeks ago Mete committed an enhancement that adds a Java AMF client API to the flex-messaging-core.jar file in BlazeDS.

You can find a specification posted here and its linked from the Developer Documentation page.

What does this do? Well, you can use this API to call (from Java) Flash Remoting end points in BlazeDS, LiveCycle Data Services, ColdFusion, PHP or whatever you have that supports AMF. Which of course is a published specification.

This feature is available in any nightly build after 3.1.0.2602 or in the trunk nightly build. Find those builds on the BlazeDS build download page.

Thursday, July 31, 2008

BlazeDS Documentation Update

The documentation team has posted an update of the BlazeDS documentation that includes all of the relevant content from the new LCDS 2.6 Developers Guide.

By the way, here is a gateway page to all of the LCDS 2.6 documentation, which has been reorganized to be much easier to read and use.

The HTML in Livedocs has not been updated yet, but a new PDF version is available.

See details on the Flex documentation blog here.

Tuesday, July 22, 2008

AIR Data Synchronization via LiveCycle Data Services ES 2.6

This is a nice article on the offline sync feature of LiveCycle Data Services 2.6 - AIR Data Synchronization via LiveCycle Data Services ES 2.6. It was written by John C. Bland II.

I worked on this feature for LCDS 2.6 (which by the way just got released last week). The SQLite DB built in to AIR is really nice to work with and the Actionscript APIs for using it were nicely designed by Jason, one of my coworkers here at Adobe.

I think the offline feature is pretty neat. There is still lots of room for improvement, but the basics that we do have are pretty powerful. We are in the planning stages for the next LCDS release and I expect that improving our offline story, particularly for AIR, will be on the list.

Check it out!

Friday, July 11, 2008

Setting optional parameters in the SMS Gateway

Paul Hastings sent me a questions yesterday about the ColdFusion SMS Event Gateway. He wanted to know how users could set their own optional parameters in the text messages sent from the gateway. He blogged my answer, which means I can just link to it without writing it up myself.

A few notes: If the gateway receives optional parameters in a message, it will be included in the data struct that is returned to the onIncomingMessage CFC function under the "optionalParameters" key. CFDump to a file or console is your friend in these cases.

If you do not format the key (has to be able to be parsed by the Java Short.decode(String) function) or the value (must be a byte[], which in CFML is a "binary object") correctly the setting is silently ignored.

This functionality has been in the SMS Gateway since 7.0.2. It is not in the 7 or 8 documentation, which is bad, but we will correct this moving forward.

Wednesday, July 02, 2008

Special Axis types and ColdFusion

A mere hours after I posted about how to figure out why CF does not like the arguments you are passing to invoke an operation in a web service, Sean Corfield pinged me with a problem exactly like that. Feeling pretty good, I pointed him to the post I made hours before. He of course had already tried the wsdl2java trick and still could not get CF to do the deed.

He sent me the WSDL and his test and everything looked OK. It was a pretty complex input to an operation (names changed to protect privacy):
public com.example.RegisterResponse register(com.example.RegisterRequest parameters)

The RegisterRequest was a JavaBean that included the following members:

private java.lang.String sessionID;
private com.example.ThingyType importantThing;
private com.example.ThingyType otherThingy;
private com.example.ThingsILike likeList;
private org.apache.axis.types.UnsignedInt duration;

Fun stuff. Sean had done all the right things creating CFML structs that matched each of the JavaBean types (ThingyType, and ThingsILike). ThingsILike was interesting because it contained a single item that was an array. His code was right on the money here, notice that the ThingsILike object had a single member named "things" that was the array. Here is what he did and (rightfully) expected to work:

thing1 = { name = "bobby", location = "Portland" };
thing2 = { name = "sally", location = "Boston" };
iLike = { things = ["Bunnies", "Kittens", "Koalas", "ColdFusion"] };
args = { sessionID = 0, importantThing = thing1, otherThingy = thing2, likeList = iLike, duration = 60 };

Bonus points to Sean for using the new ColdFusion 8 syntax to create arrays and structures. So the array wasn't the problem. What was? Well you may have noticed that duration is listed in the Java function as being of type org.apache.axis.types.UnsignedInt. this is because the XMl Schema type in the WSDL says that the number is an Unsigned Integer:

<xsd:element name="Duration" type="xsd:unsignedInt" />

As an aside here, XML Schema has a lot of different types that elements can be. Things like NonNegativeInteger, nonPositiveInteger and other slightly wacky things. Java on the other hand doesn't have any of these. So the Axis folks (which included me) came up with a set of classes that would enforce the limitations of the Schema types and allow you to 'round trip' a service that you generated from a WSDL, then deployed and allowed Axis to create the WSDL from the Java. This is good, and when you are writing Java directly against the WSDL2Java generated code no big deal because you can pretty quickly notice that you need one of these types and make one.

But back to ColdFusion. In order for the "60" given in the code to turn in to the class UnsignedInt, ColdFusion has to be smart about how to construct this object. It's not. However there is a simple work around - create the object yourself. Here is how that would look:

duration = CreateObject("java", "org.apache.axis.types.UnsignedInt").init(60);

Notice that I call init(60), which invokes the constructor of the class. Setting this object as the value of duration in our argument structure works like a charm. So problem solved, and I hope that we can make CF smarter about these types in a future release, but encountering these XML Schema types in the wild is pretty rare so I don't expect you will need this workaround much if at all.

Using WSDL2Java to figure out CFML arguments to a Web Service

For some reason I haven't actually even written up the procedure I use to figure out how to invoke a web service from ColdFusion when people ask me what CFML structures they need to pass in so CF can match the arguments to a function in the Apache Axis stub it generated from the WSDL.

I encourage you to read the reprint of Consuming Web Service complex types in ColdFusion which goes in to some great detail on how to figure this out. This posting is going to be the cheat sheet version of that.

First, if you can't figure out why CF gives you an error about not finding the right function to invoke, you probably aren't passing in the right arguments. How do you figure these out? You run the WSDL2Java command in Apache Axis, and take a look at the Java code CF is trying to invoke. Here's a batch script for Windows and ColdFusion 8:

set CFLIB=c:\ColdFusion8\lib
set CLASSPATH=%CFLIB%\axis.jar;%CFLIB%\wsdl4j-1.5.1.jar;%CFLIB%\log4j-1.2.12.jar;%CFLIB%\commons-logging.1.0.4.jar;%CFLIB%\commons-discovery-0.2.jar;%CFLIB%\xercesImpl.jar;%CFLIB%\xml-apis.jar;%CFLIB%\saaj.jar;%CFLIB%\jaxrpc.jar;%CFLIB\..\runtime\lib\jrun.jar

java org.apache.axis.wsdl.WSDL2Java %1 %2 %3 %4 %5 %6 %7 %8 %9


Save this as "wsdl2java.bat". This will also work with ColdFusion MX 7. You will need to change the log4j-1.2.12.jar file to be just log4j.jar.

Change to an empty directory. Wsdl2Java is going to spew directories and files all over the current directory, so I find its best to create something like c:\temp\x and run the command from there. Otherwise you can give the -o option and specify the output directory.

wsdl2java.bat -v -o c:\temp\x http://example.com/path/to/wsdl

The -v is the verbose argument. I like to see all the files that are being created.

Now you can start browsing through the Java files and see what we have to work with. Look for a file named the same as the "portType" name in your WSDL. For instance, if your WSDL contains a portType that looks like this:
<wsdl:portType name="CodexWS">

Then you would look for CodexWS.java. This will be the interface of the service, and you should be able to see each of the operations in your service, and the Java objects that ColdFusion will be trying to create to call them.

How do you create the CFML to pass to the operation? Simple. Most likely there will be JavaBeans created from the XML Schema in the WSDL. These aren't that big of a deal, basically they are structures with name/value pairs, which is why you will need to make a CFML structure with the same names in it.

Check out this Bean:

public class AddThingRequest implements java.io.Serializable {
private java.lang.String manifestfile;
private java.lang.String uri;
private java.lang.String certlevel;
private java.lang.String statusname;
private com.adobe.Ldapcredentials ldapcredentials;
...

There would be a getManifest() and setManifest() function in the bean also, along with a get and set function for each one of the other bean properties. These only matter because this is what CF will look for when it finds a "manifest" entry in your CFML structure. So what would we pass in to an operation that takes an "AddThingRequest" object as an argument? Something like this:

atr = StructNew();
atr.manifestfile = "foo";
atr.uri = "uri:gimme";
atr.certlevel = "high";
atr.statusname = "Alert";
art.ldapcredentials = mycreds;

But wait, you say, what is "mycreds"? Well, the process just repeats here - we would go find the com.adobe.Ldapcredentials Java object, look at the properties it contains and make a CFML structure (mycreds in this example above) that matches it.

So that's all (ha!) there is to it. You now have the magic to invoke almost any web service out there. This isn't exactly "making the hard stuff easy" (that would be that WSDL2CFML tool I haven't written yet), but once you understand how the process works, you should very rarely have to resort to grunging through the Java code generated from a WSDL to construct the right CFML inputs. My opinion is that Web Services should be designed to present an easy to use interface, simple inputs with only reasonably complex outputs. I know that isn't the case for many services out there that you are forced to used. But with this technique you should be able to easily figure out how to use the more complex ones.

Friday, June 27, 2008

CFUnited LiveCycle and ColdFusion session online

For those who were unable to attend my session at CFUnited this year, the CFUnited blog has posted the URLs of many of the sessions that got recorded via Adobe Connect. My session was recorded and is now online here. This talk has some new content about the difference between BlazeDS and LiveCycle Data Services.

The audio is very "hot" - when setting up the microphone the output from the in-room audio system was just super loud so I am distorted, but I think the session is still tolerably understandable. I am much better in person however. See you at MAX 2008. :-)

Wednesday, May 28, 2008

MAX 2008 registration is now open

MAX 2008 registration is now open. Check out more information at the MAX web site here.

MAX is the descendant of the Allaire ColdFusion Developers Conference, which turned in to the Macromedia DevCon, which morphed in to Macromedia MAX and is now Adobe MAX. This year it is going to be in San Francisco, which will be great. Not as great as MAX 2006 in Las Vegas IMHO, but SF is a fun place to visit. :-)

I will be giving a session on Livecycle Data Services and BlazeDS deployment, probably with a slant toward integrating with ColdFusion. But anyone who is interested in learning more about Data Services, BlazeDS and how they can leverage the power of these technologies in to their web applications using Flex should get a lot out of it.

Tuesday, May 20, 2008

Offline sync sample application

Christophe has posted another great LCDS sample application that uses the offline synchronization feature of Data Services to good effect. This is one of the features that I worked on for the upcoming LiveCycle Data Services 2.6 release, which is in beta right now up on Adobe Labs. Check it out here.

Friday, May 09, 2008

New blog on FlexBuilder 4 features

Scott Evans, a lead engineer on the FlexBulder team, has started a new blog - Getting and Setting that will be for public discussion of new FlexBuilder 4 IDE features. I am really excited by some of the "IDE Maturity" features that I've seen on the drawing boards for the next FlexBuilder. As an avid InteilliJ IDEA user for my Java development, my tumultuous affair with builder has been a tough ride. I am drooling over most of the improvements that I see coming.

He starts out with his lead post asking about how you use Actionscript "getters" and "setters". Do you use a special prefix for your class member variables? Do you create functions as a matter of course? Or only when you actually need them? Post a comment and let Scott know.

I also encourage you to check out Tim Buntel's $100 test posting asking for feedback on what feature you think are worth investing our resources in. He has recently posted an update on the results, which, I pointed out to Scott, valued the getter/setter generation very high on the list. Of course improving the MXML compiler speed was at the top of many folks lists.

Tuesday, May 06, 2008

Conflict bug in ColdFusion Extensions for Eclipse

If you are trying to use ColdFusion as the back end to a LiveCycle Data Services Data Management application, you should know about the extensions to Eclipse/FlexBuilder that ship with ColdFusion 8 (and 7.0.2). You can download them from the CF download page here.

One of the great features of these extensions is the ability to use the RDS Dataview window to generate CFCs and Actionscript classes from your database tables, saving you lots of tedious typing. To do this go to Window -> Show View -> Other... -> ColdFusion -> RDS Dataview. This will open the panel that allows you to browse your data sources via RDS. Right click on the panel and select "RDS Configuation" to open the settings dialog and configure your RDS server. For instance I have my local CF server configured as:
Description: localhost
Host Name: 127.0.0.1
Port Number: 8500

I am using the built in web server (port 8500) and the stand alone configuration, so there is no context root. You must have RDS turned on in your installation (see technote here for details) .

Once you have the panel open and have access to your ColdFusion data sources, you can open up a DSN, open the "Tables" folder and right-click on a table name. You can show the contents of the table or open up the Query Viewer (on Windows) and run an arbitrary query. At the bottom of the context menu you should see "ColdFusion Wizards". Select that and the sub menu is "Create CFC".

This gives you a dialog that allows you to create CFCs that correspond to this table in your database. Not only that, it will create either "Active Record" or "Bean/DAO" style CFCs that know how to create, read, update and delete (CRUD) themselves! This is pretty cool in an of itself, but if you are using LCDS to write a Data Management application, this wizard can also write the Assembler CFC (the component that has the required methods for LCDS) for you. This selection is named "LiveCycle Data Services Assembler CFC's". It will generate the Bean CFC (representing 1 row of data), the Data Access Object (DAO) CFC, which has read/write/update/delete methods and the Assembler CFC itself, which uses the Bean and the DAO CFCs to do its work.

Neat, huh?

You can even create the Actionscript class that is used in the Flex application to represent your data when it gets to the client.

This code will work right out of the gate if you have a simple single table application. You can use it as a jump start for more complex applications that have multiple assemblers and a more complex data dependencies.

Unfortunately, there is a bug in the generated code when it comes to conflict detection. As part of the "sync" method, there are private functions which perform the create, update and delete operations (named doCreate, doUpdate and doDelete respectively). If you examine the doUpdate and doDelete methods, you will see a cfcatch clause with type="conflict". This calls the ChangeObject's conflict() function. This function takes as its argument the server's version of the object we are trying to update or delete, so the client application will have all three versions of the data - old, new and the server version:

<!--- If there was a conflict, mark the change object.
Include the current version of the record --->
<cfcatch type="conflict">
<cfset variables.dao.read(id=new.getARTISTID()))>
</cfcatch>


The problem is that the code is using the DAO read() method to get the server's version of the object. If you take a look at the DAO code, the return type is defined to be this:
<cffunction name="read" output="false" access="public" returntype="src.com.ARTISTS[]">

This is an array, which is not what we want to put in the ChangeObject for LCDS. What we really want to put in this object is a single record and to do this, we have to unwrap the array returned by read():

<cfcatch type="conflict">
<cfset readresult = variables.dao.read(id=new.getARTISTID())>
<cfset serverversion = readResult[1]>
<cfset co.conflict(serverVersion)>
</cfcatch>

You may want to include some error checking code in here to verify that the call to read() has returned exactly one result (it should as in the case ARTISTID is the primary key in the table). You also may want to "var" the readResult and serverVersion variables at the top of the function to keep them local (yes, this is annoying, yes we need to fix that).

An alternative approach is to invoke the get() method on the Assembler itself, which will take care of unwrapping the array and throwing errors if there is a problem. In order to do this you would need to create a Struct that contained "ARTISTID" as the get() function takes a map of name/value pairs to support the possibility of multiple primary keys. So this fix would look like this:

<cfcatch type="conflict">
<cfset uid = {ARTISTID=new.getARTISTID()}>
<cfset serverVersion = read(uid)>
<cfset co.conflict(serverVersion)>
</cfcatch>


Notice the use of the new ColdFusion 8 structure initialization!

I can't believe we (specifically I) didn't notice this problem before, but as it turns out my conflict functions did not make use of the server version of the records in all of the applications I have written so I (and I guess our QA folks) didn't notice the problem until just recently. On the bright side, this should be an easy thing to fix (and hopefully we will fix it in future releases of the Eclipse extensions) and gives me an opportunity to talk about this really helpful feature of the Extensions.