So, I have been using FlexMonkey (an UI automation framework) , and one of the parts of it is editing a file called FlexMonkeyEnv.xml, which defines what events to listen for on each class, so you can play them back later (such as recording the “click” event on a button, or a certain key press). You are supposed to take the default FlexMonkeyEnv.xml file, edit it, and put it at the same folder location as your main mxml file, so when it compiles its at the same level as your .swf . You can see that it both embeds the file and sends a HTTPService call for the same file (why it does both, i have no idea), just FlexMonkeyEnv.xml which means the same directory the swf is in.
[Embed(source="FlexMonkeyEnv.xml", mimeType="application/octet-stream")]
protected const FlexMonkeyEnv:Class;
// misc code here....
var httpService:HTTPService = new HTTPService();
httpService.url = "FlexMonkeyEnv.xml";
httpService.resultFormat = "e4x";
httpService.addEventListener(ResultEvent.RESULT, envResultHandler, false, 0, true);
httpService.addEventListener(FaultEvent.FAULT, envFaultHandler, false, 0, true);
httpService.send();
However, the project I’m working on is doing things a bit differently. It is interacting with a Groovy on Grails server thats running on localhost. So the server is serving content out of a certain folder (web-app), so the swf needs to be in this directory rather then the bin-debug directory that it gets placed when it compiles. So we just set up a symlink in the web-app folder so that it links to the most recent compiled swf in bin-debug
dhcp-10-134-232-191:web-app markgrandi$ ls -la
<other files>
lrwxr-xr-x 1 markgrandi staff 45 Oct 6 13:30 tos.swf -> ../../workspace/to-frontend/bin-debug/tos.swf
We also have a custom url set up so when flashbuilder compiles the app, we set a URL in the debug configurations so it launches this url rather then invoking a web browser pointing to the swf in the bin-debug folder (or just launch the flash player debugger program manually), the url looks like this:
http://localhost:8080/to/tos.swf?mode=login&dbm=t&lpw=someone@example.com/showthis&rf=0
So when you run the swf, you use the FlexMonkey AIR app to ‘connect’ to your code, and from the app, you can view the Environment file, and in the window that it opens, it shows the contents of the file, and whether you were using the default file or not. No matter how many changes I made, it said I was using the default file still.
This baffled me for a wrong time. What was I doing wrong? Flexmonkey sadly doesn’t log anything so I had no idea what was going on, and the FlexMonkeyEnv.xml was in the correct place, at the same folder level as my main mxml file.
Turns out that problem 1 was, I wasn’t actually “running” the swf from the bin-debug folder (which is where the the FlexMonkeyEnv.xml file was getting copied when the application was compiled). I was running it from the web-app directory that gets called by the Groovy on Grails server when flash builder invokes the url that i set it to in the debug/launch configurations. So the solution was very simple, simply symlink the FlexMonkeyEnv.xml file in the web-app folder like I did with the swf, and everything should work, right?
dhcp-10-134-232-191:web-app markgrandi$ ls -la
lrwxr-xr-x 1 markgrandi staff 55 Jan 27 15:08 FlexMonkeyEnv.xml -> ../../workspace/to-frontend/bin-debug/FlexMonkeyEnv.xml
lrwxr-xr-x 1 markgrandi staff 45 Oct 6 13:30 tos.swf -> ../../workspace/to-frontend/bin-debug/tos.swf
Wrong. I tried running the app again, and FlexMonkey STILL was saying that it was using the default xml file. I decided at this point to actually go inside the source code (you need the source code first, not just the swc obviously). I searched for “FlexMonkeyEnv.xml” and found the file AQAdapter.as (which is where the above code snippets came from as well), which handled loading the FlexMonkeyEnv.xml file. The setTestingEnvironment method is where it makes the HTTPService call to just “FlexMonkeyEnv.xml”, and two event listeners for success and failure. I added a trace statement to both, printing out the events, and sure enough, when I ran it, it called the failure event listener and printed out this:
envfile: USING DEFAULT ENV, REASON: [FaultEvent fault=[RPC Fault faultString="Error #1090: XML parser failure: element is malformed." faultCode="Client.CouldNotDecode" faultDetail="null"] messageId="389F72DA-1D24-AE78-23C4-55105174BFC6" type="fault" bubbles=false cancelable=true eventPhase=2]
What? the xml file is malformed? That can’t be right. I ran the xml file through lxml, a python api over gnome’s libxml, and see if it complained that the xml was malformed:
dhcp-10-134-232-191:~ markgrandi$ python3
Python 3.2.2 (v3.2.2:137e45f15c0b, Sep 3 2011, 17:28:59)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import lxml
>>> import lxml.etree as etree
>>> x = etree.parse("/Users/markgrandi/Desktop/tmp.xml")
>>> x
<lxml.etree._ElementTree object at 0x101461950>
Well obviously if lxml parsed it fine, it couldn’t be malformed. I decided to set a breakpoint on the httpService.send() call, to see what Flex was doing and see what it was failing on, maybe get a line number of the xml or something.
I eventually came to the class AbstractOperation.as, and it had this code in it:
if (urlToUse && urlToUse != '') {
urlToUse = URLUtil.getFullURL(rootURL, urlToUse);
}
Pretty straight forward. Its trying to take the relative URL FlexMonkey gave it (just FlexMonkeyEnv.xml) and turn it into a full url. When this completed and i saw the value urlToUse, I immediately saw where my bug was. The url that URLUtil.getFullUrl() gave back was:
http://localhost:8080/to/tos.swf?mode=login&dbm=t&lpw=whm@cs.arizona.edu/FlexMonkeyEnv.xml
Whoa there, that doesn’t seem right! why did it stick the “/FlexMonkeyEnv.xml” AFTER the url query parameters? Its obviously getting the first part from the url of the swf, which is retrieved from LoaderInfo.url, another Adobe class. That returns the url that I’m having FlashBuilder launch when I debug the program, so that seems right, and the second part is just “FlexMonkeyEnv.xml”, also correct. But just the ‘joining’ of these two urls just seems wrong.
I wanted to be sure so i copy/pasted this into a web browser, and sure enough it just loaded the swf, and not the xml file. So thats why it was giving out the “xml is malformed’ error, because a swf is obviously not an xml document. To be sure that what FlexMonkey (and I ) was asking it to do was actually reasonable, I again opened python and used the urljoin() method of urllib.parse to see what it would give me:
>>> import urllib.parse
>>> x = urllib.parse.urljoin("http://localhost:8080/to/tos.swf?mode=login&dbm=t&lpw=something@example.com", "FlexMonkeyEnv.xml")
>>> x
http://localhost:8080/to/FlexMonkeyEnv.xml
Now THAT seems right. To verify, I paste this into a web browser and sure enough it displays the xml file as it should. It seems that URLUtil.getFullUrl() doesn’t understand the concept of query parameters and just tries to add things to the end. Looking at the documentation:
Converts a potentially relative URL to a fully-qualified URL. If the URL is not relative, it is returned as is.
If the URL starts with a slash, the host and port from the root URL are prepended.
Otherwise, the host, port, and path are prepended.
@param rootURL URL used to resolve the URL specified by the url parameter, if url is relative.
@param url URL to convert.
I tried adding a ‘/’ to the front of the url that FlexMonkey is trying to load, so it becomes httpService.url = “/FlexMonkeyEnv.xml” . I run it again, and see that getFullUrl now returns:
http://localhost:8080/FlexMonkeyEnv.xml
This is still wrong, now its completely removing the entire path, including the /to/ directory which is what the Groovy on Grails server is serving content out of. To finally fix this, I have to make FlexMonkey load “/to/FlexMonkeyEnv.xml”, so now the line is: httpService.url = “/to/FlexMonkeyEnv.xml”, with a result of:
http://localhost:8080/to/FlexMonkeyEnv.xml
Hooray. Now that it works, and to verify, I run the app, and the FlexMonkey AIR app now reports that we are NOT using the default FlexMonkeyEnv.xml file. Pretty frustrating when stupid things like this prevent you from working, especially when they are pretty silly bugs in Adobe’s framework code, but I guess its satisfying when you do figure it out and fix it. Hopefully this will save someone an embarrassingly long amount of time trying to figure out why that xml file isn’t being loaded.