Posts Tagged remote-object
RemoteObject ActionScript & Java Serialization Tips
Posted by brianr in actionscript, blazeds, flex, java on December 1st, 2010
When working with RemoteObjects (ROs) in the Flash Player via a server-side library like BlazeDS, LCDS, or WebORB, there are several simple stumbling blocks that often lead to the case where you receive back untyped, generic ActionScript (AS) Object objects as opposed to the mapping you set up between your AS objects and your Java objects. I’ll note the most common issues quickly and move on to provide additional explanation where necessary.
Common Issues
- Incorrect spelling / mapping of Java class in RemoteClass metadata in AS object: [RemoteClass(alias="fullyQualifiedJavaClass")]
- Incorrect spelling / capitalization of properties.
- Not providing empty constructors in Java object.
- Not using formal getter / setters methods for properties in Java object.
- AS mapped object not compiled into the SWF. This happens when using ArrayCollections and the item isn’t referenced anywhere in the application and thus not compiled into the SWF.
Nonexistent or Incorrect ActionScript Mapping
In order to tell the Flash Player to serialize your Java Data Transfer Objects (DTOs) to your explicit AS objects you’ll need to make sure you include the all important [RemoteClass(alias="fully qualified Java class we're mapping to")] metadata.
The most common culprit here is usually a simple misspelling of the fully qualified Java object in your RemoteClass metadata, but here’s a list of other common things to note before we look at an example:
REQUIRED:
- You must include the metadata [RemoteClass(alias="fullyQualifiedJavaClass")]. Make sure the name of Java object is the fully qualified name.
- The properties you want to map must be public.
- The properties you want to map must be spelled and capitalized exactly the same as they appear in your Java class.
- If your Java object extends another object and you want the properties in the super class also mapped to your AS object, you must either a) create that class in AS and map it to the base Java object or b) add those properties directly into your AS object.
- If your AS object contains complex objects that exist in the Java side and you want that object mapped correctly you’ll need to create that class in AS and map its corresponding Java object.
- If your AS object contains an Array or ArrayCollection that will be populated with objects you expect to be mapped, you’ll need to a) ensure the corresponding AS object is mapped and b) is compiled into the application. A simple trick to ensure that your AS object is compiled into the app is to declare ArrayCollections like so: public var foos:ArrayCollection = new ArrayCollection();Foo; This also helps you and other developers see what kind of AS object is expected in the list like a Java generic. The compiler and player obviously won’t throw errors or complain if the objects aren’t of that type, but from a readability perspective it’s quite helpful.
OPTIONAL/MISNOMER
- You can specify your public properties as either regular, old public properties or using the getter and setter functions.
- The package of your AS object does not need to match the package of your Java object. The aforementioned RemoteClass metadata takes care of that for you.
- You do not need a constructor in your AS object.
- You can have extra, non-mapped properties in your AS object.
- You do not need to implement the same interface as your Java object. In fact, you can use additional, client-side only interfaces to extend your AS object and it will not effect the mapping.
- You do not need to annotate the class with the [Bindable] metadata. It’s often the case where it is because you’re using the object as a doman/model/vo on the client and want other things to bind to it, but it’s not required.
- You can have methods in your AS obj that do not map to methods in your Java object.
package com.webappsolution.myapp.model
{
[RemoteClass(alias="com.webappsolution.myapp.dto.PersonDTO")]
public class PersonModel extends FooModel implements IUniqueItem
{
public function PersonModel()
{
super();
}
// mapping using getter ans setter functions for public properties
// also implements IUniqueItem interface
private var _id:String;
public function get id():String
{
return _id;
}
public function set id(value:String):void
{
_id = value;
}
public var age:int = 0;
public var firstName:String = "";
public var lastName:String = "";
public var birthDate:Date = new Date();
public var isBeerDrinker:Boolean = true;
public var complexObject:ComplexObject = new ComplexObject();
// put in the references to the types of items we exopect in the collection
// to ensure that they are compiled into the swf. this also provides a
// nice readability clue as well to other developers.
public var foos:ArrayCollection = new ArrayCollection();Foo;
// not mapped
public var isDisabled:Boolean;
protected var uid:int;
// not mapped
public function getFullName():String
{
return this.firstName + " " + this.lastName;
}
}
}
Java Object Issues
There’s a couple quick gotchas here that should be noted when creating Java objects that you wish to map to AS objects, as many of the important issues are covered above in the AS section — I’ll list these quickly followed by a small example.
REQUIRED:
- You must provide an empty constructor.
- You must use formal getters and setters for your properties. You cannot just list out public properties in Java like we do in AS.
OPTIONAL:
- While an empty constructor is required, it’s ok to override it and provide multiple constructors as well.
- You can have extra, non-mapped properties in your Java object.
- You can implement interfaces in your Java object that do not exist in your AS object.
- Considering using the DTO pattern on the Java side to pass objects across the wire as opposed to allowing AS objects to map to domain/entity objects in your Java tier. This can be especially important when using domain/entity objects that are mapped to a database via Hibernate — there are lazy loading issues here that can cause less than fully populated object graphs to come across the wire to your Flex app. Instead, put a service layer in front of your domain object layer that returns DTOs.
package com.webappsolution.myapp.dto;
import ...
public class PersonDTO implements IStuff
{
private String id;
private int age;
private String firstName;
private String lastName;
private Date birthDate;
private boolean isBeerDrinker;
private ComplexObject complexObject;
public PersonDTO()
{
}
public PersonDTO(String fName, String lName)
{
this.firstName = fName;
this.lastName = lName;
}
public PersonDTO(String fName, String lName, int age)
{
this.firstName = fName;
this.lastName = lName;
this.age = age;
}
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
// omitting the rest of the public getters / setters for brevity, but you'd
// need one for each property you want to map
}
For more information on RemoteObjects, please visit the Adobe Flex documentation: Using RemoteObject components
actionscript, blazeds, flex, java, remote-object, remoteclass-metadata, serialization, tips
ActionScript Dependency Injection With RemoteClass Metadata Issue With Fix - UPDATED
Posted by brianr in actionscript, blazeds, cairngorm, spring on August 31st, 2009
NOTE: My fix might be a little overkill, so I offer a simpler approach at the bottom of this post…and since this post still contains some good info on dynamically mapping classes at runtime leveraging the registerClassAlias() method, I decided to leave it as is sans this simple note.
Say you’re using Spring ActionScript (SAS) or Swiz or some other ActionScript framework for Dependency Injection (DI) in conjunction with the RemoteObject…in most cases you’d like to map custom ActionScript Request and Response objects to a corresponding server-side Request and Response object and you typically do so with a small bit of metadata in your AS class — just for an example, let’s say we have a Java LoginService with the following method signature:
public LoginResponseDTO login(LoginRequestDTO request) throws LoginException
And the Java LoginRequestDTO looks like:
package com.domain.myproj.dto.request;
public class LoginRequestDTO
{
public String username;
public String password;
}
Then we’d most likely have a corresponding LoginRequestDTO in ActionScript with the following class definition:
package test
{
[RemoteClass(alias="com.domain.myproj.dto.request.LoginRequestDTO")]
public class LoginRequestDTO extends test.AbstractRequestDTO
{
public var username:String;
public var password:String;
}
}
And via the magic of the client-side Flash Player and the server-side AMF Remoting Gateway in BlazeDS / LCDS / WebORB / GraniteDS, our ActionScript Request and Response Objects are serialized and deserialzied into their Java counterparts and all is well…the thing is, this is really only done with some big help from the Flex compiler and the all important RemoteClass metadata…ok, so what?…let’s do some further questioning and hypothesizing…
Well, let’s say you’re injecting your Business Delegates or whatever objects you’re using to actually invoke the server-side LoginService, where you’re also leveraging your cool, custom AS Req/Resp objects…well if you’re injecting your BDs, then you’re kind of injecting these other objects too, which means the compiler never actually sees the RemoteClass metadata in your Req/Resp objects…now this is very important, so I’ll repeat it in a slightly different way and with more definition to it’s importantce:
If the compiler doesn’t actually know about or see the RemoteClass metadata, then not only will the mapping of your custom AS Req/Resp objects that are supposed to map to custom Java Req/Resp objects not happen, but even worse is that core AS Remoting objects that map to their corresponding core Java Remoting Objects (part of the very framework that makes the RemoteObject possible) does not take place.
Go ahead…create an example and you’ll get a runtime error probably saying something like:
TypeError: Error #1034: Type Coercion failed: cannot convert Object@13e9921 to mx.messaging.messages.ErrorMessage.
So now you have 2 issues with the latter being the first priority, b/c if you don’t fix that then you don’t have to even worry about the custom mappings…head in hands…what to do? Well, I know Flash guys or doods writing in pure AS3 without the Flex Framework also run into issues when trying to leverage the RemoteObject, so I googled around a bit and found the answer with the registerClassAlias() method.
What we need to do is map each of the core AS Objects to the core Java Objects that are required for Java-RPC — http://forums.adobe.com/thread/295996
The registerClassAlias method allows developers to map client-side AS objects to server-side objects at runtime so the Flash Player can create the correct mappings…so to fix our issues we need to register the core objects and our custom objects.
I decided to wrap this in 2 objects: 1 for the framework mappings and a 2nd for the custom mappings:
RegisterDataServicesClassAliases
/**
* Web App Solution Confidential Information
* Copyright 2009, Web App Solution
*
* @author Brian Riley
* @date July, 6, 2009
*/
package com.webappsolution.roserviceinspector.util
{
import com.webappsolution.springactionscript.util.IRegisterRemoteObjectClassAliases;
import com.webappsolution.springactionscript.util.RegisterRemoteObjectClassAliases;
public class RegisterDataServicesClassAliases extends RegisterRemoteObjectClassAliases implements IRegisterRemoteObjectClassAliases
{
/**
* Register all the custom request and response objects for the application.
*/
override public function registerCustomFlexDataServicesImpl():void
{
// REQUESTS
//
registerClassAlias("com.domain.myproj.dto.request.LoginRequestDTO", com.domain.myproj.dto.request.LoginRequestDTO);
// RESPONSES
//
}
}
}
RegisterRemoteObjectClassAliases
/**
* Web App Solution Confidential Information
* Copyright 2009, Web App Solution
*
* @author Brian Riley
* @date July, 29, 2009
*/
package com.webappsolution.springactionscript.util
{
import flash.net.registerClassAlias;
import mx.collections.ArrayCollection;
import mx.messaging.config.ConfigMap;
import mx.messaging.messages.*;
import mx.utils.ObjectProxy;
/**
* This little gem maps client-side ActionScript objects to their servber-side Java counterparts
* at runtime.
*
* <p>
* Normally this is done via the metatdata tag for RemoteObjects [RemoteClass(alias="com.domain.project.MyDTO")]
* and the compiler automatically creates the base AS to Java class mappings for the Flex Data Services in
* LCDS / BlazeDS / WebORB / GraniteDS...
* </p>
*
* <p>
* However, there are cases where developers either don't create the metadata or inject these DTOs
* at runtime and thus the compiler never actually sees the metadata, so we're left to do the class
* mappings at runtime.
* </p>
*
* <p>
* This class provides developers with a couple options: The developer can choose to map all the Flex
* Data Service components with the <code>public static registerFlexDataServices()</code> method or
* they can simply call the <code>public static registerAll()</code> method; they can also put a list
* of custom mappings for custom requerst and response objects in the
* <code>public static registerCustomFlexDataServices()</code> method.
* </p>
*/
public class RegisterRemoteObjectClassAliases
{
/**
* Constructor
*/
public function RegisterRemoteObjectClassAliases()
{
}
/**
*
*/
public function registerAll():void
{
registerFlexDataServices();
registerFlexDataServicesMisc();
registerCustomFlexDataServices();
}
/**
* Registers the base Flex Data Service Java reguest and response objects to the base
* ActionScript reguest and response objects.
*/
public function registerFlexDataServices():void
{
/*
http://forums.adobe.com/thread/295996
I think one problem is that the various mx.messaging.messages.Message
implementations have not called registerClassAlias() for each
of their types. In Flex, the compiler notices the [RemoteClass] metadata
on each ActionScript class and codegens static initializer code to call
registerClassAlias() while the application is initializing (so
that it occurs before any data is sent or received).
Note you can see this generated code if you specify
-keep-generated-actionscript=true on the command line arguments for
mxmlc. For compiling a file called test.mxml, find the
/generated/_test_FlexInit-generated.as file and notice the calls to
registerClassAlias.
*/
// See http://www.mail-archive.com/flexcoders@yahoogroups.com/msg74512.html
// For explanation as to why this is needed. Without it, messages were not getting mapped correctly
// from server.
registerClassAlias("flex.messaging.messages.RemotingMessage", RemotingMessage);
registerClassAlias("flex.messaging.messages.ErrorMessage", ErrorMessage);
registerClassAlias("flex.messaging.messages.CommandMessage",CommandMessage);
registerClassAlias("flex.messaging.messages.AcknowledgeMessage", AcknowledgeMessage);
// registerClassAlias("flex.messaging.io.ArrayList", ArrayList);
registerClassAlias("flex.messaging.config.ConfigMap", ConfigMap);
registerClassAlias("flex.messaging.io.ArrayCollection", ArrayCollection);
registerClassAlias("flex.messaging.io.ObjectProxy", ObjectProxy);
// You may want to register pub/sub and other rpc message types too...
registerClassAlias("flex.messaging.messages.HTTPMessage", HTTPRequestMessage);
registerClassAlias("flex.messaging.messages.SOAPMessage", SOAPMessage);
registerClassAlias("flex.messaging.messages.AsyncMessage", AsyncMessage);
registerClassAlias("flex.messaging.messages.MessagePerformance Info", MessagePerformanceInfo);
}
/**
* Additional message types...doesn't work in Flex 2.0.1 SDK, so comment out the
* the body of the method if that's the case.
*/
public function registerFlexDataServicesMisc():void
{
// registerClassAlias("DSA", AsyncMessageExt); // doesn't work in Flex 2.0.1 SDK
// registerClassAlias("DSC", CommandMessageExt); // doesn't work in Flex 2.0.1 SDK
// registerClassAlias("DSK", AcknowledgeMessageExt); // doesn't work in Flex 2.0.1 SDK
}
/**
* Registers the custom Java reguest and response objects to the custom ActionScript reguest and response objects.
* Developers should list their custom objects here.
*/
public function registerCustomFlexDataServices():void
{
// map the Java Data Transfer Object passed over HTTP back to Flex to the AS object
registerCustomFlexDataServicesImpl();
}
/**
* Concrete classes must override this method, as this is just a hook operation.
*/
public function registerCustomFlexDataServicesImpl():void
{
// concrete classes must override this bad boy
}
}
}
And then I simply call the following somewhere in my app’s initialization process (very early on):
private function registerDataServices():void
{
logger.debug("registerDataServices");
var registerROAliases:RegisterDataServicesClassAliases;
// since we're injecting classes at runtime we need to set up the registerClassAlias() to
// Flex Data Service classes manually
registerROAliases = new RegisterDataServicesClassAliases();
registerROAliases.registerAll();
}
And voila! We’re back in business.
I’d like to thank my local, Boston Adobe Flex guru for providing the basic solution to my issue in the following link: http://forums.adobe.com/thread/295996
UPDATED FIX:
So I’m an idiot or rather someone that tends to overarchitect/think things sometimes…everywhere else in my app I simply make sure my classes injected at runtime are compiled into the app by simply referencing them somewhere else in the app or by putting them into a properties file like so:
LoginRequestDTO = ClassReference("com.domain.myproj.dto.request.LoginRequestDTO")
This will force the compilation of the LoginRequestDTO and thus allow the compiler to see the RemoteClass metadata…but at least you know more about what the metadata does and how it works in conjunction with the Flex compiler
dependency-injection, java-rpc, register-class-alias, remote-object, remoteclass-metadata
-
-
Archives
- April 2011 (2)
- March 2011 (1)
- February 2011 (1)
- January 2011 (1)
- December 2010 (5)
- October 2010 (4)
- September 2010 (1)
- August 2010 (2)
- April 2010 (6)
- March 2010 (2)
- January 2010 (2)
- December 2009 (1)
- November 2009 (7)
- October 2009 (3)
- September 2009 (1)
- August 2009 (3)
- July 2009 (6)
- June 2009 (5)
- May 2009 (9)
- April 2009 (8)
- March 2009 (2)
- January 2007 (1)
- August 2006 (1)
-
Meta