Archive for June, 2012

Managing Multiple Build Target Environments

1

The ability to build and deploy to multiple environments is a true enterprise application development requirement. In many cases, the out-of-the-box build and deploy processes usually lack sufficient support. If you maintain multiple environments then you will probably agree with this observation. In the majority of my past projects, we maintained one or more environments for development, testing, staging, integration, QA, Production, training and others. The environments will require unique values including database connections, paths, email addresses and IP addresses. How do we manage these unique property values when attempting to automate the deployment process?

I have participated in several projects utilizing open source scripting tools. This is true for Java and .NET projects, which are faced with the same build challenges. The most popular tool is Ant or NAnt, which both have a loyal following that continue to contribute to the projects.

The main objective is a simple and intuitive build process, so the maintenance requires minimal skills. I rarely build and deploy from the IDE (except local), so the process should allow external execution with integration to the source control repository of choice. The scripting approach is also attractive for Continuous Integration, which provides significant benefits to any enterprise system.

We will select NAnt for this article, but the same techniques can easily be applied to the Java flavor (Ant) with a few adjustments. NAnt/Ant is an XML-based scripting tool with a high-level project element containing a series of callable targets. A target contains one or more tasks. You can download NAnt and unzip the binary distribution. I also recommend the NAntContrib project, which is a collection of tasks and functions that extend the capabilities of NAnt.

Once you complete the setup, the following is a simple NAnt script to build a solution. It is just the basic script to perform a simple build.

01
<?xml version="1.0" encoding="utf-8" ?>
02
<project name="MySolution" default="build">
03
  <!-- Set project properties-->
04
  <property name="name" value="MyNAntBuildSolution"/>
05
  <property name="build.source" value="${name}"/>
06
  <property name="build.out" value="${name}\bin\Release"/>
07
  <property name="build.target" value="release" />
08
 
09
<target name="init" description="create target folder" >
10
 <mkdir dir="${build.target}" />
11
</target>
12
 
13
<target name="clean" description="delete the target folder">
14
 <delete dir="${build.target}" failonerror="false"/>
15
  <delete dir="${build.out}" failonerror="false"/>
16
</target>
17
 
18
<target name="build" depends="clean,init" description="Build">
19
 <call target="compile"/>
20
 <copy todir="${build.target}">
21
 <fileset basedir="${build.out}">
22
      <include name="*.config" />
23
      <include name="*.exe" />
24
    </fileset>
25
 </copy>
26
</target>
27
 
28
<target name="compile" description="compile using release configuration">
29
 <exec program="${environment::get-variable('SYSTEMROOT')}\Microsoft.Net\Framework\v4.0.30319\msbuild.exe">
30
 <arg value="${name}.sln" />
31
 <arg value="/p:configuration=release"/>
32
 </exec>
33
</target>
34
</project>

In the above script, we define the solution (name), build.source, build.out and build.target properties. We created the clean and init targets to simply delete and create folders. The default build target performs our build process by calling the compile target and performing several tasks, which also depends on the init and clean targets. The compile target executes the MSBuild using the release configuration. The remaining build target tasks copy the MSBuild results to the build.target folder, which is ready for deployment.

So…this was simple enough to create a basic build script. The next challenge is supporting the unique target environments, so we can inject or replace property values during the build to customize the configuration. First, we can create a configuration file with placeholders or tokens representing a value that will change based on the target environment. As we stated earlier, we would like this process to be easy to maintain. So…we will create the dev.config XML file with easy to understand property elements with name-value pair attributes.

1
<?xml version="1.0" encoding="utf-8" ?>
2
<project>
3
  <property name="environment.name" value="Development"/>
4
</project> 

The above configuration file should be created for each target environment and contain all the properties with unique values per environment. Next, edit your application configuration file and replace the static value with a placeholder or token. The placeholder instructs the build script to replace the current value with the appropriate value from the configuration file based on the name. For example: @environment.name@ will be replaced Development using the above example. The following is the configuration file with the placeholders.

1
<?xml version="1.0" encoding="utf-8" ?>
2
<configuration>
3
  <appSettings>
4
    <add key="environment" value="@environment.name@"/>
5
  </appSettings>
6
</configuration>

Next, create a process to transform the placeholders into the values. NAnt offers a style task, which will provide the transformation services that are required. The script will create a dynamic NAnt target to replace tokens or placeholders with the appropriate values. The following is the XSLT to generate the dynamic NAnt target.

01
<?xml version="1.0" encoding="UTF-8"?>
02
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
03
<xsl:output method="xml" indent="yes"/>
04
<xsl:template match="/">
05
<xsl:apply-templates/>
06
</xsl:template>
07
<xsl:template match="project">
08
<xsl:element name="project">
09
<xsl:element name="target">
10
   <xsl:attribute name="name">token-replacement</xsl:attribute>
11
   <xsl:element name="copy">
12
      <xsl:attribute name="todir">${token-replacement.todir}</xsl:attribute>
13
      <xsl:attribute name="overwrite">true</xsl:attribute>
14
      <xsl:element name="fileset">
15
         <xsl:attribute name="basedir">${token-replacement.basedir}</xsl:attribute>
16
         <xsl:attribute name="defaultexcludes">true</xsl:attribute>
17
         <xsl:element name="include">
18
            <xsl:attribute name="name">${token-replacement.include}</xsl:attribute>
19
         </xsl:element>
20
      </xsl:element>
21
      <xsl:element name="filterchain">
22
         <xsl:element name="replacetokens">
23
            <xsl:for-each select="//property">
24
               <xsl:element name="token">
25
                  <xsl:attribute name="key">
26
                     <xsl:value-of select="@name"/>
27
                  </xsl:attribute> 
28
                  <xsl:attribute name="value">
29
                     <xsl:value-of select="@value"/>
30
                  </xsl:attribute> 
31
               </xsl:element>
32
            </xsl:for-each>
33
         </xsl:element>
34
   </xsl:element>
35
</xsl:element>
36
</xsl:element>
37
</xsl:element>
38
</xsl:template>
39
</xsl:stylesheet>

Why create a dynamic target? We would like to decouple the configuration information from the build script. In this design, the build script remains unchanged regardless of modifications to the configuration information. You can also maintain separate environment-specific configuration information. So…let’s look at the NAnt build script task to generate the dynamic target.

1
 <style in="${env}.config" style="token-replacement.xslt" out="token-replacement.frag" />

Once you generate the target fragment, you must instruct NAnt to load the target dynamically. The following task will load the generated target fragment into the script. You can now call this target.

1
<include buildfile="token-replacement.frag" failonerror="false"/>

The available source code download generates the following token-replacement.frag file, which is loaded using the include task above and created by the previous style task. The target takes advantage of the replacetokens task while performing the copy process. Since this target is part of the build, you have access to all build properties.

01
<?xml version="1.0" encoding="utf-8"?>
02
<project>
03
  <target name="token-replacement">
04
    <copy todir="${token-replacement.todir}" overwrite="true">
05
      <fileset basedir="${token-replacement.basedir}" defaultexcludes="true">
06
        <include name="${token-replacement.include}" />
07
      </fileset>
08
      <filterchain>
09
        <replacetokens>
10
          <token key="environment.name" value="Development" />
11
        </replacetokens>
12
      </filterchain>
13
    </copy>
14
  </target>
15
</project>

The following is the complete NAnt build script including the transform-config target, which we can also call via Visual Studio as a Post-build event process to perform the configuration transformation for local debug modes.

01
<?xml version="1.0" encoding="utf-8" ?>
02
<project name="MyNAntBuildSolution" default="build">
03
  <!-- Set common project properties-->
04
  <property name="name" value="${project::get-name()}"/>
05
  <property name="build.source" value="${name}"/>
06
  <property name="build.out" value="${name}\bin\Release"/>
07
  <property name="build.target" value="release" />
08
  <property name="app.config" value="${name}.exe.config"/>
09
 
10
  <!-- Create a dynamic NAnt target to replace tokens in
11
       configuration file-->
12
  <echo message="Creating token-replacement target..."/>
13
  <delete file="token-replacement.frag" 
14
          failonerror="false"/>
15
  <style in="${env}.config" 
16
         style="token-replacement.xslt" 
17
         out="token-replacement.frag" />
18
  <echo message="Loading token-replacement target..."/>
19
  <include buildfile="token-replacement.frag" 
20
           failonerror="false"/>
21
 
22
  <!--- Intialize to create target folders -->
23
  <target name="init" description="create target folder" >
24
    <mkdir dir="${build.target}" />
25
  </target>
26
  <!-- Clean to remove old target folders -->
27
  <target name="clean" description="delete the target folder">
28
    <delete dir="${build.target}"
29
            failonerror="false"/>
30
    <delete dir="${build.out}"
31
            failonerror="false"/>
32
  </target>
33
  <!-- Build to compile and assemble the deployment artifacts -->
34
  <target name="build" depends="clean,init" description="Build">
35
    <call target="compile"/>
36
    <call target="transform-config"/>
37
    <copy todir="${build.target}">
38
      <fileset basedir="${build.out}">
39
        <include name="*.config" />
40
        <include name="*.exe" />
41
      </fileset>
42
    </copy>
43
  </target>
44
  <!-- Compile to call MSBuild -->
45
  <target name="compile" description="compile using release configuration">
46
    <exec program="${environment::get-variable('SYSTEMROOT')}\Microsoft.Net\Framework\v4.0.30319\msbuild.exe">
47
      <arg value="${name}.sln" />
48
      <arg value="/p:configuration=release"/>
49
    </exec>
50
  </target>
51
  <!-- Transform Config to convert the application
52
        config file using transformation -->
53
  <target name="transform-config">
54
    <property name="token-replacement.basedir"
55
              value="${build.out}"/>
56
    <property name="token-replacement.todir"
57
              value="${directory::get-current-directory()}"/>
58
    <property name="token-replacement.include"
59
              value="${app.config}"/>
60
    <call target="token-replacement"/>
61
    <!-- Replace build.out file with transform file -->
62
    <move file="${app.config}"
63
          tofile="${build.out}\${app.config}"
64
          overwrite="true"/>
65
  </target>
66
</project>

What’s next? Well, the addition of a target to handle the source control checkout/Get Latest prior to executing the build target would be very useful. NAnt and NAntContrib contain tasks supporting many popular source control products, so adding this feature requires just a little configuration for your repository. You can also include targets to run your regression tests (e.g. NUnit) and produce informative reports. Finally, the deployment task would be required to move the release folder to the target server. Again, several tasks are available to add this feature to your build script.

You can download the source code (see comments for additional information), which contains a build script for a simple console application. You can run the build script from the command prompt or download the NAnt Visual Studio plug-in. The following is the command-line to run the script, which requires the environment configuration file name as a parameter.

NAnt.exe -D:env=dev

In conclusion, using NAnt as your external build tool offers many out-of-the-box features. I am sure you will find answers to many of your challenging build and deploy scenarios. If you do not find a task to suit your needs then it is easy to create a custom NAnt task. I hope this article offers some tips for managing builds to multiple target environments.

Source Code

ASP.NET and JQuery Ajax

1

After my recent series of design pattern articles, this post will introduce an alternative ASP.NET Ajax implementation using JQuery. The result is lightweight with a smaller payload than ASP.NET Ajax. In this example, we will setup a simple web page with an Ajax call to display the current date and time. This is not an exciting demonstration, but the objective is providing the basics for a more complex implementation and evidence the data is changing for each request.

First, create a page method using the WebMethod attribute to return the current date/time as a string. The Ajax client will define the data type within the request, which our example will be JSON.

1
        [WebMethod(EnableSession = false)]
2
        public static string GetDateString()
3
        {
4
            return string.Format("{0}-{1}-{2} {3}",
5
                                 DateTime.Now.Month.ToString(),
6
                                 DateTime.Now.Day.ToString(),
7
                                 DateTime.Now.Year.ToString(),
8
                                 DateTime.Now.ToLongTimeString());
9
        }

Alternatively, you could elect to return markup and inject into the current page. In this scenario, you are performing the formatting server-side. You could also apply style on the client-side.

The Default.aspx page contains a button with the click event to trigger the Ajax call to the page method. The following is the Default.aspx body fragment with the btnCurrentDateString button and divDate elements.

1
<body>
2
  <button id="btnCurrentDateString">Get Date As String</button>
3
  <div id="divDate"></div>

We replace the ASP.NET Ajax with JQuery Ajax script, which will define the click event of the btnCurrentDateString button. We will bind the results to a div element, which is handled by the JQuery success option. As discussed previously, we set the dataType option to “json” as the data type.

01
  
02
      $('#btnCurrentDateString').click(function () {
03
          $.ajax({
04
              type: "POST",
05
              url: "Default.aspx/GetDateString",
06
              data: "{}",
07
              contentType: "application/json",
08
              dataType: "json",
09
              success: function (msg) {
10
                  $("#divDate").text('String: ' + msg.d);
11
              }
12
          });
13
      });

When you launch the page and click the button, the current date and time is displayed on the page. The IE9 browser Developer tools capture the network traffic, so you can see the response from the Ajax call.

What if you need to return a more complex object? So…let’s create the DataInfo object as a container for our properties including month, day, year and time.

01
    public class DateInfo
02
    {
03
        public DateInfo(int month,
04
                        int day,
05
                        int year)
06
        {
07
            this.Month = month;
08
            this.Day = day;
09
            this.Year = year;
10
        }
11
 
12
        public int Month { get; set; }
13
        public int Day { get; set; }
14
        public int Year { get; set; }
15
        public string Time 
16
        { 
17
            get { return DateTime.Now.ToLongTimeString(); } 
18
        }
19
    }

We will add a new page method to handle this request, which will return the DataInfo object using JSON. There is no additional processing required, since the WebMethod attribute instructs the page to handle the details.

1
        [WebMethod(EnableSession = false)]
2
        public static DateInfo GetDateObject()
3
        {
4
            return new DateInfo(DateTime.Now.Month,
5
                         DateTime.Now.Day,
6
                         DateTime.Now.Year);
7
        }

We will also add a new button to call the GetDateObject page method. The HTML fragment appears below.

1
<body>
2
  <button id="btnCurrentDateObject" >Get Date As Object</button>
3
  <div id="divDate"></div>

The following is the JQuery script to make the call and bind the response to the divDate element. The script can access the individual fields by name, so it is very easy to bind the values to one or more page elements.

01
  
02
      $('#btnCurrentDateObject').click(function () {
03
          $.ajax({
04
              type: "POST",
05
              url: "Default.aspx/GetDateObject",
06
              data: "{}",
07
              contentType: "application/json",
08
              dataType: "json",
09
              success: function (msg) {
10
                  $("#divDate").text('Object: ' + msg.d.Month + '-' + msg.d.Day + '-' + msg.d.Year + ' ' + msg.d.Time);
11
              }
12
          });
13
      });

Again the response is displayed using browser tools, so you can view the raw data format.  In this example, you can see the DataInfo fields are defined with the associated value. The serialize/deserialize process is handled for you, so no additional client or server code is necessary to manage a more complex object.

As you can see, JQuery provides a very simple and clean Ajax implementation to consume a service. I hope you find this information helpful as an alternative to ASP.NET Ajax.

Source Code

Go to Top