|
|||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |
See:
Description
Class Summary | |
---|---|
ASTranslator | ASTranslator provides the ability to translate between ASObjects used by Macromedia Flash Remoting and Java objects in your application. |
ASTranslatorFactory | |
CachingManager | |
Context | Context passed to encoders and decoders, as well as factories, during translation to and from Action Script. |
IdentityMap | A lightweight HashMap (not all methods implemented) that wraps keys with an object that uses equivalence (==) for equals() and hashCode(). |
LoopFinder | LoopFinder detects infinite loops during translation to Action Script. |
ReferenceCache | Cache used to maintain references between ASObject graphs as well as Java object graphs. |
TranslationFilter | TranslationFilter holds classes and properties that should be ignored during a translation to Action Script. |
Exception Summary | |
---|---|
ASTranslationException | |
InfiniteLoopException | Thrown when an infinite loop is detected by LoopFinder . |
ASTranslator provides JavaBean-style mapping between Java objects in any J2EE application server and ActionScript objects in Flash MX when used with Macromedia Flash Remoting MX.
ASTranslator is supported by Carbon Five and can be deployed on application servers with Flash Remoting MX including Macromedia JRun, Jakarta Tomcat, JBoss, Caucho Resin, IBM WebSphere, BEA Weblogic and ATG Dynamo.
We created ASTranslator because of an issue we found with Flash Remoting MX for J2EE. When returning Java objects from service calls to Flash, Flash Remoting serializes Java objects according to their private member state, not using it's publicly declared property accessors as you would expect. So if you have a Java class:
package com.server; public class Player { private String _name; public getName() { return _name; } }and a service class:
package com.server; public class Service { public Player getPlayer(String name) { Player player = Roster.getPlayer(name); return player; } }calling Service.getPlayer() from Flash MX would return an ActionScript object with a property "_name" instead of "name." This removes one of the primary values of object oriented programming: encapsulation. ActionScript programmers in Flash can see the internal state of Java objects on the server that even other Java objects running on the server cannot see. This is bad. It can be really bad (both for performance and security) if the internal state includes a lot more information than the object was designed to expose through its public interface. ASTranslator adds the behavior of using JavaBean-style introspection to serialize Java objects for Remoting. To use ASTranslator in the above example, we would change the Service class as follows:
package com.server; import flashgateway.io.ASObject; import com.carbonfive.flash.ASTranslator; public class Service { public ASObject getPlayer(String name) { Player player = Roster.getPlayer(name); ASObject aso = (ASObject) new ASTranslator().toActionScript(player); return aso; } }Flash will now receive an ActionScript object with the property "name."
ASTranslator follows the conversion rules for Flash Remoting for J2EE and relies on the remoting gateway to handle conversion of class types named by the conversion rules. Additionally, if it does not know what to do with an object type, it simply passes it through. Therefore it is safe to pass all objects through ASTranslator.toActionScript() when sending them back to Flash. This is the recommended use.
The Java programmer can identify classes, interfaces or specific properties of objects that should not be translated to ActionScript in a call to ASTranslator.toActionScript(). This is done through the use of ASTranslator.ignoreClass() and ASTranslator.ignoreProperty(). This is useful when you wish to reduce network traffic by not sending irrelevant data to Flash. Specific information on this is provided in the usage documentation.
ASTranslator also identifies potential loops while translating from a JavaBean to an ActionScript object.
Additionally, if the ActionScript programmer has registered ActionScript objects using Object.registerClass() that map directly to Java objects, the objects returned as the result of a Remoting service call will be of that registered type when returned to the service XXXX_onResult() callback method. More information on this is provided in the usage documentation.
With Flash Remoting alone, calling a service method with an object argument from Flash would look like:
#include "NetServices.as" NetServices.setDefaultGatewayUrl("http://localhost/gateway"); var gatewayConnection = NetServices.createGatewayConnection(); var service = gatewayConnection.getService("Service", this); Player = function() {} var player = new Player(); player.name = "Joe"; service.savePlayer(player);and the Java service class would look like:
package com.server; import flashgateway.io.ASObject; public class Service { public void savePlayer(ASObject asPlayer) { Player player = new Player(); player.setName((String) asPlayer.get("name")); Roster.savePlayer(player); } }With ASTranslator, the ActionScript would look like:
#include "NetServices.as" NetServices.setDefaultGatewayUrl("http://localhost/gateway"); var gatewayConnection = NetServices.createGatewayConnection(); var service = gatewayConnection.getService("Service", this); Player = function() {} Object.registerClass("com.server.Player", Player); var player = new Player(); player.name = "Joe"; service.savePlayer(player);and the service class would look like:
import flashgateway.io.ASObject; import com.carbonfive.flash.ASTranslator; public class Service { public void savePlayer(ASObject asPlayer) { Player player = (Player) new ASTranslator().fromActionScript(asPlayer); Roster.savePlayer(player); } }ASTranslator will create and set as many properties in the Java Player object as match in the ASObject received from Flash.
In order for ASTranslator to know what Java object type to convert the ASObject to, the type field of the ASObject must be set to the class name of the desired Java object type. This is achieved by using Object.registerClass() in ActionScript to associate the ActionScript object type with the Java class. More information on this is provided in the usage documentation.
While Macromedia is somewhat vague on the issue we strongly recommend installing Flash Remoting for your application by:
<servlet> <servlet-name>FlashGatewayServlet</servlet-name> <display-name>Flash Remoting Servlet</display-name> <description>Servlet-based plugin to Flash Remoting</description> <servlet-class>flashgateway.controller.GatewayServlet</servlet-class> <load-on-startup>10</load-on-startup> </servlet> <servlet-mapping> <servlet-name>FlashGatewayServlet</servlet-name> <url-pattern>/gateway</url-pattern> </servlet-mapping>
ASTranslator also utilizes specific components from the Jakarta Commons project. These libraries are provided in the distribution, and can be freely used by commercial and non-commercial applications. More information can be found here:
If you have installed Flash Remoting MX for J2EE in another manner, the best location for astranslator-x.x.jar will depend on the application server you are using. Here you get in to the sticky world of class loader precedence. Better to keep it all in your webapp.
import flashgateway.io.ASObject; import com.carbonfive.flash.ASTranslator; public class Service { public void savePlayer(ASObject asPlayer) { Player player = (Player) new ASTranslator().fromActionScript(asPlayer); Roster.savePlayer(player); } }Note that you must cast the result of this method call to the expected return type. fromActionScript() can take any object as an argument. It will translate any object and it's composite objects as deep as possible.
In order for ASTranslator to know what Java class an ASObject should become, you must register your ActionScript objects in Flash MX with the Java class name that the ActionScript object maps to.
Player = function() {} // Object definition Object.registerClass("com.server.Player", Player); // Player maps to com.server.Player in Java
package com.server; import flashgateway.io.ASObject; import com.carbonfive.flash.ASTranslator; public class Service { public ASObject getPlayer(String name) { Player player = Roster.getPlayer(name); ASObject aso = (ASObject) new ASTranslator().toActionScript(player); return aso; } }Be aware that your custom Java object, eg: Player, must implement Serializable in order for ASTranslator to property convert them to Action Script objects.
Note that you must cast the result of this method call to the expected return type. It will either be an ASObject, a Java Collection or one of the Java Class types known to Flash Remoting (XML Document, ResultSet,...).
If you wish to specify classes or interfaces to be left out of the translation, the code can look like this:
package com.server; import flashgateway.io.ASObject; import com.carbonfive.flash.ASTranslator; public class Service { public ASObject getPlayer(String name) { Player player = Roster.getPlayer(name); ASTranslator translator = new ASTranslator(); translator.ignoreClass("com.yourcompany.Address"); translator.ignoreProperty("com.yourcompany.Player", "email"); ASObject aso = (ASObject) translator.toActionScript(player); return aso; } }This would cause ASTranslator to ignore any object of type com.yourcompany.Address while translating, as well as the getEmail() property of the com.yourcompany.Player class itself.
For Java object that are not known to Flash Remoting, ASTranslator will set the "type" field of the resulting ASObject to be the class name of the translated Java object. If you register your ActionScript objects to map to Java objects as detailed in the section above, the ActionScript objects that are returned to Flash will be of the correct ActionScript object type.
#include "NetServices.as" NetServices.setDefaultGatewayUrl("http://localhost/gateway"); var gatewayConnection = NetServices.createGatewayConnection(); var service = gatewayConnection.getService("Service", this); // Define Player Player = function() {} // Define Player methods Player.prototype.getName = function() { return name; } // Register Player object to map to com.server.Player Java object Object.registerClass("com.server.Player", Player); // Get Player from Flash Remoting service.getPlayer("Joe"); // Handle Flash Remoting result getPlayer_Result(player) { // player is an ActionScript object of type Player var name = player.getName(); }
package com.server; import flashgateway.io.ASObject; import com.carbonfive.flash.ASTranslator; public class Service { public ASObject getPlayer(String name) throws Exception { Player player = Roster.getPlayer(name); ASObject aso = (ASObject) new ASTranslator().toActionScript(player); return aso; } }This would allow the Flash MX client to inspect all exceptions thrown during the service invocation and determine what to do about it at runtime. This strategy is discussed in the Flash Remoting documentation at http://livedocs.macromedia.com/frdocs/Using_Flash_Remoting_MX/UseActionScript8.jsp.
The "type" field of the error object returned to getPlayer_onStatus should provide the class name of the thrown exception allowing us to implement the callback in Flash as:
getPlayer_onStatus = function (error) { if (error.type == "com.server.PlayerNotFoundException") { trace("unable to find the player"); } else { trace("an unknown error occurred"); } }One limitation to this approach is that inheritance is not taken in to account. A thrown exception that extends com.server.PlayerNotFoundException is a com.server.PlayerNotFoundException but would not be handled as one in the above ActionScript code.
A more significant limitation to this approach is that Flash Remoting MX for J2EE does not actually send exceptions thrown by a service class to the XXX_onStatus callback method. Because the Flash Remoting gateway uses introspection to invoke the service methods, all exceptions thrown by service calls are nested within a java.lang.reflect.InvocationTargetException. Tracing the properties of an exception thrown by a service call results in output that looks something like:
code: SERVER.PROCESSING level: error description: Service threw an exception during method invocation: null type: java.lang.reflect.InvocationTargetException rootcause: details: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ... at flashgateway.Gateway.invoke(Gateway.java:184) ... at java.lang.Thread.run(Thread.java:536) Caused by: com.server.PlayerNotFoundException: There is no player with name 'bob' at com.server.Roster.getPlayer(Roster.java:45) ...We would like the nested com.server.PlayerNotFoundException to be used as exception upon which the error object is based but it is not. We have not come up with a great solution to this problem.
The best solution we have come up with is to build a new error object that inspects the "detail" field of the error returned by Remoting to determine the correct values for "description," "type" and "details." Unfortunately the formatting of the stack trace as provided by the "details" field differs depending on the JVM version being used on the server. Specifically, between Java 1.3 and Java 1.4 the way in which nested exceptions are printed in a stack trace is different. This makes inspecting the "detail" field brittle and a source of hard-to-uncover bugs.
Please let us know if you have recommendations for dealing with this issue.
Here's an implementation of an ArrayDataProvider that provides a DataProvider view of an array. You can use objects of this class to bind to UI components with DataGlue.
/** * ArrayDataProvider.as * Alon Salant, Carbon Five * * ArrayDataProvider implements a DataProvider view of an array * so that it can be bound to UI components using DataGlue. */ #include "RsDataProviderClass.as" _global.ArrayDataProvider = function(list) { this.init(); this.addAll(list); } ArrayDataProvider.prototype = new RsDataProviderClass(); ArrayDataProvider.prototype.addAll = function(list) { if (list != null && list.length > 0) { for (var i = 0; i < list.length; i++) { this.addItem(list[i]); } } } ArrayDataProvider.prototype.checkLocal = function() { return true; }Here's the code from a sample fla that has a list box UI component with instance name lb_people.
#include "DataGlue.as" #include "ArrayDataProvider.as" Person = function(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } var people = new Array(); people.push(new Person("Mike", "Wynholds")); people.push(new Person("Sam", "Borgenson")); people.push(new Person("Don", "Thompson")); people.push(new Person("Alon", "Salant")); var dataprovider = new ArrayDataProvider(people); DataGlue.BindFormatStrings(lb_people, dataprovider, "#firstName# #lastName#", "#firstName#-#lastName#");The last two lines you would put in a remoting XXX_Result method to bind remoting results to a UI component.
|
|||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |