Reading server URL from ActionScript 3 for Cairngorm configuration - apache-flex

I need to read the URL that the browser shows when a Flex application is called because I would to reference it in a mxml configuring Cairngorm remote objects.
The goal I would reach is to automatically configure Cairngorm services from environment to environment (dev,test,qa,prod) without statically set the value in the mxml or other ActionScript. Since the Flex client is deployed in the root of the war of the webapp, it's enough to read where the browser is pointing.
I have written a class that is doing so:
public class ConfigServer {
public function ConfigServer() {
var loaderUrl:String = FlexGlobals.topLevelApplication.loaderInfo.loaderURL;
var urlToSet:String = <loaderURL-string-manipulation>;
_serverUrl = urlToSet;
}
private var _serverUrl:String = '';
public function get serverUrl():String
{
return _serverUrl;
}
}
In my mxml I would do so:
<mx:Script>
<![CDATA[
import org.fao.fapda.util.ConfigServer;
private var configuration:ConfigServer = new ConfigServer();
]]>
</mx:Script>
<mx:RemoteObject
id="userService"
destination="userService"
endpoint= "{configuration.serverUrl}/messagebroker/amf"
showBusyCursor="true"
requestTimeout="100"
/>
But whenever I call the ConfigServer constructor and for every (known to me) technique I applied (statics or singletons or public ro so on), I have always had the same error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at org.fao.fapda.util::ConfigServer()[C:\dev\workspaces\FAPDA\trunk\FAPDA-client\src\org\fao\fapda\util\ConfigServer.as:8]
Cairngorm services initialization is done as follow:
<fx:Declarations>
<cut/>
<services:FAPDAServices id="services"/>
<cut/>
</fx:Declarations>
and the problem is that FAPDAServices.mxml is read runs before FlexGlobals is valid...
Is there a point in the Flex Application lifecycle where such loaderURL is defined so that I can construct ConfigServer? When in startup events that initialization in done?
I confess I'm a Flex epic rookie, so it's possible I'm completely wrong on this.
Best regards

I can't directly answer your question, but hopefully I can point you in the right direction until someone more experienced sees your question.
Create an event handler for the application tag's creationComplete event (or the highest mxml component tag if this is your own custom component) and instantiate ConfigServer there. It is generally neater to do initialization there since it is the last stop before anything is displayed on screen. You can read up more about the event in the adobe live docs. My paraphrasing should never be considered a replacement for official documentation.
You can also use the trace() statement to output text to the console to help you debug the order of execution and whether or not an object has been instantiated. Once again you can check the adobe live docs for more info.
Good luck.

Related

Unable to dispatch custom event in flex mobile application

I am trying to get some code working from an example I came across. most of the functionality works but it is failing when it tries to dispatch a custom event. At the moment the code that is trying to dispatch the event is inside a class that handles amf remoting.
the example has this line in it for the dispatch:
Application.application.dispatchEvent(new
RemoteResultEvent(RemoteResultEvent.USER_UPDATE_COMPLETE,"test"));
but that fails as it does not know what application.application is "Multiple markers at this line:
-Access of undefined property application"
I assume that this is because this was not written for a mobile app. I tried changing the dispatcher to EventDispatcher
EventDispatcher(
new RemoteResultEvent(RemoteResultEvent.USER_UPDATE_COMPLETE, "worked"));
but I then get this error:
TypeError: Error #1034: Type Coercion failed: cannot convert events::RemoteResultEvent#18337731 to flash.events.EventDispatcher.
This is the code in the custom event RemoteResultEvent.as :
package events
{
import flash.events.Event;
public class RemoteResultEvent extends Event {
public static var USER_UPDATE_COMPLETE:String = "UserUpdateComplete";
public var message:String;
public function RemoteResultEvent(eventType:String, message:String) {
super(eventType, false, false);
this.message = message;
}
}
}
I am bumbling around in the dark as I am new to flex and this type of development so I could well be doing something really dumb. Any help would be gratefully received.
Thanks
JaChNo
You seem confused about event dispatching in general.
Events can be dispatched in any Flex class that extends, or has a, EventDispatcher. Most Flex Components, including Application extend EventDispatcher. To dispatch the event, you are on the right track just do:
dispatchEvent(new RemoteResultEvent(RemoteResultEvent.USER_UPDATE_COMPLETE,"test"));
That will dispatch the event from your current class. Not that all Flex UI Components, including those made in MXML can be considered a class.
What you are trying to do is dispatch the event on the main level application; which is a horrible encapsulation breach, but doable. You have to cast it as an Application so you do not get a generic object. Like this:
(Application.application as Application).dispatchEvent(new RemoteResultEvent(RemoteResultEvent.USER_UPDATE_COMPLETE,"test"));
This approach is deprecated since Flex 4; and you use the FlexGlobals.topLevelApplication instead:
(FlexGlobals.topLevelapplication as Application).dispatchEvent(new RemoteResultEvent(RemoteResultEvent.USER_UPDATE_COMPLETE,"test"))
You don't say, but you allude to the fact that you are in a Mobile Project. If so, I would not expect the mx Application class to be available unless you explicitly added the SWC w/ MX Components to your class. You'll have to access the Spark Application, which does not have an Application property. That could be why you are getting the error.
Be sure to import the proper application you want to use:
import spark.components.Application
More info on Spark Application class.

Getting ahold of the Application to call its method

In my Flex 4.5 application I have a TitleWindow Settings.mxml, which is popped up by the PopUpManager.
Once the user has changed some settings, I not only need to save them to a SharedObject, but also to apply them to the main Application itself - so that the changes are visible to the user immediately.
For example I need to call its method hideApp(somevalue);
The spark.components.Application does not seem to have any static/singleton methods to get ahold of it.
So how do you do it?
And I also wonder how to declare, that an MXML file implements one or several interfaces?
package {
public interface Hiddable {
function hideApp(value:Number):void;
}
}
I'm asking this, because besides the main Application I have a SettingsTest.mxml Application in my project for "unit testing" that particular functionality.
Thank you! Alex
Yes it does:
FlexGlobals.topLevelApplication
though I would recommend you use events to avoid tight coupling.
As for the question about interfaces: use the attribute implements
<s:Component ... implements="IClassA,IClassB" ... />
About implementing of interfaces in MXML components see the following documentation.
What about passing changed data back from your pop up window to the application I recommend you to use Observer pattern with Flash event model something like the following:
var myWindow:MyWindow = MyWindow(PopUpManager.createPopUp(this, MyWindow));
myWindow.addEventListener(MyWindowEvent.SUBMIT, myWindowSubmit);
private function myWindow(event:MyWindowEvent):void
{
// Unsubscribing from events
var myWindow:MyWindow = MyWindow(event.currentTarget);
myWindow.removeEventListener(MyWindowEvent.SUBMIT, myWindowSubmit);
// Changed data is passing with custom event object
someData = event.someData;
}
And you should implement your custom event for that (MyWindowEvent in my pseudo code) and fire it from your TitleWindow component. You can read more about implementing custom event in documentation.

Accessing <mx:Image> from AS class

I want to draw something on an <mx:Image> I have in my .mxml file, but I want to keep my drawing implementation in a separate AS class.
How can I access my drawing AS class in my .mxml?
I think what your are asking is how to keep your actionscript files separate from the MXML files that contain your designs. The answer is simple:
Create your actionscript file. Include only methods in this file, and do not wrap the code in a package or class definition. The file should look like the following:
import mx.controls.Alert;
// ActionScript file
/**
*
* Created By jviers
* Created on Apr 14, 2011
*
*
*/
public function hello():Alert{
Alert.show("Hello World!");
}
Create your MXML file that contains your "Design" components. Create a Script element on this mxml file and set the source to the relative path of your ActionScript file. The MXML should look like this:
<?xml version = "1.0" encoding = "utf-8"?>
<s:Application xmlns:fx = "http://ns.adobe.com/mxml/2009"
xmlns:s = "library://ns.adobe.com/flex/spark"
xmlns:mx = "library://ns.adobe.com/flex/mx"
minWidth = "955"
minHeight = "600">
<fx:Script>
<![CDATA[
protected function button1_clickHandler ( event : MouseEvent ) : void {
// TODO Auto-generated method stub
hello ();
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script source = "./scratch/MyFile.as" />
<s:Button label = "Show Alert" click = "button1_clickHandler(event)" />
</s:Application>
You'll notice that the ActionScript in the scratch.MyFile.as is executed when the application is run.
You can use this method to include your drawing logic in your application. The external ActionScript is treated as if it were the method definitions for the class generated by the MXML.
Let me caution you before everyone jumps all over me. This is NOT a best practice. There are specific use-cases in which to use this feature of Flex. Before I get to them let me explain why your notion of keeping "logic" separate from your "view" is inaccurate.
Flex MXML files are not view-only code. They are a declarative dialect that simplifies ActionScript class definitions. When a Flex Project is compiled using mxmlc or compc (those are the compiler programs for flex applications and component libraries, respectively), MXML files are pre-compiled into ActionScript class definitions. If you add the -keep-generated-actionscript directive to your compiler options in Flash/Flex Builder/Ant/command line compile command, the compiler will leave the generated classes that compose the ActionScript class derived from the declaritive MXML files in your project. Thus, an MXML file becomes a class.
Having ActionScript defined in a Script block in your MXML is NOT mixing "logic" with "presentation". Likewise, MXML does NOT define the presentation of your project. It simply is a declarative subset of the ActionScript language that makes it easier to define presentation classes. As proof, you can define ArrayCollections, Strings, and Vectors in MXML. Those classes are data containers and have absolutely nothing to do with presentation.
Now, the reason putting all your ActionScript in external files is not a good thing is that it makes maintaining your project a headache. Not only does someone have to look up your component defined in MXML, but now they have to hunt through the codebase to find the script named Logic.as relatively defined to your component.
The use-cases in which including the external ActionScript via Script.source are the following:
A group of predefined methods/properties are used in lots of components without change. Rather than copy-and-paste those methods into each component, you use the Script.source property to define the methods once and reference them throughout your application.
Similar, but different from use-case-1: You require a mix-in. Like an interface, a mix-in is a set of methods that must be defined for the component to work, and those methods must be reusable and define explicit input parameters and output parameters. Unlike an interface, those methods can be of any namespace type: protected, final, public, internal, mx_internal, etc., and can must function bodies, i.e. have code inside the {} function block. For an example of a "mix-in" think of an enumerable object, an object that has next(), previous(), reset(), and iterator(), methods. When you want to iterate over the object's properties, you call iterator(), which returns an iterator object that calls next() and previous() to fetch the next and previous property values from the object. You can mix this functionality into all kinds of objects and use them usefully. Also, your included functionality is encapsuled within the classes in which it is included. It works like an include directive in AS3.
Hope this helps both your perceptions of what "logic" and "presentation" are in Flex, and helps you solve your particular issue.
I think you have it backwards. If you include the .as file into the .mxml using the <mx:script> tag, you will be able to see the function defined therein.
To address the image, set it an id attribute. From that point, it becomes addressable as if it were defined using ActiveScript like
var image:Image = new Image()
It is no very obvious what relations are between MXML and AS classes you're talking about but the simplest way is create public method in MXML which returns Image. Something like this:
…
<mx:Script>
<![CDATA[
public function getImage():Image
{
return myImage;
}
]]>
</mx:Script>
<mx:Image id="myImage" />
…
So as far as you can refer to your MXML from AS you can call this method.
If you want to draw something in an image by using your own drawing class, i suggest you add it to the constructor of your drawing class, like so:
/*********************************************
* Variables
*********************************************/
private var _myImageIWantToDrawIn:Image;
/*********************************************
* Properties
*********************************************/
public function set image(value:Image):void
{
_myImageIWantToDrawIn = value;
}
public function get image():Image
{
return _myImageIWantToDrawIn;
}
/*********************************************
* Constructor
*********************************************/
public function myDrawingclass(imageYouWantToDrawIn:Image)
{
_myImageIWantToDrawIn = imageYouWantToDrawIn;
}
like this, you can always access the image from that class and draw in it if you want to. (You can also access the _myImageIWantToDrawIn.graphics if you want to draw programmaticaly). you can add the properties if you want to change the image in runtime. you can then simply say: myDrawingclass.image = imageYouWantToDrawIn;
edit: this previous answer was actually the opposite of what you are asking, but I think this will do just fine for what you would want to do. If you want to access your as-class, just make an instance of your class and add public methods to it instead of private ones.

Accessing a class from an externally loaded Flex SWF

Using Flex 4, I have created an SWF that has the following code:
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import com.Index ;
protected function start(event:FlexEvent):void
{
this.addElement (new Index());
}
]]>
What I am trying to do is load the SWF into another SWF and access this class. The problem is the Flex class Main is what is recognized by the loading SWF. I have tried accessing its children(elements), but the added item is not there.
My ultimate goal is to load an SWF that has an object that can interact with the loading SWF.
lee
Addition:
Thanks for your addition. The code is working now, but it only access the Flex class created by the MXML. Here is the complete MXML:
<?xml version="1.0" encoding="utf-8"?>
width="1000"
height="700"
creationComplete="start(event)"
>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import Test;
protected function start(event:FlexEvent):void
{
this.addElement(new Test ());
}
]]>
To try and extract the class Test, I did the following:
var MainC:Class = L.contentLoaderInfo .applicationDomain .getDefinition ("Main.Test") as Class;
var O:Object = new MainC();
This returns 'Error #1065: Variable Test is not defined.'
I get the same result if I use ...getDefinition("Test").
If I use ....getDefinition("Main"), it works fine. But I cannot seem to reach into the class it creates to extract anything.
lee
The best way to go is to have that swf as a library (swc) and link it to your project. That way you could achieve strong typing, and access the underlying classes.
If you don't have access to neither the library release of the swf object, nor it's source to build the swc library yourserlf, you can still use the external classes, but not with strong typing:
var ExtIndex : Class = loader.contentLoaderInfo.applicationDomain.getDefintiion("Index");
var instance : Object = new ExtIndex();
this.addElement(instance);
so you must define your instance as Object.
Although, in case the Index class in your swf implements an interface that you know of (you have it in your project), like I_Index, you can use it instead of Object:
var instance : I_Index = new ExtIndex();
this.addElement(instance);
You might also find this article useful in deciding which way to go.
Update 1
Declaring the loader in Flex:
var loader: flash.display.Loader;
[...]
var ExtIndex: Class = loader.contentLoaderInfo.applicationDomain.
getDefinition("your.package.Index") as Class;
In case you have your Index class in a package (other than the default), you should specify it in its name. The code above searches for the Index class inside the your/package package.
API Docs for getDefinition:
Gets a public definition from the
specified application domain. The
definition can be that of a class, a
namespace, or a function.
Parameters:
name The name of the definition.
Returns: The object associated with the definition.
Throws: ReferenceError - No public definition exists with the specified name.
Language Version: 3.0
Player Version: Flash 9, AIR 1.0
Update 2
To make the Test class defined in your external application accessible from other flex applications by loading the swf that contains it, you must have a variable declared with that type inside your swf's main application. Otherwise that class won't be compiled into the swf (to save the size of it). Flex only compiles classes actually in use into the swf, so it's not enough to just import a class. In the main application of your swf you should declare:
var _tmp:Index;
This makes sure that the Index class will be compiled into your swf, and you can access it from an other application which loads that swf.
Update 3
You can check out how this works at this link. The source code is included too.
Since there are two projects, in the [src] folder you can see the test's source, and in the [source path src] folder the source of the externally loaded swf file.
If you download the whole project, make sure that you separate the two projects.

Setting the WebServiceWrapper endpointURI at run time

I'm in the middle of switching from Flex Builder 3 to Flash Builder 4, and one of the problems I have run into is that support for web services in 4 is substantially different. In both IDE's I am able to import a WSDL for my web service and it will generate the appropriate client classes for communicating with the service. The generated code in each is different.
In my Flex3 code I was able to access the endpointURI property of the mx.rpc.soap.AbstractWebService, but in the Flex4 code that is generated, the new class extends com.adobe.fiber.services.wrapper.WebServiceWrapper which does not have the endpointURI property.
My project has mulitple game servers and the player picks which server they want to play on. In the past if the player wanted server 1, I would set the endpoint URI to http://game1.server.com/service.asmx, and like wise if they wanted server 2 I would set the endpoint to http://game2.server.com/service.asmx.
What am I looking for to accomplish this in Flash Builder 4?
Short Answer:
var s:ClassThatExtendsWebServiceWrapper = new ClassThatExtendsWebServiceWrapper;
s.serviceControl.endpointURI = 'http://service.com/service.asmx';
Long Answer:
Well I finally found a solution. Adobe seems to have made this much harder than it should have been.
Web Service classes that are generated by Flash Builder 4 extend the com.adobe.fiber.services.wrapper.WebServiceWrapper. WebServiceWrapper has a property called serviceControl that can be used to control the service. The problem is that not all the members of serviceControl are accessible at the application code level. Lets assume that I have a web service called GameService. When I use the data tool to connect to the web service by providing a WSDL, Flash Builder will create two classes for me automcatically.
internal class _Super_GameService extends
com.adobe.fiber.services.wrapper.WebServiceWrapper
{ ... }
public class GameService extends _Super_GameService
{}
_Super_GameService contains all the automatically generated code to make calls to the web service. GameService contains no code itself, but unlike _Super_GameService, it is public. The idea here is that any enhancements that we need to make can be made to GameService, then later on if we need to update, _Super_GameService can be regenerated, but out changes to GameService will not be overwritten by the code generation tool.
Now this leads us to usage of these generated classes. Typically all I should have to do is create an instance of GameService and call a method on it. In this example DoSomethingAwesome is a method available on the web service.
var gs:GameService = new GameService();
var token:AsyncToken = gs.DoSomethingAwesome();
Now this will call the service using the URI of the service specified in the WSDL file. In my situation I wanted GameService to connect to a different URI. This should have been simple, but things fell apart.
My first problem was that viewing the documentation on WebServiceWrapper (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/com/adobe/fiber/services/wrapper/WebServiceWrapper.html) did not render properly in Firefox. So when I was reading the documentation I wasn't getting the full picture. This really needs to be fixed by Adobe.
Viewing the documentation in another browser helped me find out about the serviceControl property of WebServiceWrapper. serviceControl is declared as a mx.rpc.soap.AbstractWebService. AbstractWebService does have an endpointURI property which makes the following code valid.
var gs:GameService = new GameService();
gs.serviceControl.endpointURI = 'http://game1.service.com/GameService.asmx';
The other problem I had is that for some reason the endpointURI property of serviceControl does not appear in the Intellisense context menu. So since I didn't see serviceControl in the online documentation at first, and I didn't see endpointURI in intellisense, I didn't realize the property was there to be set.
If you look at the source for AbstractWebserivce, (http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/frameworks/projects/rpc/src/mx/rpc/soap/AbstractWebService.as) there doesn't seem to be an Exclude tag to explain why endpointURI does not appear in the Intellisense context menu. So I don't know what is going on there.
You should be able to override the endpointURI on the WebService. But I'm not sure where to do that with the generated code since I use <s:WebService/>.
This is the only way I could get it to work, in the generated stub for your service:
import com.adobe.fiber.core.model_internal;
Also:
/**
* Override super.init() to provide any initialization customization if needed.
*/
protected override function preInitializeService():void
{
_needWSDLLoad = false; // to prevent loading the default WSDL
super.preInitializeService();
// Initialization customization goes here
wsdl = "http://localhost/yourservice?wsdl";
_needWSDLLoad = true;
model_internal::loadWSDLIfNecessary();

Resources