How to Monkey Patch when using Flex RSLs

Monkey patchers run into problems when using Flex RSLs. In this post I’ll explain why monkey patching does not work when using RSLs and provide details of how to make it work. I’ll monkey patch the mx.controls.Button class in this post and ListCollectionView and Spark Button in later posts.

Why doesn’t monkey patching work when using RSLs? To answer this question it is important to understand two things:

  1. The Flash Player has a first-in-wins rule when loading classes.
  2. Flex applications contain two frames. The first frame contains few classes and loads the RSLs. The second frame contains the rest of the application’s classes.

Say you’ve monkey patched mx.controls.Button, which will end up in frame 2 of your application. In frame 1 of the application, the framework RSL will be loaded which contains the mx.controls.Button class. When frame 2 of the application loads the monkey patched version of mx.controls.Button is ignored because of the Flash Player first-in-wins rule.

When you monkey patch a class that is in frame 1, then you are done, you don’t need an RSL.

The basic steps I’ll use to monkey patch are:

  1. Create an RSL with just the monkey-patched file and its inheritance dependencies.
  2. Add your RSL to the configuration to get it loaded.

To demonstrate the process I created a project with a simple application that contains one button.

<mx:Button label=”My Button”/>

The project was developed using a pre-release version of Flex4, build 4.0.0.13672.

 

Create an RSL

My modification of Button is just a change to set label() so I can see if a Button in my application is a real or patched Button. When the Button’s label setter is called I just prepend “Monkey patched ” to the original value.

 

_label = “Monkey patched ” + value;

I started by adding Button.as to my source tree using the same package as Flex, “mx.controls”. Next compile your source into an RSL. From the command line it will look something like this:

>compc -directory -output=button -link-report=link.rpt -include-classes=mx.controls.Button -source-path=.

>optimizer button/library.swf –output -button.swf

–keep-as3-metadata=Bindable,Managed,ChangeEvent,NonCommittingChangeEvent,Transient,SkinPart

>digest button.swf -swc-path=button

After running these commands the size of button.swf is 143,596 bytes. Looking at the link report, there are a lot of classes besides mx.controls.Button that we don’t need. Ideally, all we want in the RSL is mx.controls.Button. But the Flash Player requires classes that Button extends or implements to be defined when the Button class is loaded. Since we are loading this RSL before the framework RSL, we need to also include the classes Button extends and implements in framework.swc. For each additional class we include, we also need to include the classes it extends and implements.

Take the link report from the first compile of Button and copy it to a file we will use as our externs file. The externs file is what we will use to remove classes we don’t want in the RSL. I actually seeded my externs.xml file by copying and pasting the script children from framework.swc’s catalog. My thinking was that I wanted to exclude everything in framework.swc except Button and the classes it extends or implements. But now I can see that the externs file is much larger than it would have been if I had just started with the initial link report.

Next edit the externs file by removing the script elements for mx.controls.Button and all of its inheritance dependencies. After recompiling I was able to get the RSL size down to 35,152 bytes. That is still pretty big but it’s the best we can do because we need UIComponent and UICompoment is big. When monkey patching spark components we will be able to remove UIComponent and get a much smaller RSL.

Add your RSL to the configuration

Now that you have your RSL, you just need to add it to the RSL configuration. I created a local configuration file, main-config.xml, with the RSLs I wanted. You can start by copying the <runtime-shared-library-path> elements from flex-config.xml to your local configuration file. Then add the entry for your RSL before the framework RSL.

<!– monkey patch mx.controls.Button from Framework SWC –>

<runtime-shared-library-path>

<path-element>button</path-element>

<rsl-url>button.swf</rsl-url>

<policy-file-url></policy-file-url>

</runtime-shared-library-path>

The order of the runtime-shared-library-path options is important because it determines the load order of the RSLs. The RSL URLs in my configuration file are all local SWF files so before running the application I will copy the RSLs from my SDK frameworks/RSLs directory to where my application lives.

When the new RSL is loaded for the first time you will mostly encounter verify errors because you missed removing an interface from the externs file. As you encounter a verify error, remove that class from the externs file and try again. Eventually the RSL will contain all the classes it needs and load without errors.

Finally, run the application to verify the monkey patched Button is being used.

The postings on this site are my own and don’t necessarily represent Adobe’s positions, views, strategies or opinions.

 

 

24 Responses to How to Monkey Patch when using Flex RSLs

  1. Hi Darrell,
    Thanks for a simaple and straight post for this issue. Trust me this RSL is a topic which is rare across the net. Kudos to you.

    Ok, now I am using the source code you provided and using them with my Flex SDK 3.2. I am able to compile using my ANT build, however not able to run it.
    The screen does not moves after,
    Flex Error #1001: Digest mismatch with RSL http://localhost/testRsl/button.swf. Redeploy the matching RSL or relink your application with the matching library.

    Any help will be a life saver.
    Cheers,
    Anand

  2. dloverin says:

    Whenever you recompile the button library and relink your application with it you need to redeploy button.swf to your server.

  3. Hi Darell,
    Thanks for the quick response. I however doing the same , actually I am using tomcat for my local environment, so I copy all the files(including the button.swf) to the server each time, but it shows the same error everytime.

    Do I not compile the Button SWF everytime ?
    I am using the default Flex-config.xml for Button.SWF compilation and a custom config.xml for

    This POC is very critical for me because I am working on one of an enterprise application where some of the guys have badly messed with the Flex source code. I want it to be fixed and so far only this option looks viable than any other suggested across.

    Please help.
    Anand

  4. Hi Darell,

    Few more pointers I found, I guess I am stuck in FLEXDOCS-432 issue.
    http://bugs.adobe.com/jira/browse/FLEXDOCS-432

    This seems to be fixed in Flex 4, however is there any fix in Flex 3.2.
    Anand

  5. Hi,
    I fixed that issue. It was my mistake, I was referring to two different RSL sources. I was creating Digest from Library SWF and then instead of optimizing button.swf I was re-optimizing library.swf only. 🙁 Silly mistake over there.

    Now, The code got compiled but did not run as expected.
    the button does not shows “Monket Patched” label and also the SWZ files are getting copied in “Temporary Internet Files” instead of AssetCache. Any pointers ?

    Anand

    • dloverin says:

      You need to make sure button.swf rsl is specified before framework rsl in the “runtime-shared-library-path” option. The first definition of the button class will win.

  6. dloverin says:

    You get a digest error when the digest in a library does not match the rsl. In the button library open the catalog.xml. Find the digest element, this is the digest of your rsl. If you change the rsl by running optimizer on it, then you need to update the library by running “digest” as I did in my example.

    After you have the rsl and button library in sync (according to digests) then you can link your application against the library. Link your application the “-keep” option. Search for “cdrsls” in the generated code. You will see a digest value for the button.swf rsl. This is the expected digest of button.swf when the library is loaded from the server. It should match the digest value in button’s catalog.xml.

    I hope this helps you find the problem.

    -Darrell

  7. Hi Darell,
    Thanks yu so much. With your help I am reaching bit by bit to the exact issue. Ok I guess the build file you shared has the keep=”true” already ,

    <mxmlc fork=”true”
    file=”MonkeyPatchRSLProject.mxml”
    static-rsls=”false”
    output=”${BUILD_DIR}/MonkeyPatchRSLProject.swf”
    keep=”true”
    link-report=”main.rpt”>
    <load-config filename=”${FLEX_HOME}\frameworks\rsl_main_flex-config.xml” />
    </mxmlc>

    And I in rsl_main_flex_config, I am declaring,

    <runtime-shared-library-path>
    <path-element>C:/MonkeyPatchedRsl/button</path-element>
    <rsl-url>button.swf</rsl-url>
    <policy-file-url></policy-file-url>
    </runtime-shared-library-path>
    <runtime-shared-library-path>
    <path-element>libs/framework.swc</path-element>
    <rsl-url>framework_3.2.0.3958.swz</rsl-url>
    <policy-file-url></policy-file-url>
    <rsl-url>framework_3.2.0.3958.swf</rsl-url>
    <policy-file-url></policy-file-url>
    </runtime-shared-library-path>
    …..
    ..
    <static-link-runtime-shared-libraries>false</static-link-runtime-shared-libraries>

    Is there something I am missing ?

    Anand

  8. dloverin says:

    The config looks correct.

    I would look at a couple of things.
    1) Search for “cdrsls” in the generated code. Verify the order of the rsls in this object is the same as in rsl_main_flex_config.
    2) Statically link the application with the button library to verify the patch is doing what you expect (should do this step before creating the rsl).

  9. Anand says:

    Is there any specific class in which I need to look for “cdrsls”. I opened the _Button.as class and did not find any such entry.

    Anand

  10. Got it … I guess this is in place .. right ?
    Still not working, not even the RSL’s getting copied in the AssetCache.

    override public function info():Object
    {
    return {
    cdRsls: [{“rsls”:[“button.swf”],
    “policyFiles”:[“”]
    ,”digests”:[“46acf3dca0089710b8480debaabbf343ae02bc0c6916fa6f5bc2f84dacfd588b”],
    “types”:[“SHA-256”],
    “isSigned”:[false]
    },
    {“rsls”:[“framework_3.2.0.3958.swz”,”framework_3.2.0.3958.swf”],
    “policyFiles”:[“”,””]
    ,”digests”:[“1c04c61346a1fa3139a37d860ed92632aa13decf4c17903367141677aac966f4″,”1c04c61346a1fa3139a37d860ed92632aa13decf4c17903367141677aac966f4”],
    “types”:[“SHA-256″,”SHA-256”],
    “isSigned”:[true,false]
    },
    {“rsls”:[“datavisualization_3.2.0.3958.swz”,”datavisualization_3.2.0.3958.swz”],
    “policyFiles”:[“”,””]
    ,”digests”:[“6557145de8b1b668bc50fd0350f191ac33e0c33d9402db900159c51a02c62ed6″,”6557145de8b1b668bc50fd0350f191ac33e0c33d9402db900159c51a02c62ed6”],
    “types”:[“SHA-256″,”SHA-256”],
    “isSigned”:[true,true]
    },
    {“rsls”:[“rpc_3.2.0.3958.swz”,”rpc_3.2.0.3958.swf”],
    “policyFiles”:[“”,””]
    ,”digests”:[“f7536ef0d78a77b889eebe98bf96ba5321a1fde00fa0fd8cd6ee099befb1b159″,”f7536ef0d78a77b889eebe98bf96ba5321a1fde00fa0fd8cd6ee099befb1b159”],
    “types”:[“SHA-256″,”SHA-256”],
    “isSigned”:[true,false]
    }]
    ,
    compiledLocales: [ “en_US” ],
    compiledResourceBundleNames: [ “containers”, “core”, “effects”, “skins”, “styles” ],
    currentDomain: ApplicationDomain.currentDomain,
    layout: “absolute”,
    mainClassName: “MonkeyPatchRSLProject”,
    mixins: [ “_MonkeyPatchRSLProject_FlexInit”, “_alertButtonStyleStyle”, “_ScrollBarStyle”, “_activeTabStyleStyle”, “_textAreaHScrollBarStyleStyle”, “_ToolTipStyle”, “_advancedDataGridStylesStyle”, “_comboDropdownStyle”, “_ContainerStyle”, “_textAreaVScrollBarStyleStyle”, “_linkButtonStyleStyle”, “_globalStyle”, “_windowStatusStyle”, “_windowStylesStyle”, “_activeButtonStyleStyle”, “_errorTipStyle”, “_richTextEditorTextAreaStyleStyle”, “_CursorManagerStyle”, “_todayStyleStyle”, “_dateFieldPopupStyle”, “_plainStyle”, “_dataGridStylesStyle”, “_ApplicationStyle”, “_headerDateTextStyle”, “_ButtonStyle”, “_popUpMenuStyle”, “_swatchPanelTextFieldStyle”, “_opaquePanelStyle”, “_weekDayStyleStyle”, “_headerDragProxyStyleStyle” ]
    }
    }

  11. Its weired. Tried on other machine, RSL were copied in the AssetCache, but it is not loading the MonkeyPatched button.swf before the framework.swc.

    It runs perfectly but never shows the “Monkey Patched” attached with the label. However If I build with FLex builder keeping static-rsls=true, I can see the output as expected.

  12. dloverin says:

    Flash Player 9,0,115 is required to load signed RSLs into the Player Cache. That could explain the issue your are seeing on different machines. What operating systems are you running on the different machines? What version of Flash Player?

    The fact the you can load a signed RSL into the player cache should not change how the program behavior because I can see you have the unsigned rsl (framework_3.2.0.3958.swf) as a failover for the signed RSL.

    When you tried on the other machine did you recompile everything or just load the application off your server? How do you know button.swf is not being loaded before the framework RSL. Run your application in the debugger and show me your debug output. We will be able to tell if button is being loaded before or after the framework rsl.

  13. I have FP 10 with debugger in all machines.

    When I tried it on other machine. I just copied all the files (including Framework RSLs) to the other machines webroot. Then run through other machines browser using http://localhost reference.

    On the other machine I saw AssetCache had the SWZ copied, but still it didn’t worked.

    Can you confrim if Flex SDK 3.2.0.3958 had this fix/feature avaialble. I think it is not re-ordering the framework RSL and always loading Framework first and then the Button.

    Also I was wondering while compiling the Button.swf, which will be the static-rsls value, for now I am using the default Flex-Config.xml which keepts static-rsls=true.

    Once again thanks a ton Darell, for your time. So far its a great learning experience with you. I guess I am this close to the issue and if it got fixed, i will be able to fix my code base too.
    Anand

  14. dloverin says:

    Loading button.swf in Flex 3.2.0.3958 will work. Flex will load the RSLs in the order listed in the generated code you pasted in. When you are compiling the button library you want static-rsls to be true. When you are compiling the application you want static-rsls=false.

    The order of the RSLs in your generated code looks correct. I’d be interested in seeing the console output when you run the application in the debugger.

  15. Itts a funny debug, I am not able to figure out anything from it,
    [SWF] C:\RSL_workspace\MonkeyPatchRSLProject\bin-debug\MonkeyPatchRSLProject.swf – 231,549 bytes after decompression
    [SWF] C:\RSL_workspace\MonkeyPatchRSLProject\bin-debug\MonkeyPatchRSLProject.swf – 46,740 bytes after decompression
    [SWF] C:\RSL_workspace\MonkeyPatchRSLProject\bin-debug\MonkeyPatchRSLProject.swf – 1,152,031 bytes after decompression
    [SWF] C:\RSL_workspace\MonkeyPatchRSLProject\bin-debug\MonkeyPatchRSLProject.swf – 600,128 bytes after decompression
    [SWF] C:\RSL_workspace\MonkeyPatchRSLProject\bin-debug\MonkeyPatchRSLProject.swf – 247,813 bytes after decompression

  16. dloverin says:

    When looking at the debug output, there is a know issue that each RSL loaded is given the same name as the application. You can tell which RSL is being loaded by looking at the size of the swf loaded. The Framework RSL is larger than all the rest so it is a good marker in the output. The framework RSL is the 1,152,031 bytes swf. The swf before that is 46,740 bytes. That’s about how big I’d expect the button.swf to be so I’d say that button.swf is being correctly loaded before the framwork RSL.

    It looks you are loading the RSL correctly. If the button library works correctly when static linking then the RSL should work correctly in dynamic linking.

    Are you using the simple application that came with the project?

    • Yes I am using the same project which you have shared with this article. My idea was to run the project with 3.2, if I could get the patched Button class, I could get my project working in the same fashion.
      If I keep ststic-rsls=true. Everything workes file but static-rsls=false ruins everything.

      I tried it running through Flex builder, using my ANT. Nothing seems working.

      Anand

  17. 🙂 This i getting crazy, I kept AS£Verbose=1 in mm.cfg and saw the out,
    The out too suggest button.swf loaded before the framework. Still the weired output.

    80685:cpool_string[2511]=utf8 “apply”
    80691:cpool_string[2512]=utf8 “MonkeyPatchRSLProject”
    80713:cpool_string[2513]=utf8 “http://localhost/rsl/button.swf”
    80745:cpool_string[2514]=utf8 “1af8b665c1c9ad48be37ccf7acd68d450efc3e6d002b459fcea028b71f186cd0”
    80810:cpool_string[2515]=utf8 “http://localhost/rsl/framework_3.2.0.3958.swz”
    80856:cpool_string[2516]=utf8 “framework_3.2.0.3958.swf”
    80881:cpool_string[2517]=utf8 “1c04c61346a1fa3139a37d860ed92632aa13decf4c17903367141677aac966f4”
    80946:cpool_string[2518]=utf8 “http://localhost/rsl/datavisualization_3.2.0.3958.swz”
    81000:cpool_string[2519]=utf8 “datavisualization_3.2.0.3958.swz”
    81033:cpool_string[2520]=utf8 “6557145de8b1b668bc50fd0350f191ac33e0c33d9402db900159c51a02c62ed6”
    81098:cpool_string[2521]=utf8 “http://localhost/rsl/rpc_3.2.0.3958.swz”
    81138:cpool_string[2522]=utf8 “rpc_3.2.0.3958.swf”
    81157:cpool_string[2523]=utf8 “f7536ef0d78a77b889eebe98bf96ba5321a1fde00fa0fd8cd6ee099befb1b159”
    81222:cpool_string[2524]=utf8 “containers”
    81233:cpool_string[2525]=utf8 “core”

  18. dloverin says:

    I tried the project in 3.5.0 and I think I found the problem you are running into. The first set of problems I assume you always resolved. The main one being Button.as from Flex4 does not compile in Flex3. So I replaced the Button.as source with the source of from 3.5.0 and re-applied my monkey-patch. I also replace the other source files for the monkey-patch as well.

    If you look in the catalog.xml of the button library you will notice that mx/controls/Button is NOT listed! One difference between Flex3 and Flex4 in that in Flex4 the -include-classes option will override what symbols are specified as externals. To solve the problem in Flex3, edit externs.xml and remove the entry for “mx/controls/Button”. That will keep Button from being externed from the Button library. Now Button.swf will actually contain the monkey-patched Button class!

    -Darrell

  19. Darrell

    Yo da man.

    It worked like a charm.

    thanks you so much. I removed the entry from extren.xml and it did the magic.
    Thank you so much for your precious time.

    Have a super time ahead.

    Cheers
    Anand

  20. Frank says:

    Hey Darrel. I’ve been trying to figure out…what to do after making the swf file and adding the entry in the flex-config.xml. (I got my mx DataGridHeader down to 7kb)
    How do i actually link this swf with my flex project? (say from the builder). I am using flash builder 4.5. Because in the build path of the properties the builder only lets me add swc files.
    So what do i do now???

  21. dloverin says:

    There is a nice blog entry on how to do this. I haven’t been able to locate the link yet.