Bye bye -services!

When you are building an application that uses the Flex FDS or ColdFusion servers as a back-end you need to define in your FlexBuilder project properties an additional compiler argument, -services. Why? FlexBuilder needs this argument/path to your services-config.xml file so it can pull just a few pieces of information out of the xml file. In turn, these pieces of information are what allows your flex client application to know what server to talk to and how. However, this has a few drawbacks.

  • You need to be able to access your services-config.xml file. Which can be difficult if you are developing against a remote Flex server. Confusion with the launch url in FlexBuilder.
  • By default endpoint urls in the services-config.xml use wildcards to define the host/port of the url "{server.host} and {server.port}". However, the default for FlexBuilder is to launch flex applications with a file://c:\ absolute path, easily corrected but still the default. Now the problem is that if your movie is launched with a file:// path, Flex can't use the {server.host}/{server.port} wildcards (there is no http url to pull the host and port from)
  • But, you can't hardcode the url in services-config.xml either. Otherwise your .swf will be compiled with a url that points back to your development server. And you don't want to recompile the .swf for production because then the .swf you tested is NOT the same .swf as the one you deploy. (you always want to deploy the same code you test). Context-root problems, if you develop with one context-root and deploy to production under a different context-root - Watch out!
  • Because the context-root needs to be hard coded in the services-config.xml or defined as a compiler argument in FlexBuilder too, it is compiled into the swf as a hard coded value. Which means, you need a different .swf for each platform you run one (a testing nightmare).

Alas, there is a solution! (You knew I was going there didn't you). All of this can be defined at runtime without the -services argument and the services-config.xml. Defining this at runtime also has a few advantages.

  • Your context-root can be defined at runtime as a FlashVar and your .cfm or .jsp can easily set that with a simple variable.
  • You can switch URL's as needed, for instance if the movie is loaded under an http:// url, use the wildcards. If it's loaded under a file:// or c:\.. path use a hard coded development url.
  • Different developer enviroments and configurations don't matter, everyone should be able to drop the code, sorry sync from source control, on their machine and run the application.

There are different ways to do this but how I do it is with 2 files; a ConfigModel class and a standard ServiceLocator class.

ConfigModel.as



package model
{
    import mx.core.Application;
    import mx.controls.Alert;
    import mx.utils.ObjectUtil;
    
    /**
     * Used to store Application config settings, and the
     * FlashRemoting channel and endpoint url settings, with this you
     * won't need define a -services compiler argument for the project.
     *
     * @author: Mike Nimer
     **/

    [Bindable(event="configChange")]
    public class ConfigModel
    {
        public static var TIMEZONEOFFSET:Number; //est=4 or 5, depending on DST
        
        // debug setting to switch between live and stub data
        public static const USE_STUB_DATA:Boolean = false;

        // Flash remoting config issues
        public static const CHANNELID:String = "orca-amf";        
        private static const default_context_root:String = "/"; // adjust as needed for your default server.


        /**
         * pull the context-root out of the flash var, if it's defined in your <object/> or <embed/> tags.
         * ex: contextRoot=/flex
         **/
        
        private static function get CONTEXT_ROOT():String
        {
            var app:Application = Application.application as Application;
            if( app.parameters.contextRoot != null )
            {
                //Alert.show( ObjectUtil.toString(app.parameters) );
                return     app.parameters.contextRoot;
            }
            return ConfigModel.default_context_root;
        }
        
        
        // return the correct endpoint, if this is server from an HTTP request
        // return the default localhost:8500 for development, if FlexBuilder launches
        // the application locally with an absolute path (file://c:\...)
        public static function get AMFENDPOINT():String
        {
            var app:Application = Application.application as Application;
            if( app.url != null && app.url.indexOf("http:") != -1 )
            {
                return "http://{server.name}:{server.port}" + ConfigModel.CONTEXT_ROOT +"/messagebroker/amf";
                // for ColdFusion users
                // return "http://{server.name}:{server.port}" + ConfigModel.CONTEXT_ROOT +"/flex2gateway";
            }
            else
            {
                return "http://localhost:8500" + ConfigModel.CONTEXT_ROOT + "/messagebroker/amf";
                // for ColdFusion Users
                // return "http://localhost:8500" + ConfigModel.CONTEXT_ROOT + "/flex2gateway";
            }
        }

    }
}


ServiceLocator.as



package services
{
    import model.ConfigModel;
    import mx.messaging.Channel;
    import mx.messaging.ChannelSet;
    import mx.rpc.remoting.mxml.RemoteObject;
    import mx.rpc.events.ResultEvent;
    import mx.messaging.channels.AMFChannel;
    import mx.rpc.events.FaultEvent;
    import mx.rpc.AbstractService;
    import mx.messaging.config.ServerConfig;
    
    /**
     * Create the RemoteObject tags used by the application service delegates
     * @author: Mike Nimer
     **/

    public class ServiceLocator
    {
        public var service:AbstractService;
        public var amfChannelSet:ChannelSet;
        public static var serviceLocator:ServiceLocator;
        
        public function ServiceLocator()
        {
            var amfChannel:Channel = new AMFChannel(ConfigModel.CHANNELID, ConfigModel.AMFENDPOINT);
            amfChannelSet = new ChannelSet();
            amfChannelSet.addChannel(amfChannel);
        }        
        
        public static function getInstance():ServiceLocator
        {
            if( ServiceLocator.serviceLocator == null )
            {
                ServiceLocator.serviceLocator = new ServiceLocator();
            }
            return ServiceLocator.serviceLocator;
        }
        
        
        /**
         * Individual Services used by the Application.
         **/
                
        public function getSomeService():AbstractService
        {
            if( service != null )
            {
                return service;
            }            
            service = new RemoteObject('someDestination');
            service.channelSet = this.amfChannelSet;
            (service as RemoteObject).showBusyCursor = true;
            
            // service.source = "dot.path.to.cfc" // If you are using ColdFusion            
            //service.setCredentials(username, password); // if you need it
    return service;
        }
        
    }
}

Set Context-root FlashVar (ColdFusion)


// You'll find this in the default html flex generates, just make the html page a cfm and add this FlashVar.
// note: Use contextRoot=<%=request.getContextPath()%> for JSP pages.

    AC_FL_RunContent(
            "src", "Orca",
            "width", "100%",
            "height", "100%",
            "align", "middle",
            "id", "Orca",
            "quality", "high",
            "bgcolor", "#869ca7",
            "name", "Orca",
            "flashvars",'contextRoot=#getContextRoot()#&historyUrl=history.htm%3F&lconid=' + lc_id + '',
            "allowScriptAccess","sameDomain",
            "type", "application/x-shockwave-flash",
            "pluginspage", "http://www.adobe.com/go/getflashplayer"
    );

Comments
Webdevotion's Gravatar Just what I was looking for. Great for shared hosting environments.
Thanks!
# Posted By Webdevotion | 1/11/07 1:36 PM
Webdevotion's Gravatar I run into this error:

The return statement cannot be used in static initialization code.   
on lines 43 & 46 in model/ConfigModel.as
# Posted By Webdevotion | 1/11/07 2:07 PM
Webdevotion's Gravatar Found, copy paste error in my editor.
Please delete this foolish comments :)
# Posted By Webdevotion | 1/11/07 2:09 PM
Cristian Pop's Gravatar What about parsing the service-config.xml at runtime and using that info instead of FlashVar ? I may be way off here but it just came to me while I was reading you post.
# Posted By Cristian Pop | 1/18/07 4:53 PM
Mike Nimer's Gravatar Parsing some other XML for the data makes sense. However, I wouldn't parse the services-config.xml. If you need to do that just use the "-services" option. It's a lot easier and you don't need to write any code.
# Posted By Mike Nimer | 1/18/07 9:17 PM
Dan Zeitman's Gravatar Mike,
Thanks, cool idea - I'm still new to eclipse, BUT isn't there a way to setup vars and use them within the IDE? One of the problems I see with using the flashvars here as you suggested, is you still need to edit the html-template for each project. (doesn't seem to be a huge shortcut)

My preference would be to setup a value somewhere in Eclipse \ (actually I use Flexbuilder ) to note the current "testing" setup and then change change that value when I'm ready to deploy.

--- I might be mistaken, and MAYBE, this functionality is there, but it's not documented or discussed anywhere that I can find. --- Info on "context-root" and the services.xml file is sparse.

I spoke briefly with Tom Lane (on Flexbuilder team) he contridicted what you say here about what the -context-root "" setting is used for. He said it's only for Java server stuff, has nothing to do with the deployment pathing and should be left blank.

I posted this question to Ben Forta in the past:
He said this is where you make the changes, that you simply create different service.xml files for each deployment scenerio. Ben is great, but that didn't work well with the CRUD wizards.
and now I'm here reading your post, which agrees with Ben's Logic, and suggests the opposite of what Tom on the Flexbuilder team tells me!

So I'm completely confused... Maybe you can futher explain the context-root and -services compliler options, and the service.xml files?

Here's why!! - I rarely test or deploy off the webroot. I use the CRUD wizards all the time to code 90% of the remoting and db intereaction of my flex apps. I'm exhaused with using "find in files" to search and replace server deployment values each time I re-run the wizard.

A better understanding of the IDE, the compiler options and service.xml would be helpful - since I'm guessing that my frustration is not singular. Maybe you or one of your readers, have some suggestions?

Keep up the great work!
# Posted By Dan Zeitman | 1/28/07 11:20 AM
Mike Nimer's Gravatar Dan,
There are ways to set up vars, and you can use them in eclipse. However, that has nothing to do with this problem/solution. They are good for things like project library properties.

So I think the thing you are missing is that for flash to talk to a server, it needs to call a servlet - a file at a specific url.

Tom is right context roots are java stuff on the server. But I think he is talking about something different. When you deploy a java servlet (the flex servlet) you do it under a context-root on the server, which could be blank or it could be a "/something" string. Now you can hard code the path to the context-root in the url that is used by flash client. But if you deploy your local/dev/test/production servers under different context-roots (so the url is different), then you need to use a variable in the url.

If you hard code the path to the url, then Tom is right you don't need it or you can leave it blank. However, if your path is dynamic and you use the "{context-root}" variable in the endpoint url to your servlet, then you can define that at compile time with the -context-root compiler flag, or as a FlashVar at runtime as I've described above. Basically if your using context roots then you need to worry about it, if your not then don't worry about it.

And Ben is right too. However, he is saying that you should define services-config.xml files for each of your different configurations. The problem with this, you have to re-compile the swf for each server configuration. What I've described in my post was a different way to do the same thing. I didn't want to recompile for each server configuration, I wanted one swf to work on all configs for all developers. We are both describing different solutions for the same problem.

So really it's just a matter of defining the right url to the flex server servlet in your flash client. Iif your urls are the same on all of your different server configurations (dev, production, etc.) Just hard code the url, and don't worry about all of this context-root stuff. If it is different you need to decide how to handle it; multiple service-config.xml files or runtime settings.
# Posted By Mike Nimer | 1/28/07 4:59 PM
Hua's Gravatar Mike, thanks for sharing this.
how to use your method to replace the following two mx:RemoteObject? Do i need to setup two diffenect getSomeService() methods in ServiceLocator.as? I appreciate you provide examples on how to use it.

<mx:RemoteObject
   id="productService"
   destination="ColdFusion"
   makeObjectsBindable="true"
   source="store.business.Product"
      showBusyCursor="true">
<mx:method name="getAllProducts"
       result="handleGetProductResult(event)"
       fault="Alert.show(event.fault.message)" />
</mx:RemoteObject>

<mx:RemoteObject
   id="creditCardService"
   destination="ColdFusion"
   makeObjectsBindable="true"
   source="store.business.CreditCard"
   showBusyCursor="true">
<mx:method name="getAllCreditCards"
       result="handleGetProductResult(event)"
       fault="Alert.show(event.fault.message)" />

</mx:RemoteObject>
# Posted By Hua | 2/7/07 3:31 PM
Darren Houle's Gravatar Mike,

How do you set/define the service's <adapter-definition> in AS? The livedocs just say you have to put it in the services-config and reference that. Doesn't that affect how dynamic you can get with AS service definitions?
# Posted By Darren Houle | 3/21/07 8:41 PM
Tom Chiverton's Gravatar "You need to be able to access your services-config.xml file. Which can be difficult if you are developing against a remote Flex server. Confusion with the launch url in FlexBuilder."
We just have a local services-config.xml stored at the same level as the base 'src' directory, and compile against that. All our devs have local CF installs and setup Eclipse for that, so we can leave the {} place holders in and it Just Works.
# Posted By Tom Chiverton | 12/3/07 7:27 AM
????'s Gravatar He said this is where you make the changes, that you simply create different service.xml files for each deployment scenerio. Ben is great, but that didn't work well with the CRUD wizards.
# Posted By ???? | 1/8/08 3:47 AM
Hmm's Gravatar This is great, but it sucks at the same time. Adobe needs to find a better solution to this problem.
# Posted By Hmm | 1/25/08 6:03 PM
Greg's Gravatar keeping this thread going... I have done exactly how you describe. The error I get is: The MessageAgent's destination must be set to send messages.']' faultDetail:'Couldn't establish a connection to '''
I guess I'm really trying to understand where/how the compiled SWF is asking for the path to the "services-config.xml". Using CF7.02 for Flex 3beta3 (company won't purchase till this is deployed).
Many different configurations going from "sandbox" to development, staging/testing, production (we can't be like most shops who have mirrored environment configurations --frustrated sarcasm--)…
and we can not be changing the path to the services-config.xml file for each migration to the next environment. should I try mapping something? couldn't we just put the call to the CGI vars in the
CFM wrapper and pass them in? If we do that, what var is the SWF looking for in the FlashVars to pass in? Would I be using the same var passed in from Application.application.parameters.PASSED IN VAR and use it in my RemoteObject?
# Posted By Greg | 3/5/08 5:18 PM
Greg Murnock's Gravatar I have my wrapper CFM create the toString("http:/" & CGI.SERVER_NAME & "/flex2gateway/") then pass it in via flashVars, read the flashVar in my init() and set it to a new var and then use it in the
RemoteObject endpoint setting. Works like a charm.
# Posted By Greg Murnock | 3/6/08 4:02 PM
Ryan H's Gravatar Our production site is https, what modifications do I need to make for this to work?

we have a shared development server, so I don't need to worry about the "file//c:\" situation.
# Posted By Ryan H | 4/30/08 2:18 PM
g's Gravatar Any chance you have an example of this you can post?
# Posted By g | 6/18/08 5:58 PM
Apimpl's Gravatar Damn...
U can just use relative paths......
# Posted By Apimpl | 7/31/08 5:22 AM
SpesBona's Gravatar very well done!
# Posted By SpesBona | 10/22/08 8:23 PM
Erick Ruiz's Gravatar Just to comment an issue I just resolved...

If you need to connecto to yout amf endo point and its url is in a secure environment (a.k.a. HTTPS) you need to change the AMFChannel to a SecureAMFChannel. Took me some time to figure it.
# Posted By Erick Ruiz | 3/22/09 1:22 AM
Akshaya K rawat's Gravatar Hi ,
I am newt o Flex and BlazeDS. Going through the post , just thought can we do following -
1. Read the endpoint from a xml file , using <mx:HttpService> ,like they do in a lot of examples in Tour-De-Flex.
2. In the <mx:RemoteObject > use endPoint as the value read from the xml.

Though this would mean that you will have to change the XML file at the deployment time. But this could be like
chaning a web.xml of a web project.
# Posted By Akshaya K rawat | 5/21/09 7:00 AM
twelve sky Gold's Gravatar I really like your post, thanks for sharing.
# Posted By twelve sky Gold | 6/6/09 5:36 AM
twelve sky Gold's Gravatar I really like your post, thanks for sharing.
# Posted By twelve sky Gold | 6/6/09 5:36 AM
pisany's Gravatar For more information and advice on choosing a cat name that is right for you please visit http://www.catwebsite.org/types-of-cats.htm
# Posted By pisany | 6/7/09 1:22 AM
habbo credits's Gravatar I like play online game, and is very cheap,
# Posted By habbo credits | 6/11/09 1:08 AM
lyff penya's Gravatar I really like your post, thanks for sharing.
# Posted By lyff penya | 6/11/09 1:32 AM
???'s Gravatar A slim, wide-eyed [url=http://www.mygamegold.net]mygamegold[/url]woman almost human in [url=http://www.virbanks.net/]virbanks[/url]features eyed the pair. Her nose [url=http://www.agamegold.org/]agamegold[/url]was sharp, but very elegant. She had a [url=http://www.tbcgold.net/]tbcgold[/url]pale, beautiful face the color[url=http://www.trade4game.org/]trade4game[/url] of ivory, and for [url=http://www.veryge.net/]veryge[/url]hair she wore a wondrous mane of downy feathers.
# Posted By ??? | 6/13/09 11:09 AM
Sho Online Mun's Gravatar The website sells the kinds of game gold, you can buy in here, and you can find the gold is very cheap.
# Posted By Sho Online Mun | 6/15/09 1:39 AM
World of Warcraft Gold's Gravatar If you have any good advice, please post it on our website, and our after service is also good.
# Posted By World of Warcraft Gold | 6/15/09 1:40 AM
knight online gold's Gravatar As a new player , you may need some game guides or information to enhance yourself.
<a href="http://www.gamekoo.com/product/Knight_online_Noah.... gold</a> is one of the hardest theme for every class at the beginning . You must have a good way to manage your <a href="http://www.gamekoo.com/product/Knight_online_Noah.... noah</a>.If yor are a lucky guy ,you can earn so many <a href="http://www.gamekoo.com/product/Knight_online_Noah.... online gold</a>
by yourself . But if you are a not , I just find a nice way to get <a href="http://www.gamekoo.com/product/Knight_online_Noah.... online noah</a>. If you need , you can buy <a href="http://www.gamekoo.com/product/Knight_online_Noah.... knight gold</a> at our website . Go to the related page and check the detailed information . Once you have any question , you can connect our customer service at any time .
# Posted By knight online gold | 6/15/09 6:05 AM
dofus kama's Gravatar Do you know dofus kama?
# Posted By dofus kama | 6/15/09 7:25 AM
Bounty Bay Coin's Gravatar good...
# Posted By Bounty Bay Coin | 6/17/09 12:18 AM
Dragonica Gold's Gravatar good
# Posted By Dragonica Gold | 6/17/09 12:29 AM
LOTRO Gold's Gravatar i like it
# Posted By LOTRO Gold | 6/22/09 1:06 AM
mabinogi gold's Gravatar it is cheap
# Posted By mabinogi gold | 6/22/09 1:07 AM
lastchaos gold's Gravatar It is really what I want to find out, thanks.
# Posted By lastchaos gold | 6/22/09 2:18 AM
buy lindens's Gravatar good site
# Posted By buy lindens | 6/22/09 4:57 AM
Darkfall Gold's Gravatar It is so nice.
# Posted By Darkfall Gold | 6/22/09 6:31 AM
hot news's Gravatar How do you set/define the service's
http://blog.6vie.com/search/label/fashion/
# Posted By hot news | 6/24/09 1:26 AM
daily news's Gravatar Yes I did, the difference is that this is a new adapter that works

inside of official Adobe servers..
# Posted By daily news | 6/24/09 1:27 AM
louis vuitton's Gravatar Once upon a time, in the place of our wandering winter wonderland,

where we roam labyrinths laced with magic
http://www.replicaslouisvuitton.com/2009-collectio...
# Posted By louis vuitton | 6/24/09 1:29 AM
Fake rolex's Gravatar Thanks, cool idea - I'm still new to eclipse
# Posted By Fake rolex | 6/24/09 1:33 AM
DAOC Plat's Gravatar good...
# Posted By DAOC Plat | 6/26/09 2:55 AM
SOF gold's Gravatar The only part of this game will require you to pay SOF gold.
# Posted By SOF gold | 6/28/09 11:10 PM
Megaten money's Gravatar good game gold
# Posted By Megaten money | 6/29/09 12:48 AM
# Posted By 111 | 6/29/09 7:38 AM
Guest's Gravatar ust to comment an issue I just resolved...

If you need to connecto to yout amf endo point and its url is in a secure environment (a.k.a. HTTPS) you need to change the AMFChannel to a SecureAMFChannel. Took me some time to figure it. http://telkixxxboyss.ru http://sexxxxtrajh.ru http://sexytrahxxxj.ru http://sexyxtraxk.ru http://telkdixxxboyss.ru
# Posted By Guest | 6/30/09 10:25 AM
# Posted By jeff | 6/30/09 10:25 AM
# Posted By 111 | 7/1/09 9:18 AM
[Home]