The amazing adventures of Doug Hughes

Archive for October, 2009

How To Deploy From SVN to Staging and Production Using Ant4CF

I received the following question in email this morning and I thought it would create a pretty good blog entry:

Hi Doug,

Thanks for the meetup presentation for Ant. I’m getting into SVN and ANT at the moment and was looking for some advice on my setup.

I have a dev server with an SVN repo, a staging server and a live server. What is the best way to upload changes from dev to staging. It would be individual files and folders rather than the whole application. Do you think FTP from the repo to staging, if thats possible?

Many Thanks

This is a variation on a fairly common question I get about how we deploy applications at Alagad. It seems that a lot of people have applications that are made up of lots of files. I get the impression that they’re used to working in an environment where, when deploying their application, they only deploy the files that have been modified.

I vaguely remember this approach from before I fully embraced source code control, but honestly it’s such a foreign concept to me that I almost want to simply ignore it.

I assure you that at Alagad we have worked on some very large applications made up of thousands of files. And yes, it can take time to check the application out of Subversion into staging or production. However, usually those three servers are on the same network, or at least are on networks that have significant connections to the Internet. This generally means it will only take a few minutes to get the files for the deployment out of subversion.

The other point is that I’d rather feel really confident that my entire application is deployed correctly. Deploying only a few files would make me nervous that something subtle was missed. By doing a blanket update of everything I know I have the latest of everything.

So, a simple Ant4CF build script to do what I suggest would look like these two files.

First, this is the local build.xml file. This file has two targets, BuildStaging (the default) and BuildProduction. For the same of simplicity I’ve assumed that these are two different servers that are setup exactly the same. This means I can use the same build.remote.xml file for both servers.

In the real world there’s a good chance that you will have some configuration differences between staging and production and my either execute different targets in the build.remote.xml file or would have two completely separate build scripts.

build.xml

<project default="Build" name="Build Script">

    <target name="Build">
        <!-- if we don't specify a target when we run this script the default target will ask us which one we want. The default will be BuildStaging -->

        <input addproperty="target" defaultvalue="BuildStaging" message="Which target do you wish to run?" validargs="BuildStaging,BuildProduction"/>
        <!-- call the target chosen -->

        <antcall target="${target}"/>
    </target>

    <target name="BuildStaging">
        <antcall target="RunBuild">
            <!-- this property sets which server the build script will be run on (staging) -->

            <param name="ant4cfUrl" value="http://staging.yoursite.com/CFIDE/ant4cf"/>
        </antcall>
    </target>

    <target name="BuildProduction">
        <antcall target="RunBuild">
            <!-- this property sets which server the build script will be run on (production) -->

            <param name="ant4cfUrl" value="http://production.yoursite.com/CFIDE/ant4cf"/>
        </antcall>
    </target>

    <target name="RunBuild">

        <!-- if the ant4cfUrl property is not set, then fail this script -->

        <fail message="Please run the BiildProduction or BuildStaging target." unless="ant4cfUrl"/>
        <!-- output which url we're using to build -->

        <echo message="Building via URL: ${ant4cfUrl}"/>

        <!-- Gather usernames and passwords for the cfadmin and svn -->

        <input addproperty="cfAdminPassword" message="Please enter CFAdmin password:"/>
        <input addproperty="svnUsername" message="Please enter SVN username:"/>
        <input addproperty="svnPassword" message="Please enter SVN password:"/>

        <!-- define the tasks for ant4cf -->

        <taskdef classname="com.alagad.ant4cf.Configure" classpath="antlib/ant4cf-java-2.0-SNAPSHOT-jar-with-dependencies.jar" name="configure"/>
        <taskdef classname="com.alagad.ant4cf.RemoteAnt" classpath="antlib/ant4cf-java-2.0-SNAPSHOT-jar-with-dependencies.jar" name="remoteant"/>

        <!-- configure ant4cf to deploy to your staging server -->

        <configure adminPassword="${cfAdminPassword}" ant4cfUrl="${ant4cfUrl}"/>
        <!-- run the remote ant script on staging -->

        <remoteant antfile="build.remote.xml" properties="cfAdminPassword,svnUsername,svnPassword,dsnPassword" timeout="240"/>

    </target>
</project>

remote.build.xml

<project default="BuildRemote" name="Remote Build Script">

    <target name="BuildRemote">

        <!-- get the app from subversion -->

        <echo message="Export App out of SVN"/>
        <exec executable="svn" failifexecutionfails="true">
            <arg line="export http://svn.cfu09ant.com:81/svn/Presentations/Trunk/cfunited09/DeployingAppsWithAnt/exampleApp/c:pathtoyoursite --username ${svnUsername} --password ${svnPassword} --force"/>
        </exec>

	</target>
</project>

This example is somewhat different from a lot of the other examples I’ve shown mainly because the build script lets you choose which site to build, staging or production. This means you can run the script by simply running the command “ant”, or you can manually choose a target with “ant BuildStaging” or “ant BuildProduction”.

The remote.build.xml file is, for the sake of this example, extremely simple. All it does is check out the entire application from SVN and dump it into the webroot. We’re not using any Ant4CF features remotely so I don’t even define those tasks. In a typical script I like to insure that the application and server is configured correctly, but that wasn’t the question.

So, that’s how I’d deploy to staging or production. But, what if you really only want to deploy files that have changed in subversion? That’s a more of a challenge. And honestly, not one I’ve taken on before.

I spent much of the day today trying to write a service in CF that would export only the files which had changed between specified revision numbers in SVN. After about 150 lines of code I decided to throw in the towel on this.

My goal had been to create a new service that would take the SVN url, revision range, and other related information. Then, using the “svn log” command get a lits of the files that had changed. I could then loop over the files and export each one individually.

There were a few problems in this plan though. First off, the log output shows the output for each revision and therefore the same file could be in there more than once. The other problem was that it’s not as easy to tell if something in Subversion is a file or a directory. In other words if I may know that this path “/trunk/www/admin/capabilities/views/shared” was modified in my last commit. However, I don’t know if “shared” is a file without an extension or a directory.

This mean that I have to perform a fair amount of work to manage the data from the log to not only remove duplicates, but avoid exporting a directory followed by exporting each file in that directory. Overall, it’s a mess I’d rather not get into.

So, my recommendation is to simply use “svn export” and dump everything. It’s faster and easier and more accurate.

Deploying ColdFusion Applications with Ant Recording and Materials

I’ve promised multiple times (CFUnited, CFinNC, and now CF Meetup) to post the materials for my Deploying ColdFusion Applications with Ant presentation. I’ve finally done that and they can be downloaded here.

Also, those who want to, can see the recorded presentation here.

Those who are interested can get started with Ant4CF by downloading it from GitHub. The documentation is pretty good, so hopefully that’ll get you started.

Deploying ColdFusion Applications with Ant on CFMeetup Tomorrow

I’ll be giving my presentation on Apache Ant and how it can be used to deploy ColdFusion applications tomorrow, Thursday, November 29th at 12:00 ET.

This presentation is the same one I gave at CFUnited 09 and CFinNC. In it I give a brief tour of Ant and then show how it can be used to improve the deployment process of a typical ColdFusion application. I’ll also show and discuss an example using my Ant4CF project.

Frankly, I think this may be one of my better presentations. It has gone over fairly well (despite technical problems at CFUnited) and I feel really good about the topic.

Be there or be square!

For more information visit this link: http://www.meetup.com/coldfusionmeetup/calendar/11696775/

Styling Item Renderers in the DataGrid

I’ve been blogging about different styling issues within the Flex framework. I’d like to take a second to touch on a fairly common issue when trying to style item renderers.

Item renderers are extremely helpful with memory management. As a user scrolls through a list the the flash player only has to instantiate a handful of item renderers that are showing (and a few buffer instances above and below). This recycling is a mixed blessing. As a developer, we have to keep in mind that the item renderer isn’t really being created each round and if we style a certain item renderer based on data we need to remember to double check it each time the data changes. You’ll often see issues arise with icons or style changes within a item renderers when they are being recycled (scrolling). In the following example I’ve got a set of data and the negative numbers are suppose to be styled red.

Item Renderer Code:

package {
    import mx.containers.Canvas;
    import mx.controls.Label;
    import mx.controls.listClasses.BaseListData;
    import mx.controls.listClasses.IDropInListItemRenderer;
    import mx.core.ScrollPolicy;
    public class SampleItemRenderer extends Canvas implements IDropInListItemRenderer {
        protected
        var redLabel: Label = new Label();
        protected
        var negative: Boolean;
        protected
        var regularLabel: Label = new Label();
        protected
        var _listData: BaseListData;

        public
        function SampleItemRenderer() {}

        /**
         *  override public set data.  If it's differnt check if it's
         * less than zero and set view accordingly.
         */
        override public
        function set data(value: Object): void {
            if (value != super.data) {
                super.data = value;
            }
        }

        override protected
        function createChildren(): void {
            super.createChildren();
            this.addChild(regularLabel);
            this.addChild(redLabel);
            redLabel.x = 10;
            regularLabel.x = 10;
            setStyles();
        }

        protected
        function setStyles(): void {
            /set light red color
  redLabel.setStyle('color', 0xFF8888);
  this.verticalScrollPolicy = ScrollPolicy.OFF;
  this.horizontalScrollPolicy = ScrollPolicy.OFF;
  }

  /----------------------------------/  listData
  /----------------------------------

            /**
             *  Implements the <code>listData</code> property
             *  using setter and getter methods.
             */
            public
            function get listData(): BaseListData {
                return _listData
            }

            /**
             *  @private
             */
            public
            function set listData(value: BaseListData): void {
                if (value != _listData) {
                    _listData = value;
                    redLabel.text = _listData.label;
                    regularLabel.text = _listData.label;
                    if (Number(regularLabel.text) < 0) {
                        negative = true;
                    }
                    redLabel.visible = negative;
                    regularLabel.visible = !negative;
                }
            }

        }
    }

Two things are interesting to point out here. The first is a little counter intuitive, but it’s actually more efficient to have two labels and toggle the visibility (and the includeInLayout property if needed). Calling setStyle is fairly resource heavy. (This is also a good trick if changing icon images)

The next gotcha is to always remember to reset the style correctly. In our case if the data is a number that is NOT less then 0 we still need to make changes to this item renderer. This will cover the case when a recycled red renderer needs to be changed back to white.

Here’s the CSS I used for the above example:

DataGrid {
    border-skin: Embed(source="assets /DataGrid_borderSkin.png",
    scaleGridLeft="8", scaleGridTop="8", scaleGridRight="95", scaleGridBottom="95");
    color: #fff;
    column-drop-indicator-skin: Embed(source="assets /DataGrid_columnDropIndicatorSkin.png");
    column-resize-skin: Embed(source="assets /DataGrid_columnResizeSkin.png");
    header-colors: #666, #000;
    roll-over-color: #555;
    selection-color: #777;
    stretch-cursor: Embed(source="assets /DataGrid_stretchCursor.png");
}
mx:DataGrid id="dataGrid" dataProvider=" {
    sampleData;
}
" x="25" y="25" backgroundAlpha="0" headerHeight="25";
dropEnabled="true" borderStyle="solid" itemRenderer="SampleItemRenderer";
height="200" filters=" {
    [new GlowFilter(0x000000, 1, 6, 6, 2, 1, true),
    new GlowFilter(0x000000, .25, 45, 45, 2, 1, false)];
}

Anyone else have tricks they’d like to share for making renderers more efficient? Maybe flagging the display change and calling invalidate display list or avoiding the Canvas container?

Ant4CF Mailing List Created

I probably should have created a mailing list for Ant4CF when it was released, but I just never thought of it. There seems to be a good bit of interested in the project so Ive gone ahead and created a Google Group for it. Here is the information for the group:

Homepage:
http://groups.google.com/group/ant4cf

Group email:
ant4cf@googlegroups.com

Ill try to answer question right away as we get started on the list, but Im hoping others will be able to chime in as well (at least eventually)!

A Few Ant4CF 'Gotchas'

I have been using Ant, in some form or another, for project deployment for quite sometime. Recently, we released Ant4CF, which I love using, as it makse the process of updating your deployment scripts much easier than the process I was following. However, there were a few unexpected behaviors I ran into when setting up Ant4CF the first time.

First, I used to run all my Ant tasks from inside of Eclipse. Simply right-click, then choose ‘Run as…’ –> ‘Ant Build’ and Ant would take care of what needed to be done. When I started using Ant4CF, I would get an error when running the Ant task:

/Users/blah/Sites/usdaa/wwwapp/Deployment/DevBuild.xml:5: java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM

After chatting with Doug Hughes about this, we determined the issue stemmed from the fact that I was using an older version of Eclipse that did not allow me to run the tasks from Eclipse. After chatting with Chris Peterson, another Alagadian, he informed me that he is running the 64-bit version of Eclipse 3.5 (Galileo) and that he could run Ant4CF tasks without issue. I went and downloaded Galileo and my Ant4CF tasks run withoiut issue. If you are using Ant4CF and run into a similar error as above, try updating your Eclipse to a newer version, or even a 64-bit version and you should be good.

Next, when creating my deployment scripts, I used the XML tasks that are used in some of the samples in the Ant4CF documentation. These work great for being able to find specific values or nodes and modify them. This is often necessary to set configuration settings based on the environment (staging vs. production, for example). When I ran these scripts, they worked without issue, but when I checked the web application I was getting a lot of weird errors with ColdSpring, such as incorrect values being passed, or values of the incorrect type being passed.

This took some serious investigation. I was looking at my ColdSpring.xml files on my local machine and the XML attributes that were reported as causing the issue were not there. Not that they were set differently, they were not set at all. I looked at the ColdSpring.xml file on the server and the first thing I noticed was that the documents formatting, in terms of spacing and line breaks, was different than my local file. It also had a lot of attributes set that are not specified in my local copy. After a lot of trial and error, I determined that the issue stemmed from my document declaration in the XML file and specifically the DTD that was specified. It appears that the XML task actually rewrites the entire document based on the DTD, and in my case caused a lot of issues. The simple solution was to remove the reference to the DTD from my local copy of the file and re-deploy. This time everything worked as expected.

Finally, after I was able to get all my tasks running, and files updated as needed, I had to add a CF Admin specific task to clear the trusted cache. Before I go any further, let me explain the process I follow to do deployments. I typically set up a ‘web site’ on the server named something like ‘deployment.clientName.com’. I then drop all the Ant4CF files into the root of this site and add HOSTS file entries pointing URL to the server’s IP address.

When I added the task to clear the trusted cache, it would fail at this step. After doing some poking and prodding in the Ant4CF code, I discovered that Ant4CF actually makes an HTTP request to the proxy file sused to handle the CF Admin API functionality. This is actually run in the server, not on my local machine. Can you see the problem? The task would fail because the server couldnot resolve the domain name ‘deployment.clientname.com’. I added a HOSTS file entry to the server for ‘deployment.clientName.com’ and everything worked as expected.

Programmatic Drawing Bean

Continuing my rant about styles in the Adobe Flex world I’d like to review programmatic drawing of assets. Just for fun I’ll be drawing jelly beans.

First up I’ll take a stab at drawing one with the regular ole drawing api. This may sound odd to our Flex audience but a long long time ago in a land before degrafa or fxg flash developers just drew out lines, squares and jelly beans using the drawing api. The nice thing about being familiar with the drawing api is it will serve you well when you switch over to doing an Action Script only project.

Drawing out a quick Jelly Bean:

package
{
    import flash.display.GradientType;
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.filters.BlurFilter;
    import flash.filters.DropShadowFilter;
    import flash.filters.GlowFilter;

    public class JellyBean extends Sprite {
        protected
        var gloss: Sprite = new Sprite();

        public
        function JellyBean() {
            super();
            this.filters = [new GlowFilter(0x550000, .5, 25, 25, 2, 1, true), new DropShadowFilter(5, 90, 0x000000, .95, 15, 15, 1, 1)];
            this.addChild(gloss)
            gloss.filters = [new BlurFilter(8, 8)];
            drawBean()
        }

        protected
        function drawBean(): void {
            var g: Graphics = this.graphics;

            g.clear();
            g.lineStyle(2, 0x440000, .25, false, 'normal', null, 'round');
            g.beginGradientFill(GradientType.RADIAL, [0x880000, 0x550000], [1, 1], [75, 225], null, 'pad', 'rgb', 0.75);
            g.moveTo(0, 50);
            g.curveTo(0, 75, 50, 75);
            g.curveTo(100, 75, 100, 50);
            g.curveTo(100, 20, 75, 25);
            g.curveTo(50, 35, 25, 25);
            g.curveTo(0, 25, 0, 50);
            g.endFill();

            g = gloss.graphics;
            g.clear();
            g.moveTo(7, 50);
            g.lineStyle(0, 0xFFFFFF, 0)
            g.beginGradientFill(GradientType.LINEAR, [0xFFFFFF, 0xFFFFFF], [.25, 1], [0, 255]);
            g.curveTo(8, 30, 25, 30);
            g.curveTo(5, 28, 20, 30);
            g.curveTo(8, 30, 2, 50);
            g.curveTo(5, 55, 7, 50);

            g.beginGradientFill(GradientType.LINEAR, [0xFF0000, 0x660000], [.35, .35], [0, 255]);
            g.drawEllipse(10, 40, 65, 20)
        }
    }
}

If this were a Flex project you might extend the UIComponent and override the updateDisplay function to measure out the size of the bean a little better. It’d also be smart to make this a little more dynamic by adjusting the colors based on a public color value.

I also wanted to compare drawing out a jelly bean in FXG. I won’t post the Catalyst (http://labs.adobe.com/technologies/flashcatalyst/) generated code because it’s over 11,000 lines of code. I’m guessing I should have optimized that a little bit.

Lastly, I don’t want to overlook the simplest styling method of just importing an image:

source="@Embed('alagad/jellyBean.png')".

So simple and yet so effective.

Tag Cloud