Re: Mapping objects

Todd Hivnor <[email protected]> Tue, 04 Jul 2006 13:32:03 -0700
Newsgroups gmane.comp.java.openamf.user
Message-ID <[email protected]>
You are on the right path, generally.

SmackletType is defined in 
public class HelloWorld, which seems wrong. I would define SmackletType 
in SmackletType.as. SmackletType is also defined in the 
com.flashdb.services package, but this package isn't included in your 
call to Object.registerClass.

I keep the same class hierarchy in Flash and Java, so if SmackletType is 
defined in the com.flashdb.services package in Java, then I create that 
exact same package in Flash. It just keeps things clear, and makes the 
class mappings simple:

<custom-class-mapping>
  <java-class>com.flashdb.services.SmackletType</java-class>
  <custom-class>com.flashdb.services.SmackletType</custom-class>
</custom-class-mapping>

Object.registerClass("com.flashdb.services.SmackletType", 
com.flashdb.services.SmackletType);

You seem to have a couple of packages in play, com.flashdb.services and 
com.flashdb.domain. This might be part of the problem.
       
I personally don't use the underscore in front of variable names. I use 
function names like  getSmackletName() and receive values in Flash like 
smackletName. The underscore may work, but if you might experiment with 
dropping it if you can't get things to work, after getting the packages 
in line.

I recommend using local HTTP proxy server to inspect the Remoting 
traffic. I personally use ServiceCapture, but there is apparently 
something else called Charles which might work. But generally these let 
you see the data flows between Java and Flash, which is really handy.

You do need to implement Serializable in your Java class, as I 
understand it.

OpenAMF will use introspection to dig into your Java object, and call 
all of its getters. This is convenient, but can be a hassle if you have 
getters which access data you don't want serialized. I use methods like 
fetchSmackletName(), as opposed to getSmackletName(), if I don't want 
smackletName sent to Flash.

If you want to learn about the serialization process, it is helpful to 
understand that OpenAMF uses CarbonFive's ASTranslator. The ASTranslator 
documentation is pretty good.

You can do a lot of great things with Java and Flash Remoting, they work 
really well together. You can serialize very complex nested objects. I 
personally define my whole application interface in Java, and then have 
Flash just render it. So can change my user interface from Java, without 
recompiling the .swf. When I first started using Remoting, I didn't take 
the time to understand how powerful the object mapping stuff is. I later 
rewrote the app using object mapping, and the code is much simpler & 
cleaner. So I would encourage you to take the time to fully experiment 
with the object mapping stuff, before writing any real application code.

One thing to understand is that the Java-->Flash serialization is more 
powerful than the Flash->Java serialization. If you have Object A which 
references Object B, and Object B references Object A, then you have a 
circular reference loop. When serializing from Java to Flash this is 
fine. Java will send one copy of each object, and not get confused. If 
you sent this back, from Flash to Java, then Flash will lock up. It 
can't resolve the circular reference, and gets hopelessly confused. So, 
watch out for that. You can do a recursion check in Flash, to detect this.

Here is my code, which has a couple of references to things you don't 
have. It can easily be adapted. The code will recursively walk through 
an object, building up an array of the all the objects encountered thus 
far. If it finds a duplicate, then there is a recursive loop, so it 
reports the problem and bails out.

class LearnerService {

    /** Return true of the object is Ok.
     * Also return true if we are not currently checking for recursion.
     * If there is a problem trace it, and set the problematicObject 
variable
     */
     
    private static var problematicObject;
    public static function recursionCheck(currObject : Object) : Boolean {
        if (DebugConfig.checkRecursionInEvents()) {
            trace(Log.L_INFO+ "Performing Recursion Check ...");
            var result : Boolean = 
LearnerService.recursionCheckHelper(currObject, new Array());
           
            if (result) {
                trace(Log.L_INFO+ "Recursion Check Passed! ");
            }
            return result;
        } else {
            return true;
        }
    }

    private static function recursionCheckHelper(currObject : Object, 
objectsFound : Array) : Boolean {   
        var childObject : Object;
        var i : Number;
        var hasError : Boolean = false;
        for (var key : String in currObject) {
            childObject = currObject[key];
            if ( (typeof(childObject) == "object") || 
(typeof(childObject) == "movieclip"))  {
                for (i=0; i<objectsFound.length; i++) {
                    if (childObject == objectsFound[i]) {
                        trace(Log.L_ERROR+ "Found recursive child 
object=" + childObject + " with key=" + key +
                                " in object=" + currObject + " inside 
runEvent.");
                        LearnerService.problematicObject =childObject;
                        return false;
                    }
                }
                objectsFound.push(childObject);
               
                // recursively check
                if (recursionCheckHelper(childObject, objectsFound) == 
false) {
                    hasError = true;
                    break;
                }
                   
            } else if (childObject == null) {
                // no need to check null's
            } else if (
                (typeof(childObject) == "string") || 
                (typeof(childObject) == "boolean") || 
                (typeof(childObject) == "number") || 
                (typeof(childObject) == "function")
                ) {
                // no need to check these things


            } else {
                trace(Log.L_ERROR+ "Not an object: child object=" + 
childObject + " is " + typeof(childObject) + " with key=" + key +
                        " in object=" + currObject + " inside runEvent.");
            }
        }
        return ! hasError;
    }
}



Luke DeWitt wrote:
> Hey Guys,
>  
> I finally got OpenAMF up and working (had to use the last release 
> though) and I am am so sorry to ask this question again, but I have 
> read through a bunch of the mail list archives and I am getting nowhere!
>  
> I used this tutorial here: 
> http://www.flash-db.com/Tutorials/helloOpenamf/hello2.php?page=4 to 
> get started in the wonderful world that is java to flash remoting. 
> This was a simple little tutorial, but I am looking to do more than 
> just echo a String. At this moment I am trying to send a custom java 
> object (SmackletType) to Flash. I figure that if I can get this 
> working I will have no problem doing the next couple of steps which 
> are just extenstions of this.
>  
> Here is some code to help people visualize what I am doing:
>  
> Java Code for Smacklet Type:
>  
> package com.flashdb.domain;
>  
> import java.io.Serializable;
>  
> public class SmackletType implements Serializable {      // does this 
> need to be serializable?
>   
>  // class properties
>  private String _smackletName;
>  private String _smackletTeaseLink;
>  private String _smackletLink;
>  private String _smackletThumbnail;
>  
>  // constructor
>  
>  public SmackletType(String smackletName, String smackletLink, String 
> smackletTeaseLink, String smackletThumbnail) {
>    
>   _smackletThumbnail = smackletThumbnail;
>   _smackletTeaseLink = smackletTeaseLink;
>   _smackletLink = smackletLink;
>   _smackletName = smackletName;
>   
>  } // SmackletType constructor
>  
>  // getters and setters
>  
>  public String get_smackletLink() {
>   return _smackletLink;
>  }
>  
>  public void set_smackletLink(String link) {
>   _smackletLink = link;
>  }
>  
>  public String get_smackletName() {
>   return _smackletName;
>  }
>  
>  public void set_smackletName(String name) {
>   _smackletName = name;
>  }
>  
>  public String get_smackletTeaseLink() {
>   return _smackletTeaseLink;
>  }
>  
>  public void set_smackletTeaseLink(String teaseLink) {
>   _smackletTeaseLink = teaseLink;
>  }
>  
>  public String get_smackletThumbnail() {
>   return _smackletThumbnail;
>  }
>  
>  public void set_smackletThumbnail(String thumbnail) {
>   _smackletThumbnail = thumbnail;
>  }
>  
> } // SmackletType class
>  
>  
> ActionScript Code for SmackletType:
>  
> class SmackletType {
>  
>  // class properties
>  private var _smackletName:String;
>  private var _smackletTeaseLink:String;
>  private var _smackletLink:String;
>  private var _smackletThumbnail:String;
>  
>  // constructor
>  
>  public function SmackletType(smackletName:String, 
> smackletLink:String, smackletTeaseLink:String, smackletThumb:String) {
>   
>   _smackletThumbnail = smackletThumb;
>   _smackletTeaseLink = smackletTeaseLink;
>   _smackletLink = smackletLink;
>   _smackletName = smackletName;
>   
> } // SmackletType constructor
>  
>  // getters and setters
>  public function get smackletName():String {
>   return _smackletName;
>  }
>  
>  public function set smackletName(sSmackletName:String):Void {
>   _smackletName = sSmackletName;
>  }
>  
>   public function get smackletLink():String {
>   return _smackletLink;
>  }
>  
>  public function set smackletLink(sSmackletLink:String):Void {
>   _smackletLink = sSmackletLink;
>  }
>  
>  public function set smackletTeaseLink(sSmackletTeaseLink:String):Void {
>   _smackletTeaseLink = sSmackletTeaseLink;
>  }
>  
>  public function get smackletTeaseLink():String {
>   return _smackletTeaseLink;
>  } 
>  
>   public function get smackletThumbnail():String {
>   return _smackletThumbnail;
>  }
>  
>  public function set smackletThumbnail(sSmackletThumbnail:String):Void {
>   _smackletThumbnail = sSmackletThumbnail;
>  }
>  
> } // SmackletType class
>  
> I have added the following into my openamf-config.xml file:
>  
> <custom-class-mapping>
>   <java-class>com.flashdb.domain.SmackletType</java-class>
>   <custom-class>SmackletType</custom-class>
> </custom-class-mapping>
>  
> This is the simple piece of java code I have edited to send my object:
>  
> package com.flashdb.services;
>  
> import com.flashdb.domain.*;
>  
> public class HelloWorld {
>    
>  SmackletType testSmackletType = new SmackletType("Slots", 
> "http://www.google.com", "http://www.google.ca", "00.gif");
>  
>  public SmackletType makeEcho(){
>         return testSmackletType;
>     }
>  
> }
>  
> And Finally, the ActionScript code to actually call for the object:
>  
> //Import needed classes
> import mx.remoting.Service;
> import mx.services.Log;
> import mx.rpc.RelayResponder;
> import mx.rpc.FaultEvent;
> import mx.rpc.ResultEvent;
> import mx.remoting.PendingCall;
> import mx.remoting.debug.NetDebug
> NetDebug.initialize()
> // initialize the Logger
> var myLogger : Log = new Log (Log.DEBUG, "logger1" );
> // override the default log handler
> myLogger.onLog = function (message : String ) : Void {
>    trace ("myLogger-->>>" + message );
> }
>  
> //Create the service
> myService = new Service ("http://localhost/helloworld/gateway", 
> myLogger, "com.flashdb.services.HelloWorld", null, null);
> Object.registerClass("com.flashdb.domain.SmackletType", SmackletType);
> //on Data handler
> //function onEchoData (msg : ResultEvent){
> function onEchoData (msg : SmackletType){
>    mx.remoting.debug.NetDebug.trace ({level : "Debug", message : 
> "onCategoryData" });
> //   show.text = msg.result;
>    //var temp:SmackletType = msg;
>    var temp:SmackletType = msg;
>    trace(temp.smackletName() + "            HOOGEY BOOGEY!");
> }
> //on Error handler
> function onEchoFault (rs : ResultEvent){
>    mx.remoting.debug.NetDebug.trace ({level : "None", message : "There 
> was a problem: " + fault.fault.faultstring     });
> }
> //Buttons callbacks
> callButton.onPress = function () {
>    //create the remoting call
>    var pc : PendingCall = myService.makeEcho();
>    pc.responder = new RelayResponder (this._parent, "onEchoData",
>    "onEchoFault" );
> }
> //just clear the textfield
> clearButton.onPress = function (){
>    show.text = "";
> }
> stop();
>  
>  
> When I run my program and hit the "callbutton", I get this...
>  
> myLogger-->>>7/4 15:15:2 [INFO] logger1: Creating Service for 
> com.flashdb.services.HelloWorld
> myLogger-->>>7/4 15:15:2 [INFO] logger1: Creating gateway connection 
> for http://localhost/helloworld/gateway
> myLogger-->>>7/4 15:15:2 [INFO] logger1: Successfully created Service
> myLogger-->>>7/4 15:15:3 [INFO] logger1: Invoking makeEcho on 
> com.flashdb.services.HelloWorld
> undefined            HOOGEY BOOGEY!
> myLogger-->>>7/4 15:15:3 [INFO] logger1: 
> com.flashdb.services.HelloWorld.makeEcho() returned {_smackletLink: 
> "http://www.google.com",
>      _smackletName: "Slots",
>      _smackletTeaseLink: "http://www.google.ca",
>      _smackletThumbnail: "00.gif"}
>  
>  
> I just want to be able to get that info that is being passed into 
> Flash to become my SmackletType object. Can anyone give me a hand? 
> This stuff is making my life so much easier right now over web 
> services, and any help I could get would be greatly appreciated.
>  
> Thank you!
>  
> --LD
>


Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642