Author Archives: Ruprict

About Ruprict

I am a nerd that is a Nerd Wannabe. I have more kids than should be allowed by law, a lovely wife, and a different sense of humor than most. I work in the field of GIS, where I am still trying to find myself on the map.

2010 ESRI Dev Summit Wrap Up

Back in Charlotte after another lively ESRI Developers Summit.  I went back and read my impressions from last year, and have to say that they were hit and miss.  You could replace last year’s impressions mentioning 9.4, with some mentioning 10 (the new and improved 9.4) and it would at least partially apply.  New stuff this year to get your inner (and outer) GIS nerd in a frenzy are:

  • Editing from the web.  The new FeatureLayer in the REST (and, thus, the various web) API is the big deal.  Simple editing of GIS data from the web.  In my oft-hyperbolic opinion, this is a game changer.
  • Attachements support in the REST API.  I have mixed feelings about this, as it seems that ESRI might be trying to make the geodatabase the everything-base, but I guess attachments are just another kind of data.  I can see cases where we’d use this, but I plan to be very careful…
  • Scriptable REST admin (my sessions were almost all either REST or Flex or both), which could be very useful.
  • REST-enabled Server Object Extensions (SOE) look very promising as well.
  • The Flex API has AMF support at 10.  Truthfully, I’ve not done much with AMF in Flex, but I understand it’s superdy-duperdy fast.  That’s on the immediate todo list.
  • Also on the Flex side, although not ESRI specific, is the release of Flex 4.  I went to a couple of sessions where they demoed Flash Catalyst, Flash Builder, and the new workflow.  I finally understand what Catalyst is, which is a good thing.  Depending on it’s cost, we may or may not use it.
  • Various small bits, like point clustering being supported by the GraphicsLayer, complete with a cool “flare-out” symbol.

This year was the second where users were allowed to present.  I presented on Cairngorm 3 and best practices in Flex.  I thought the presentation when as well as I could have hoped.  The slidedeck and code are available, and you can find all that information here.  The app I used basically allows the user to draw a polygon around NFL stadiums in the US and then click on the selected stadiums to see a pop-up with an aerial view of that stadium.  Cairngorm 3 and Parsley made it very easy to create the app, and the amount of code I had to write is shockingly little.

I will say I was surprised at the number of Flex vs Silverlight developers this year.  Last year, I wrote about Silverlight being the Queen of the Ball, with most developers I knew going to all SL sessions.  The buzz was much bigger about SL then, which was a 180 turn-around this year.  All of the Flex sessions seemed to be packed, and the buzz was Flex-heavy.  I didn’t actually go to any SL sessions, but I heard more than one developer say that the sessions seemed less full than last year.  Maybe it’s an alternating year thing or something.  Or maybe the release of Flex 4 on the Monday of the summit had something to do with it.  If you are a Sliverlight developer, please bear in mind that I don’t really care if Flex or SL has more “buzz” or attendees, but I just find the dynamic between the two camps and their respective APIs mildly interesting.

For more info, you can go to ESRI’s Dev Summit site and watch plenary videos as well as all tech sessions.    The user sessions aren’t posted yet, but they’re coming.  If I think about it, I’ll post a link to mine when it comes online.  Oh, and if you checkout the Twitter #devsummit tag, you’ll see a mountain of info and links for your perusing pleasure.

It was a great conference, as always.  Already looking forward to next year.


2010 ESRI Developers Summit

So, I’m off to the ESRI Dev Summit next week to meet and learn from a legend (the official unit of measure of geonerds) of geonerds.  I will be giving a user presentation on using Cairngorm 3 to create testable applications with ArcGIS Server.  The presentation is all but done, and I’ll have links to the slides as well as the source I use for the demo app once the conference is over.  I am very interested in some of the other user presentations, which span the gamut of what can be done with ArcGIS and a bit of nerd elbow grease.  I’ll definitely be attending the Ruby/Rails based user presentations, as well as some of the other Flex and javascript-based presos.  Just like last year, I’ll not likely go to any Silverlight presentations, simply because we are not currently using Silverlight.

If you’re going to be in Palm Springs this year and want to have a pint or ten, hit me on twitter (@ruprictGeek).  For what I do, the ESRI Developers Summit is far-and-away the most relevant and important conference, so the more geonerds I can meet, the better.

Hope to see you there!


ArcGIS Javascript API Tasks and the Back Button

Today I was working on an issue with a Sharepoint (I know, I know) web part that we have created to host a map. The web part using the ESRI ArcGIS Javascript API to dynamically load some graphics and layers when the page is loaded. The structure of the Sharepoint pages is hierarchical, so the user would select a property, then a site on that property, then an agreement on that site. Each of these pages contained a map that zoomed to the relevant area and loaded the relevant graphics/layers. This hierarchy leads the users to hit the browser Back button quite often. Go to a property, then a site, back to the property, to another site, then an agreement, back to the site….you get the idea.

Unfortunately, when the user hit the back button in that piece of crap Internet Explorer, the queries would not fire. In all other browsers, the queries work fine when the back button is clicked, but not IE. As you probably already know, if an enterprise is using Sharepoint, then they are using IE as their standard browser. This issue was causing a ton of grief.

I tried many different approaches, from forcing HTTP headers (Pragma: no-cache or Expires: -1) to trying to reload the page if the history.next object was defined. What finally worked was slightly changing the URL that task used so that IE wouldn’t use the client-side cache. I used a random number generator in javascript and appended it to the URL. The code is below.


var find = new ESRI.ArcGIS.VE.Query();

find.Where="ATTRIBUTE='"+this.value.toUpperCase()+"'";

find.OutFields=["ATTRIBUTE,OTHER_ATTRIBUTE"];

var findTask = new ESRI.ArcGIS.VE.QueryTask();

//Have to append a random number to get this to work when back button is pressed

findTask.Url=th.Url+"/3?_esi="+Math.floor(Math.random()*111);

findTask.Execute(find,dojo.hitch(this,this.handleResults));

The “_esi=” is just a key I put in to know it was mine and is, likely, unnecessary. May not be the best solution (I bet Vish will skewer it) but it works. Anyway, this took a few hours away from my life, so I thought I’d post it here. Hope it helps someone else.


Continuous Integration with Flex, Hudson, and ArcGIS Server-Part V

(Part 1, Part 2, Part 3,Part 3, Part 4)

In what I hope is the last post in this particular series, we will get Hudson up, building our project and running our tests. Let’s get to it.

Get Hudson

Download Hudson from here. This will download hudson.war, which is a java web archive. Oh, and I presume that you have a java SDK installed somewhere. If you don’t go to java.com and get one.

Start Hudson

Hudson is super easy to get up and running. I suggest you copy the hudson.war file to c:\hudson and open a command prompt to that directory. Type:

java -jar hudson.war

This will start the Hudson service on port 8080. If you have anything already running on this port (I did) you need to shut it down or pass the –httpPort= to the command with the port you wish to use.

Talk to Hudson

Open up a browser to http://localhost:8080 and you’ll see this:
Hudson Dashboard

We need to create a job for our Flex project. Click New Job put in a job name (I am using “AGSFlexBuild”) and select “Build a free-style software project” and Click OK. This will bring up the main configuration of the build. The first item we will set up is the Subversion repository. The high-level process that Hudson will follow is:

  1. Poll Subversion for changes
  2. When a change is detected, run an SVN updte to get the latest version
  3. Build the project
  4. On a successful build, run the tests

So, the first thing we should do is setup our Subversion repository information.

Hudson SVN

Put your repository URL in as shown in the above image. In the “Build Triggers” section select “Poll SCM” and put “* * * * *” in the “Schedule” text box. This will cause Hudson to poll SVN every minute.

Now we have to add the build step. Since we have already set up our ant build script, this part is easy. Click “Add Build Step” and select “Invoke Ant”, then fill out the text box as shown here:
Hudson Build Step

At this point if we ran the build from Hudson, we’d get an error complaining about ${deploy_dir}. The reason for this is we don’t have a local.properties file on the server (remember when we did that?). There are several possible solutions to this issue.

  • Create a ci.properties file, check it in to SVN, and pass a parameter to the build step in Hudson.
  • Create an ant task that copies the local.properties.template file to local.properties, then add a build step in Hudson that runs before the build-flex-project step.
  • Default the properties in the build.xml file.

The last option is the ickiest, but the first two are about the same, in my opinion. I took the second option, creating an ant task and adding it to my Hudson configuration.
Hudson Build Step 2
I leave the creation of the ant task as an exercise for the reader.

The final build step to be configured will run the tests. Add another build “Invoke Ant” build step that invokes the “test” ant task. At the bottom of the page, click the “Save” button. This will take you to the AGSFlexBuild Project page.

Run the build

Click “Build Now” on the left hand side of the page and the build will be scheduled. The build should run successfully. Click on the hyperlink with the build date and time, which will take you to the details of the build. If you click on “Console Output”, the entire build log is displayed. When things go awry in the build, you’ll likely go here first.

A continuous integration process is incomplete without some kind of notification when the build fails. There are a couple of options for notification: e-mail, CCTray, or twitter, just to name a few. E-mail is available in the base Hudson package (see Manage Hudson, Configure System, put in SMTP information). CCTray allegedly works, but I don’t use it and can’t find it. Twitter is available as a Hudson plugin.

Speaking of plugins, there are TONS of them. Twitter, Git, Windows Authentication, etc. You can find them in the Hudson configration pages of the site. I reccommend you look through them and find what you need.  Finally, it’s super easy to get Hudson running as a Windows service, as there is a “Install as Windows Service” option in the Manage Hudson configuration area.

It’s difficult to find a stopping place when discussing Hudson, so if you have questions, send me an e-mail or leave a comment. The code for this series can be found on github (you could fork it and test using Hudson with git, just for fun…. ;)) I hope someone found this series useful.


Continuous Integration with Flex, Hudson, and ArcGIS Server, Part IV

(Part 1, Part 2, Part 3, Part 5)

(Note:  I have started moving our builds to rake, the Ruby build DSL.  It is, howyousay, fantastic.  I see more posts in our series….)

Now that the school year has begun, I can get back into this series.  Last we left, we had ant running the build and the scripts.  Now it’s time to make the build portable, which means that our build.properties file needs to well, go away.  All this really means is taking build.properties and copying it to build.properties.template.  Then deleting build.properties out of source control.  Anyone getting our build our of source control will have to create a local build.properties file, complete with all the settings that make the build go.  This is a “best practice” and stops developers from overwriting each others’ properties when they check in the build file.

So, do that.  Rename build.properties to build.properties.template.  If you ant build-all now, the build throws an error, something about ${wrapper.dir} not found.  That’s because the build couldn’t pull in the properties file.  Copying build.properties.template to build.properties fixes the build.  However, we aren’t done with the renaming.  I want to introduce the concept of build environments to my build.  That way, I can have different properties files for named environments.  I may want to set up different SDK builds, etc. as well, and environments will help me do that.  All I need to do is add a property to the top of the build.xml file to hold my environment name.  It looks like:

<property name="env" value="local"/>

This means that, by default, I’ll be running in the local environment.  This also means that build.properties.template becomes local.properties.template.  Now, I have to copy local.properties.template to local.properties to get the build going.  Another change to the build.xml file is to the line that pulls in our properties file.  It changes from:

<property file='build.properties'/>
to
<property file='${env}.properties'/>

The cool thing is that I can add another file called (for example) 34SDK.properties that points to the Flex 3.4 SDK.  Then to run that build, I can write:

ant build-all -Denv=34SDK

and it will overwrite our env variable and look for a 34SDK.properties file.  Flexibility is fun.

The SCMelephant in the Room

Up to this point, I have committed a cardinal sin.  No source control.  I know, bad developer.   However, I want to get the directory structure set up in a more CI friendly manner before I involve an SCM, so there is a method to the madness.  Let’s do that now.  There are roughly 1, 233, 443 articles on a developer’s workspace or CI directory structure.  I am going to use one that I’ve borrowed (and slightly modified) from Vish.  He is smarter than me, so it must be the way to go.  Here is the structure, in pictures:

3479c0f09a281e0fc8655d29fcba313b

You can ignore the .metadata folder, as that is a Flex Builder thing and I won’t put it in source control.   The build folder will hold the output of our build, docs is self-explanatory, src is where our Flex Builder project lives, and tools holds stuff we need to run the build.  In this case, we need the FluintAirTestRunner.exe, which I’ll copy into my tools dir.  The build file lives in the root of our project space, which means it has moved up relative to the Flex Builder project.  We need to change some things in the build.properties file to get our build going again.

#Copy this file locally to local.properties and change below for your env

# change this to your Flex SDK directory path
FLEX_HOME=C:/Program Files (x86)/Adobe/Flex Builder 3/sdks/3.2.0

# this points to your project's src directory
# {$basedir} is a default variable that can be used in any Antscript
# and it points to the project's root folder [ flex_ant_pt1_Tasks ]
# in this case
srcpath.dir =${basedir}/src/FlexAGSCI/src

# points to the project's libs directory
libs.dir =${basedir}/src/FlexAGSCI/libs

# this is the folder we want to publish the swf to
deploypath.dir = ${basedir}/build

wrapper.dir=${basedir}/src/FlexAGSCI/html-template

version.major =0
version.minor=9
version.revision = 0
APP_TITLE = Flex Solution
APP_WIDTH = 100%
APP_HEIGHT =100%
locale = en_US
html.file=${deploypath.dir}/index.html
application.name=FlexAGSCI

#Fluint vars
fluint.dir = ${basedir}/tools/FluintAIRTestRunner
report.dir = ${deploypath.dir}/reports

Now, running ‘ant’ at the project space root builds the project, and running ant test builds the test module and runs the test.  We copy this file to local.properties.template, and we are ready to import this project into source control.

I found a couple of posts that show how to import an existing directory into Subversion and make that directory your working copy, which is snazzy. It’s all here.  If you know anything about svn, that is a pretty straightforward post.

That’s that.  We are now ready for Hudson.  in the next post, we’ll get Hudson up, running, building our stuff, and running our test.


Continuous Integration with Flex, Hudson, and ArcGIS Server, Part 3

(Part 1Part 2Part 4)

One of the tenets of continuous integration is automated testing.  The most basic form of testing, in this context, is unit testing, where each class or method is tested without running the other bits of the application.  Unit testing is focused and, hopefully, simple.  In this post, I am going to add unit testing to our application and get ant to run the tests.  There are several unit testing frameworks out there, and we’re going to use Fluint. Fluint and FlexUnit have kinda merged into FlexUnit4, which is in alpha.  FlexUnit4 is, howyousay, super-fantastic, but it doesn’t currently offer any ant tasks.  When it does, I’ll be moving to it post haste.

Go get Fluint from here.  Put the fluint-1.1.1.swc file in the libs folder of your project.  We are now ready to start with some simple Test Driven Development.  Let’s add a button that zooms the map one level in, shall we?  As all examples, it’s contrived, but the point is to focus on the process.   The process here will follow simple TDD: write a test, watch it fail, write the code to make it pass.  In order to be able to watch the test fail, we’re need a bit of a foundation.  For the sake of this article, I have made a “tests” folder in the src directory where I will store the test bits.  Also, we will need a test runner for when we want to manually run our test suite.  That can be downloaded from here and copied to the src directory as well.  Once the runner (FlexTestRunner.mxml) is in the src dir, be sure to go into the project properties and add it to the list of Flex Applications for the project.  if you’ve done all that properly, then you will be able to run the FlexTestRunner application, which runs the Fluint test suites, by default.

image

We don’t want to run the framework tests, so remove all mentions of FrameworkSuite from the code in FlexTestRunner.mxml.  In Fluint, a test runner runs test suites and test suites are composed of test cases.  We will make our own test suite, called ZoomButtonTestSuite, and our own test case called (you guessed it) ZoomButtonTestCase.  These live in the aforementioned “tests” dir.

The test suite is simple:

package tests
{
    import net.digitalprimates.fluint.tests.TestSuite;
    public class ZoomButtonTestSuite extends TestSuite
    {
        public function ZoomButtonTestSuite()
        {
            addTestCase(new ZoomButtonTestCase());
        }

    }
}

All we do is pull in our test case, which looks like:

package tests
{
    import net.digitalprimates.fluint.tests.TestCase;

    public class ZoomButtonTestCase extends TestCase
    {
        public function ZoomButtonTestCase()
        {
            super();
        }
    }
}

Before we go on, go back to FlexTestRunner.mxml and add ZoomButtonTestSuite to the suiteArray in startTestProcess, so it looks like:

protected function startTestProcess( event:Event ) : void
{
    var suiteArray:Array = new Array();
    suiteArray.push( new ZoomButtonTestSuite() );

    testRunner.startTests( suiteArray );
}

We should be ready to right our first test.  For the ZoomButton, here are the specs:

  • When the user clicks the button, zoom the map in one level.

Pretty complicated, eh?  Here’s the test:

public function testZoomIn():void{
    var button:ZoomButton = new ZoomButton();
    var beforeLevel:uint= button.map.level;
    button.doZoom();

    assertTrue(button.map.level < beforeLevel);
}

I have started with the button API design, which is the real aim of our test.  Just from this test, you can see that:

  • The button is it’s own class.
  • The button has a property called map.
  • The button has a method called doZoom() (it has to be public for this example…not sure i like that)
  • The doZoom() method changes the map level.

This won’t even compile, so let’s get that happening.

package widgits
{
    import com.esri.ags.Map;
    import mx.controls.Button;

    public class ZoomButton extends Button
    {
        public var map:Map;
        public function ZoomButton()
        {
            super();
        }
        public function doZoom():void{

        }
    }
}

OK.  The ZoomButton extends the Button class, has a map property (setter/getter not used for brevity) and a doZoom() method.  Launch the test runner, and you’ll see:

image

About what we’d expect.  The button.map property is null.  However, I don’t want to create a full blown ESRI map for my tests.  It will be clunky and slow and difficult.  We need to fake the map.  This is where Mocks come in.

Mocking What We Don’t (need to) Understand

Just like unit testing frameworks, there are quite a few mocking frameworks in Flex land.  For this article, I am going to use MockAS3. (You have to build from source.  Or you can leave a comment with an e-mail and I’ll send you my copy).  Mock AS3 is nice, as it lets us mock a class and not just an interface.  ESRI does not implement an interface with its controls (like Map) for reasons that I don’t know, but I wish they would.  Also, this unit test is an interaction-based test, meaning we are going to set expectation of how we expect the ZoomButton and the map to interact, then verify those expectations as part of the test.  This is what mocking is, testing interactions and expectations presuming all dependent objects do what they are supposed to do.

So, what do we expect the ZoomButton to do in the doZoom() method?  We expect it to call map.zoomIn(), right?  Our first, naive test was testing properties on the map.  While this is testing a valid post-condition, it bleeds too far into testing the ESRI Map control and not the ZoomButton.  After all, the ZoomButton does not set the map level, it just calls a method on the map. If it does that, we are happy with our ZoomButton.  Any issues outside of it are not related to the ZoomButton and its purpose.  With this in mind, our new test looks like:

public function testZoomIn():void{
        var button:ZoomButton = new ZoomButton();
        var mapMock:MapMock = new MapMock();
        mapMock.mock.method('zoomIn').once;

        button.doZoom();

        mapMock.mock.verify();
}

The new test now creates a mock object for the ZoomButton.map property and tells it to expect the “zoomIn” method to be called one time.  We then test the doZoom method, followed by asking the mock to verify that our expectation was met.  Here is the mock:

package tests.mocks
{
    import com.anywebcam.mock.Mock;
    import com.esri.ags.Map;
    public class MapMock extends Map
    {
        public var mock:Mock;
        override public function MapMock()
        {
            mock = new Mock(this);
        }

        override public function zoomIn():void{
            mock.zoomIn();
        }

    }
}

This follows the pretty-good documentation on the Mock AS3 Google Code site for mocking classes.  We only need to mock the methods on which we are setting expectations.  Build and launch the test runner again.

image

Test still fails, but we get a new error:  Verifying Mock Failed. We haven’t coded our doZoom() method yet.

public function doZoom():void{
    if (map)
        map.zoomIn();
    else
        throw new Error("Map is not set");
}

Now, running the tests gives us sweet green….

image

Test Ant, Test Ant…

Now, we need to get these tests running with ant.  Luckily, Fluint ships with ant tasks (which are not available for FlexUnit4 at the time of this writing, but they assure me they are coming)  However, the ant tasks will not use the FlexTestRunner.mxml.  They use a different test runner with is Adobe AIR based, called (you guessed it) AirTestRunner, which you can download from here.  Double clicking on the .air files will launch the AIR installer.  Just follow all the prompts and it will install the FlexAirTestRunner to C:\Program Files\FluintAIRTestRunner.  In that directory you’ll see a .EXE file as well as a .SWF file, which is the actual test runner.  Another caveat to running the Fluint tests with ant is that the AIR test runner requires the use of Flex Modules.  What this means is that you have to create one or more Flex Modules for your tests, depending on how you want to structure the tests.  Basically, each module acts like a container for TestSuites that you want to run, then add the module to the ant task.  Enough talk.   A sample test module can be downloaded from the Fluint Google Code site, here or can be easily written.  A Fluint test module implements the ITestModule interface, which has a single method: getTestSuites().  Here is ours:

<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" implements="net.digitalprimates.fluint.modules.ITestSuiteModule">
    <mx:Script>
        <![CDATA[

            public function getTestSuites() : Array
            {
                 var suiteArray : Array = new Array();
                suiteArray.push( new ZoomButtonTestSuite());
                return suiteArray;
            }
        ]]>
    </mx:Script>
</mx:Module>

Looks a lot like the FlexTestRunner.mxml, huh?  The only thing we have left to do now is add a test target to our ant script.  As some of you might have guessed, we need to add the Fluint ant tasks to our build.xml file.  The easiest (read: not necessarily RIGHT) way to do this is copy them into the c:\ant\lib directory.  You can get the JAR file with the ant tasks here.   Once they are in the ant lib directory, add the following line to your build.xml (right under the flexTasks is a good spot)

<taskdef name='fluint' classname='net.digitalprimates.ant.tasks.fluint.Fluint'  />

There, now we can use our Fluint tasks in the ant file.  Since the tests are using Flex modules, we’ll want to have a couple of targets in the build script: one that builds the module, and one that executes the test.  Heres’ the target that builds the module:

<target name="build-test-module">
     <echo>Building test module</echo>
    <mxmlc file='${srcpath.dir}/tests/ZoomButtonTestModule.mxml'  target-player="10.0.0"
        output='${deploypath.dir}/tests/ZoomButtonTestModule.swf' >
         <load-config filename='${FLEX_HOME}/frameworks/flex-config.xml' />
         <source-path path-element='${FLEX_HOME}/frameworks'/>
         <!-- source paths -->
         <compiler.source-path path-element='${srcpath.dir}'/>
        <!-- add external libraries -->
        <include-libraries file='${libs.dir}' />
    </mxmlc>
</target>

If you go to the command line and type ‘ant build-test—module’, it will build the module and copy it to our bin-debug directory. Now, we just need to tell the Fluint task to go find that module and run the test.  Here’s the target:

<target name='test'>
    <fluint
       debug='true'
       headless='true'
       failOnError='true'
       workingDir='${fluint.dir}'
       testRunner='${fluint.dir}/FluintAIRTestRunner.exe'
       outputDir='${report.dir}'>

       <fileset dir='${deploypath.dir}/tests'>
          <include name='**/*TestModule.swf'/>
       </fileset>
    </fluint>
</target>

You may have noticed the two new build variables: fluint.dir and report.dir.  The Fluint task needs to know where the FluintAirTestRunner.exe file resides, which is the former.  The latter tells fluint where to write the report of how the tests faired.   I added this to the bottom of the build.properties file:

#Fluint vars
fluint.dir = C:/Program Files (x86)/FluintAIRTestRunner
report.dir = ${basedir}/reports

Now, running ‘ant test’ at the command line will run our test and generate the test report.  It’s an XML file that looks like:

<testsuites status="true" failureCount="0" errorCount="0" testCount="1">
  <testsuite name="tests.ZoomButtonTestCase" errors="0" failures="0" tests="1" time="0.237">
    <properties/>
    <testcase name="testZoomIn" time="0.237" className="tests.ZoomButtonTestCase"/>
  </testsuite>
</testsuites>

So, our ant file is now running our tests.  We are well on our way to continuous integration with Flex.  Coming up, we’ll need to get some of the hardcoding out of the build file, look at our project directory structure, format our test reports to be more readable, and bring Hudson into the mix.  We have a lot to do.

I hope you find this useful.


Continuous Integration with Flex, Hudson, and ArcGIS Server, Part 2

(Part 1Part 3Part 4)

In the last post, I talked about the very basics of getting a Flex project going with Flex Builder and ant.  The current ant script we have just builds the SWF file, which leaves out a bunch of files that Flex Builder uses, and that is bad as defined by what I am trying to blog about here.  So, let’s get going on pulling over the rest of the files.  Well, at least, the important ones.  The most important one is likely the HTML file that wraps our SWF.  If we are going to deploy this SWF in any meaningful (read: web) manner, we need that HTML file.  Actually, we need one that we make ourselves and pull into our build process, but that will left as an assignment for the reader.  For the sake of this series, we’ll just reuse the HTML file that Flex Builder was kind enough to generate.

In the default Flex Builder project, one of the folders is html-template, that looks like:

image

Not much to it, really.  I am not gonna go into the meat of what the files are, if you want that go here.  In a nutshell, what we need to do here is copy this whole directory to our output folder (our ${deploy.dir} for you sharp ones following along).  There is a slight caveat here, tho, and that’s the contents of the index.template.html.  If you open that file in a text editor, you’ll see snippets like:

AC_FL_RunContent(
        "src", "playerProductInstall",
        "FlashVars", "MMredirectURL="+MMredirectURL+'&MMplayerType='+MMPlayerType+'&MMdoctitle='+MMdoctitle+"",
        "width", "${width}",
        "height", "${height}",
        "align", "middle",
        "id", "${application}",
        "quality", "high",
        "bgcolor", "${bgcolor}",
        "name", "${application}",
        "allowScriptAccess","sameDomain",
        "type", "application/x-shockwave-flash",
        "pluginspage", "http://www.adobe.com/go/getflashplayer"
    );

where you may notice the now familiar ${} variables.  So, now our simple copy has turned into a copy and replace all the ${} tokens with their appropriate values.  Nothing is ever as simple as it should be.

The good news is that it’s not so hard either.  Breaking down what we need to do, I think it looks something like this:

  1. Copy the html-template contents.
  2. Parse the index.template.html and replace the tokens.

Let’s create our ant task to get this done.  I’ll it create-html-wrapper, and it looks like this:

<target name='create-html-wrapper'>
    <copy todir='${deploypath.dir}' overwrite="true">
      <fileset dir='${wrapper.dir}'>
         <exclude name='**/index.template.html' />
     <exclude name='**/index.template.html.svntmp' />
         <exclude name='.svn' />
      </fileset>
</copy>

The copy ant task is a standard task from the ant library.  We are excluding some items here:  we don’t want any source control artifacts to end up in the deploy directory.  I am using subversion here, so I explicitly exclude those.  The one that may look odd is the exclusion of index.template.html.  The reason that file is excluded is because we want to rename it.  I don’t want my HTML file to be named index.template.html, do you?

(NOTE:  The Flex ant tasks include an html-wrapper task, but I found it lacking for reasons that now escape me.  I think the parsing of the HTML files was either not there or crappy.  Either way, I dumped it for some very valid reason, I am sure.)

For the index.template.html, we can use another cool ant ask that uses regular expressions to replace content within a file.  The name of that task is replaceregexp, and it’s dead sexy.  Here is the copy command, and it’s a bit of a doozy.  I am not gonna go through each token, as that would be REALLY boring (not super exciting like the rest of this post)

<!-- Copy and rename the index.template.html -->
<copy file='${wrapper.dir}/index.template.html'
tofile='${html.file}' />
<!-- Copy and rename the index.template.html --> <copy file='${wrapper.dir}/index.template.html' tofile='${html.file}' /> <replaceregexp file='${html.file}' flags='gs' match='\$\{width\}' replace='100%'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{height\}' replace='100%'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{title\}' replace='${APP_TITLE}' encoding='utf-8'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{version_major\}' replace='${version.major}'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{version_minor\}' replace='${version.minor}'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{version_revision\}' replace='${version.revision}'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{application\}' replace='${APP_TITLE}'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{bgcolor\}' replace='#FFFFFF'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{swf\}' replace='${application.name}'/>
(BTW, the replaceregexp stuff I blatantly stole from here, which is a GREAT post on using ant with Flex.  Also goes to show you that I am a couple of years behind the cool kids.)

Finally, let’s take another look at our build.properties file, which added quite a bit of properties for this task.

#Copy this file locally to build.properties and change below for your env

# change this to your Flex SDK directory path
FLEX_HOME=C:/Program Files (x86)/Adobe/Flex Builder 3/sdks/3.2.0

# this points to your project's src directory
# {$basedir} is a default variable that can be used in any Antscript
# and it points to the project's root folder [ flex_ant_pt1_Tasks ]
# in this case
srcpath.dir =${basedir}/src

# points to the project's libs directory
libs.dir =${basedir}/libs

# this is the folder we want to publish the swf to
deploypath.dir = ${basedir}/bin-debug

wrapper.dir=${basedir}/html-template

version.major =0
version.minor=9
version.revision = 0
APP_TITLE = Flex Solution
APP_WIDTH = 100%
APP_HEIGHT =100%
locale = en_US
html.file=${deploypath.dir}/index.html
application.name=FlexAGSCI

We have some of our HTML tokens, like width and height, a new directory (wrapper.dir) and the name of our HTML file.  For the ant build, I rename it to index.html because I plan on using ant to actually deploy the application to a web server and I want a default document name to be used.

So, next post will concentrate on adding a unit testing framework and getting ant to run our tests too.  I know the first couple of posts were mongo-basic, but I wanted to lay a bit of a foundation before getting into the cooler stuff.  We’ll get there.  I am about to head off on a mini-vacation, so it might be a week or more before I get to it.  I hope you can manage until then….

Oh, and here’s our build.xml, in full, just for fun:

<project name='Flex Ant Tasks Build Script' default='build-flex-project'>
    <!-- load properties file -->
    <property file='build.properties'/>

    <!-- points to are flexTasks.jar -->
    <taskdef resource='flexTasks.tasks' />    

    <!-- delete and redeploy -->
    <target name='init'>
        <delete includeemptydirs='true'>
            <fileset dir='bin-debug' includes='**\*' excludes='**\.svn'/>
        </delete>

    </target>
    <target name='build-flex-project' depends='init,create-html-wrapper'>
        <mxmlc file='${srcpath.dir}/${application.name}.mxml' output='${deploypath.dir}/${application.name}.swf'>
            <load-config filename='${FLEX_HOME}/frameworks/flex-config.xml'/>
            <include-libraries file='${libs.dir}' />
        </mxmlc>

    </target>

    <target name='create-html-wrapper'>
        <copy todir='${deploypath.dir}' overwrite="true">
              <fileset dir='${wrapper.dir}'>
               <exclude name='**/index.template.html' />
               <exclude name='**/index.template.html.svntmp' />
               <exclude name='.svn' />
              </fileset>
        </copy>
          <!-- Copy and rename the index.template.html -->
         <copy file='${wrapper.dir}/index.template.html'
              tofile='${html.file}' />
          <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{width\}'
              replace='100%'/>
           <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{height\}'
              replace='100%'/>
         <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{title\}'
              replace='${APP_TITLE}'
              encoding='utf-8'/>
         <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{version_major\}'
              replace='${version.major}'/>
         <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{version_minor\}'
              replace='${version.minor}'/>
           <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{version_revision\}'
              replace='${version.revision}'/>
         <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{application\}'
              replace='${APP_TITLE}'/>
          <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{bgcolor\}'
              replace='#FFFFFF'/>
         <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{swf\}'
              replace='${application.name}'/> 

    </target>
</project>

Continuous Integration with Flex, Hudson, and ArcGIS Server, Part 1

(Part 2, Part 3, Part 4)

I’ve been meaning to blog about our findings with getting CI going for Flex, since I took time to whinge about it earlier.  It was a bit of a journey, but all in all, not that bad.  Much of the thanks goes to Hudson, which is, how you say?, super-fantastic.  I plan to break this into a few blog posts rather than one big-old doozy.   The parts (I think) will be along the lines of:

  1. Getting your Fles project building with ant and Flex Builder, parts 1 and 2.
  2. Setting up unit testing with Fluint.
  3. Setting up Hudson.

Whachoo Need (OOH)

This post is focused on getting your Flex Builder project building with ant.  As such, I presume you are using Flex Builder, but if you aren’t and just want to build with ant, you should be OK. Oh, and I am doing this on Winders, even though ant is usually the domain of the Unix-based Java wanks (I mean “wanks” as a term of endearment, like “I love those wanks”)

Go get the following and download to your computer:

  • ANT - The original builder.  It can carry, like, 10,000 times its own weight in builds.  Unzip it to c:\ant.
  • Flex Ant Tasks – These come with the Flex SDK, which comes with Flex Builder.  You can find them in C:\Program Files (x86)\Adobe\Flex Builder 3\sdks\3.2.0\ant\lib\flexTasks.jar.  If you don’t have Flex Builder, go download the SDK and put it somewhere.
  • ArcGIS Server Flex API – So we can get our map on, know what I’m saying? (I am using 1.2)

Create the Project

Open Flex Builder and create a new project.  I am presuming that your local workspace is c:\projects\FlexCI, though it really doesn’t matter.

So, we’ll start by creating a new Flex Project in Flex Builder.

Create Flex Project

Hit ‘Next’ in the project wizard and we have our project.  Copy the AGS Flex API swc into your libs folder (see pic).

flexapi

Now we can make a map and stuff.  Add the following code to your app:

<esri:Map>
     <esri:ArcGISTiledMapServiceLayer
          url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"/>
</esri:Map>

Now we have our map.  In Flex Builder, build (Ctrl+B, unless you are using ‘Build Automatically’) the site and run it, you should see a map of our world.  Building with Flex Builder works, which was a requirement for me.  I wanted people to be able to build and debug in the IDE, but also quickly build from the command line with ant.  I like Flexibility, #rimshot.

High Apple Pie in the Sky Hopes

In order to build with ant, we need a couple of items

  • A build file, we will call build.xml
  • A properties file, we will call build.properties

I am not about to go into the gory details of how ant works, but presume you know the basics, which is all I know.  We need to define a build target that compiles our Flex project.  The steps the build file will go through are:

  1. Clean out the build dir.
  2. Build the project.

In ant-speak, that is two targets.  I will call them ‘init’ and ‘build-flex-project’.  For the init target, we just need to delete everything in the directory that Flex Builder set up as the deploy location, which is (by default) the bin-debug directory.  Create a build.xml file in your Flex Project root, has the following XML:

<project name='Flex Ant Tasks Build Script' default=”init”'>
    <!-- delete and redeploy -->
    <target name='init'>
        <delete includeemptydirs='true'>
            <fileset dir='bin-debug' includes='**\*' />
        </delete>
    </target>
</project>

This is a fully-functional ant script now.  Test it by opening a command prompt, switching to the project directory, and typing ‘c:\ant\bin\ant’ (minus the quotes, of course)  You should see something like:

firstant

Also, your bin-debug directory should be empty.  WOOHOO!  If you build with Flex Builder again (you may have to “Clean” since FB won’t build unless it detects a change) the files will go back into the bin-debug dir.  Right, now let’s get the build part going.  Adobe was nice enough to create ant tasks that will build Flex projects.  The easiest way to make them available to your ant script is to:

  1. Copy the flexTasks.jar file into your c:\ant\lib folder.
  2. Reference these tasks in your build.xml file.

The second part is done by adding the following code to the top of the build.xml file:

<!-- points to are flexTasks.jar -->
<taskdef resource='flexTasks.tasks' />

That’ll do it.  (NOTE:  In a real CI scenario, which we’ll get to, we would not copy the jar into the ant\lib folder, but make it part of the project artifacts and reference it locally.  Bear with me, baby steps on the bus…)

Now, let’s add the build-flex-project target, which uses the mxmlc Flex ant task.

<target name='build-flex-project' depends='init'>
     <mxmlc file='${srcpath.dir}/${application.name}.mxml' output='${deploypath.dir}/${application.name}.swf'>
           <load-config filename='${FLEX_HOME}/frameworks/flex-config.xml'/>
           <include-libraries file='${libs.dir}' />
     </mxmlc>
</target>

WHOA!  OK, so I’ve jumped ahead a bit, so lemme ‘splain.  First off, we tell ant that this target depends on the init target, so it will run init before running the build.  Second, those funky ${} items that you see are properties.  So, you can see that I have one for”:

  • srcpath.dir – the source directory, so we knows what to build.
  • FLEX_HOME – this is where our SDK lives
  • deploypath.dir – where to put the build output
  • libs.dir – where are external libraries (like the AGS Flex API) live
  • application.name – What do we want our SWF to be called.

Now we need to put these properties somewhere, and the aforementioned build.properties file is where they belong.  Looks like:

#Copy this file locally to build.properties and change below for your env

# change this to your Flex SDK directory path
FLEX_HOME=C:/Program Files (x86)/Adobe/Flex Builder 3/sdks/3.2.0

# this points to your project's src directory
# {$basedir} is a default variable that can be used in any Antscript
# and it points to the project's root folder [ flex_ant_pt1_Tasks ]
# in this case
srcpath.dir =${basedir}/src

# points to the project's libs directory
libs.dir =${basedir}/libs

# this is the folder we want to publish the swf to
deploypath.dir = ${basedir}/bin-debug

application.name=FlexAGSCI

We need to tell our build.xml file about the build properties file, and we do so by adding this to the top (meaning, first thing under the <project> opening tag):

<!-- load properties file -->
<property file='build.properties'/>

The last thing we need before running the build again is to change the default target to build-flex-project.  You’ll find the default attribute on the root project element.

Now, running ‘c:\ant\bin\ant’ (btw, putting c:\ant\bin in your PATH is advisable, then you could just type ‘ant’) at the command line, gives:

secondant

Look in your bin-debug folder, and you’ll see the SWF file, which should be fully functional.

I think I’ll stop today’s post there.  It has gotten a bit long on me (they always do).  The next post will cover how to get the rest of the files that Flex Builder creates, like the HTML wrapper and Flash-detecting javascript files, incorporated into the build.

Hope this is useful.


Adding Drag/Drop Reorder to ArcGIS Flex Viewer TOC

Wow, that is a long, clumsy title.  Google will like it, though.

Anyway, I’ve been neck-deep in the world of Flex, focusing on the ArcGIS Server Flex API the last couple of months.  We delivered a map viewer based on Flex to a client recently, and it was a great learning experience.  ESRI released a Sample Flex Viewer component that includes the Table of Contents (TOC) sample from the code gallery.  The TOC component was originally built by Tom Hill at ESRI.  He and I have traded a couple of e-mails about adding drag and drop reorder functionality, so I thought I’d run through the basics of what it takes to get it working.

Download the Flex Viewer

If you haven’t already, download the flex viewer from here.  Extract the sample SOURCE (there are two zips, binary and source…we are using the source archive)  to your development area.  I am using FlexBuilder, so my life is easy, as I can just import the existing project into the IDE.  You don’t have to have FlexBuilder, but I am going to presume you know how to edit source files and compile the project.

Modify the LiveMapsWidget

The TOC component is used by the com.esri.solutions.flexviewer.widgets.LiveMapsWidget MXML component.  Open the source for that bad boy (the namespace==the directory).   Find the TOC component, which looks like

<toccomp:TOC id="toc" width="100%" height="100%"/>

We want to enable dragging on the component.  The TOC component extends the core Flex Tree component, so enabled drag drop is as simple as adding

dragEnabled=true

to the element.  If you do that and the run the viewer, you’ll notice you can open the Live Maps widget (in the Globe menu)  and drag the “Lousiana Landbase” layer around.  It doesn’t do much, but you can drag it.  In fact, you get a rex “X” while you drag, which means you can’t drop it anywhere.  So, how do we get rid of that pesky red “X”?   We have to tell the DragManager that the TOC will accept a drag/drop.  The DragManager is a Flex class that maanged drag and drop operations.  Google it for more info (sorry, but I have to constrain the post ot it will go War and Peace on me)   The time to tell the DragManager about the openness of the TOC is when something is dragged (?  drug?) over it.  We do this with the dragEnter event.  So, adddragEnter

dragEnter="onDragEnter(event)"

to the toc element.  Now we have to write the onDragEnter  method.  In the mx:Script area of LiveMapsWidget.mxml, copy this code:

			private function onDragEnter(event:DragEvent):void{
				DragManager.acceptDragDrop(event.currentTarget as TOC);
				if ((event.currentTarget as TOC)==null)
				{
					DragManager.showFeedback(DragManager.NONE);
					event.preventDefault();
				}

			}

This function fires when we drag something over the TOC and tells it that we are open for business. Business is, of course, things that can be dragged and dropped. You could put more logic in here to filter out non TOCItems, etc, but that is left as a lesson for the reader.

OK,  so our red “X” is gone over the TOC (but still there if you drag a layer over the map, cool!)  but now we want it to do something when we drop the layer.  Specifically, we want it to reorder the layers.  But we only have 1 layer, so let’s quickly add another.  Open up the config.xml in the src directory and add the following tag to the <livemaps> element:

<mapservice label="Louisville Public Records" type="dynamic" visible="false" alpha="0.4">http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Louisville/LOJIC_PublicSafety_Louisville/MapServer</mapservice>

Now we have two layers, making a drag/drop scenario much more compelling.  If you run the viewer you will see both our layers in the LiveMapsWidget.   Now, just like we had to tell Flex to enabled dragging on the TOC, we have to tell it to enabled dropping.  You guessed it, add

dropEnabled="true"

to the toc element.  Now, when you drag/drop a layer, they will reorder in the TOC, which is nice.  The map layers don’t do anything, but we’ll get there.  We have a problem now, though.  The Tree component makes no distinctions between services (roots, in this case) and the layers that make up those services.  This results in the ability of the user to be able to drop a map service under another map service, which is bad.  You can do a lot of checking when the drop occurs to make sure that the user hasn’t dropped one service as a child of another.  I am gonna go an easier route and collapse all the roots (map services) on drag start so those crafty users can’t do it.  They may not like it, but life is sometimes hard.  So, let’s add a dragStart function:

(on the toc)

dragStart = "onDragStart(event)"

(the function)

			private function onDragStart(event:DragEvent):void{
				//Close the dragged item
				_draggedLayer=toc.selectedItem  as TocMapLayerItem;
				var openItems:Array = toc.openItems as Array;
				for each(var o:Object in openItems){
					toc.expandItem(o,false);
				}
				//setInfoText(resourceManager.getString("resource","toc-layer-reorder"));
			}

K…we’re getting there.  Now to the meat of what needs to happen.  When we drop the service, we need to calculate it’s new index in the map, then tell the map to reorder the services.  It took me awhile to find an algorithm that worked for this, and here is why:

  • The basemaps.  The TOC doesn’t have the basemaps, so you have to take that into account when calculating the new index.
  • If the layer is dragged down, then you have to account for that.
  • The TOC indices are reversed form the map services.  So index 0 in the map is the lowest, but it’s the topmost node in the TOC.

So, nothing overwhelming, but details.    Let’s add the event to the TOC element:

dragComplete = "onDragComplete(event)"

and the code:


private function onDragComplete(event:DragEvent):void{
 if (event.action!="move")
 return;
 var _roots:ArrayCollection = toc.dataProvider as ArrayCollection;
 //Unclear why I have to do this....but I need the selectedIndex later
 toc.selectedItem = _draggedLayer;
 var dropIndex:uint=toc.calculateDropIndex(event);
 // I've seen thie calculated drop index be > than the number of 
 // services.  This usually happens when a service node is expanded,
 // but let's just make sure.  We'll put it at the bottom of the list 
 // in this cse.
 if (dropIndex>_roots.length)
 dropIndex=_roots.length;

 var ind:int=0;
 // Set in onDragStart....if it's null, get outta dogdge
 if (_draggedLayer==null)
 return;
 var delta:int = _roots.length-dropIndex;

 ind = delta + 1;//We have two base layers....HACK...THIS IS BAD, MAKE IT BETTER

 // If the selected item is dragged down, then the index needs to account for that
 if (toc.selectedIndex>dropIndex)
 ind=ind-1;

 toc.map.reorderLayer(_draggedLayer.layer.id,ind);

 }

The comments in that function go through what I am trying to do.  I’ll be the first to admit that it isn’t the prettiest code at the ball, but it’s the one I brought so I am dancing with it (Note to self:  work on better analogies)

So, you’d expect it to work now, woudln’t you?  Well, it doesn’t.  We have to make a minor change to the TOC to handle the layer reorder.   In the TOC.as (in src\com\esri\solutions\flexviewer\components\toc) the onLayerReorder function looks like:


private function onLayerReorder( event:MapEvent ):void
 {
 var layer:Layer = event.layer;
 var index:int = event.index;

 for (var i:int = 0; i < _tocRoots.length; i++) {
 var item:Object = _tocRoots[i];
 if (item is TocMapLayerItem && TocMapLayerItem(item).layer === layer) {
 _tocRoots.removeItemAt(i);
 _tocRoots.addItemAt(item, _tocRoots.length - index - 1);
 break;
 }
 }
 }

Modify the TOC Code (Just a little…)

When you run the viewer now, you’ll get RangeErrors on the addItemAt line above.   So, my approach was to calculate the new TOC index by figuring out the difference between the number of layers and the new index.  Then, make sure somethign didn’t go haywire and we are out of range.  See below:

private function onLayerReorder( event:MapEvent ):void
 {
 var layer:Layer = event.layer;
 var index:int = event.index;
 //How far did we move?
 var addbackind:int=(map.layerIds.length-1) - event.index;
 for (var i:int = 0; i < _tocRoots.length; i++) {
 var item:Object = _tocRoots[i];
 if (item is TocMapLayerItem && TocMapLayerItem(item).layer === layer) {
 _tocRoots.removeItemAt(i);

 // If we are out of range on the high end, rein it in
 if (addbackind>_tocRoots.length)
 addbackind=_tocRoots.length-1;
 // If we are out of range on the low end, rein it in
 if (addbackind<0)
 addbackind=0;
 _tocRoots.addItemAt(item,addbackind);
 break;
 }
 }
 }

May not be the prettiest…etc, etc.  But it works.  You should now have drag/drop reoder working like a champ.  Obviously, this code could still be improved.  The biggest example is accounting for the basemaps in a cleaner fashion.  I will tell you that we used the Specification Pattern to determine if a service was a basemap, allowing the TOC to ask the specification.  I liked that, but didn’t include it here to try and keep this post focused.

Anyway, try it out and see how it goes.  If you have improvments or suggestions, hit me in the comments.  Here is a link to the final LiveMapWidgets.mxml I used for this post.


Creating a REST Service with WCF and Windsor

Following on from my previous post, here is a short post showing how quickly you can create a REST based service using the WCF Facility for Castle Windsor.   We will use the same service contract and implementation as the last post, with some very minor changes (only one, actually).    The list of steps to expose our service from the previous post with a REST endpoint consists of:

  1. Create a new .svc file for the second endpoint.
  2. Add an attribute to the operation contract to specify what HTTP verbs are allowed.

That’s it.  Please note that we are not replacing our SOAP endpoint from before, nor are we changing the configuration of the WCF Facility, nor are we touching the web.config.  The changes below will highlight how little we need to do.  Here is the new service endpoint (.svc file):

<%@ ServiceHost Service="my service"
Factory="Castle.Facilities.WcfIntegration.WindsorServiceHostFactory`1[[Castle.Facilities.WcfIntegration.Rest.RestServiceModel,
Castle.Facilities.WcfIntegration]], Castle.Facilities.WcfIntegration" %>

All we have done here is replace our factory with the REST aware service model from the WCF Facility.  We are using the Service attribute to point to the same component as the SOAP endpoing.

And the new service contract:

[ServiceContract]

public interface IMyService

{

[OperationContract]

[WebGet]

string MyOperation1(string myValue);

}

We’ve added the System.ServiceModel.Web attribute [WebGet] to allow HTTP GET to this endpoint.  Now, our operation is availalbe as a REST endpoint, using a URL like so:

http://server/site/rest.svc/MyOperation1?myValue=I%20am%20RESTful

which returns

<string>A WCF Facility Service says I am RESTful</string>

It’s likely that you wouldn’t want applicaiton/xml to be the default return content-type, but the point of this post is not to show a best practice with REST, but to show how bleeding easy it is to expose a REST endpoint.  We are also now exposing TWO endpoints (SOAP and REST) with a singular service implementation, a detail worthy of note.  In the real world, you would likely have a service manager that registered service implementations and formatters, allowing the clients to specify a return type (XML, JSON, etc) and amending the content-type accordingly.

This, however, is my blog and could not be farther from the real world. ;)


Follow

Get every new post delivered to your Inbox.