Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Friday, December 10, 2010

"Constants" Framework in .Net

Motivation:
  • Constants are spread all over the code, in different files
  • Sometimes they are defined in Constants.cs or AllConstants.cs 
  • Some are defined as constants in the file it is used
  • Some are defined right before being used
  • Sometimes they are not defined at all, just used in place as a number or as a string
  • You have to be familiar with the code in order to tell the value of a particular constant and/or change it
  • Same constants are defined multiple times in different files
  • To make an existing constant being read from a config file you need to write some non trivial code
With the new Framework
  • All constants are defined in a single XML file
  • The framework introduces a very consistent way of declaring and using constants. Strong typing remains ensured. Code duplication is reduced.
  • With the new framework to make a constant editable is as easy as changing the mode from “const” to either mode=“runtime” (always possible to change the value of a “constant” via modifying a configuration XML file and restarting the service) or mode=”runtimeTest” (you can modify the constants at the runtime for testing and debug builds, but you won’t be able to do that in optimized non-test builds).
  • The consumer of a constant does not need to worry about the fact that the value might come from a config file. In other words we get “configurable” constants for free. Some might argue that constants should not be editable, but in my experience this becomes useful for testing/debugging. Playing with timeout/retry values could help expose deadlocks or leaks. A leak that takes 1 day to show up with an interval of 2 minutes could appear after 15 minutes with a 1 second interval.
  • It becomes very easy to get an overview of the constants being used.
How does it work?
All the work is done by the ConstGen.xslt XML conversion: it has been implemented and can be reused by all projects: get it from here.

As the entry argument this XSLT file has an XML project definition file. In difference to the XSLT file above which can be reused for all the projects, each project must define an XML constants definition file. Here is an example:

<?xml version="1.0" encoding="utf-8" ?>
<constants>
  <constant namespace="MyCompany.MyProject" managedName="Constants" access="internal" isStatic="yes">
    <description>All constants for Server.</description>
    <fields>
      <!-- Constants -->
      <field name="VERSION" type="string" value='1.0' mode="runtimeTest" configName="Version" access="internal">
        <description>Current Server version.</description>
      </field>
      <field name="TRANSFER_TIMEOUT" type="int" value="30 * 1000" mode="runtimeTest" configName="Transfer" access="internal">
        <description>Time in millisecods for how long the transfer operation can take.</description>
      </field>
    </fields>
  </constant>

  <constant namespace="MyCompany.MyProject.Common" managedName="Constants" isStatic="yes">
    <description>Common constants for the Call Park Server and Music on Hold functionality.</description>
    <fields>
      <field name="NUMBER_THREADS" type="int" value="Environment.ProcessorCount" mode="runtime" configName="NumberThreads" access="internal">
        <description>Number of running threads in parallel</description>
      </field>
      <field name="RETRY_INTERVAL" type="int" value="3 * 60 * 1000" mode="const" access="internal">
        <description>The retry interval for Enpoint.Establish() in milliseconds.</description>
      </field>
      <field name="RETRY_MAX_ATTEMPTS" type="int" value="10" mode="const" access="private">
        <description>Maximum number of retries to reserve an orbit.</description>
      </field>
    </fields>
  </constant>
</constants>
     
    Next you run XSLT giving as an entry argument the constants definition XML file. As a result a real C# constants file is created. For the XML file above it will look like this.
   
    Now, how do we make this preprocessing step of conversion of an XML file to C# happen? The easiest is to use Visual Studio: it has a built-in XSLT support, so just click on Execute button on opened XSLT file (the one above), it will ask you for an entry XML file, and that's it.
But of course you will want to automate this step. Then, what you need to do is to download MSXSL tool and run it before compiling your project.

If you want to change a value of a constant at runtime, change the corresponding value of the MyApp.exe.config file. Then restart the service. This file should be placed under the same directory where your executable is. Here is an XSLT file that you can use to create the configuration file where you can change the constant values. I.e. "constant" values now will be read from a configuration file. They still will be constants since they will remain same through the execution of the program and you need to restart the program if you change the "constant" value in the configuration file.

The configuration file created by running the XSLT file above and the same XML file will be the following:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
  <appSettings>

    <add key="Version" value="1.0"/>
    <add key="Transfer" value="30 * 1000"/>
    <add key="NumberThreads" value="Environment.ProcessorCount"/>

  </appSettings>
</configuration>

Wednesday, November 3, 2010

How to write a client to access Google App Engine with GWT

This tip is needed when you create a new Google App Engine project using Eclipse and choose the default settings, in particular when you leave Google Web Toolkit box checked (no trick needed if you uncheck it). If you always uncheck Google Web Toolkit you won't need this. You may still find useful the below example on how to access Google App Engine from a C# client.

The problem is that the framework will create for you all the skeletons for accessing the App Engine from the browser. But what if in addition you want to access the App Engine from a custom made client?

Passing regular parameters will not help - they won't be parsed correctly by the Google App Engine framework.
You need a custom parameter passed, that looks like this:

"5|0|6|http://1.latest.myapp.appspot.com/myapp/|SOMEGUID|myapp.clientGreetingService|greetServer|java.lang.String/2004016611|ThisIsAParameterValue|1|2|3|4|1|5|6|";


Here is the client in C# accessing Google App Engine (created from Eclipse with Web Toolkit option): 

WebRequest request = WebRequest.Create(   "http://1.latest.myapp.appspot.com/appname/greet");
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = "POST";
request.ContentType = "text/x-gwt-rpc; charset=utf-8";

string content = "5|0|6|http://1.latest.myapp.appspot.com/myapp/|AF76B9CA2759BDA4DBA207D5542054B2|myapp.clientGreetingService|greetServer|java.lang.String/2004016611|ThisIsAParameterValue|1|2|3|4|1|5|6|";

byte[] contentBytes = System.Text.UTF8Encoding.UTF8.GetBytes(content);
request.ContentLength = contentBytes.Length;
using (Stream stream = request.GetRequestStream())
{
    stream.Write(contentBytes, 0, contentBytes.Length);
}

WebResponse response = request.GetResponse();
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
Console.WriteLine(responseFromServer);

reader.Close();
response.Close(); 

In a nutshell, what is important is to provide the servlet name (greet server as in the example above) and then parameter types and values. Other values are not important, the Google App Engine parser just needs the number and placement of pipes.

Tuesday, October 26, 2010

Creating and Extracting a Zip File for Free

I could not find a free zip/unzip program on the web, so I had to create one myself.
Good guys here are at http://dotnetzip.codeplex.com that created a free zip library (Ionic.zip.dll) for us to use.

Using the Ionic library the program code for zipping and unzipping files is straightforward:

using System;
using System.IO;
using System.Text;
using Ionic.Zip;

namespace Zip
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length < 2)
            {
                Usage();
                return;
            }

            if (args[0].StartsWith("z"))
            {
                Pack(args[1]);
            }
            else
            {
                UnPack(args[1]);
            }
        }

        static void Pack(string dir)
        {
            try
            {
                string dirRoot = Path.GetDirectoryName(dir);
                string dirName = Path.GetFileName(dir);
                string result = Path.Combine(dirRoot, dirName + ".zip");

                using (ZipFile zip = new ZipFile())
                {
                    zip.AddDirectory(dir, dirName);
                    zip.Save(result);
                }
                Console.WriteLine("Saved {0} to {1}", dir, result);
            }
            catch (Exception exc)
            {
                Console.WriteLine("Error packing: " + exc.Message);
                return;
            }
        }

        static void UnPack(string zipfile)
        {
            try
            {
                string dirRoot = Path.GetDirectoryName(zipfile);
                string dirName = Path.GetFileNameWithoutExtension(zipfile);
                string result = Path.Combine(dirRoot, dirName + "_extracted");

                using (ZipFile zip1 = ZipFile.Read(zipfile))
                {
                    foreach (ZipEntry e in zip1)
                    {
                        e.Extract(result, ExtractExistingFileAction.OverwriteSilently);
                    }
                }
                Console.WriteLine("Extracted {0} to {1}", zipfile, result);
            }
            catch (Exception exc)
            {
                Console.WriteLine("Error unpacking: " + exc.Message);
                return;
            }
        }

        static void Usage()
        {
            string exe = System.Reflection.Assembly.GetExecutingAssembly().Location;
            Console.WriteLine("Usage: {0} zip|unzip <directory|zip file>", exe);
            Console.WriteLine(" E.g.: {0} zip \"C:\\tmp\"", exe);
            Console.WriteLine(" E.g.: {0} unzip \"C:\\tmp\\myfile.zip\"", exe);

            Console.WriteLine();
            Console.WriteLine("Please press any key.");

            Console.ReadKey();
        }
    }
}


You can download this free zip/unzip program at http://www.nanogmail.com/Zip.zip and the dll from

Instructions:
  • Download Zip.zip and rename it to Zip.exe
  • Download Ionic.Zip.zip and rename it to Ionic.Zip.dll
  • Place Ionic.Zip.dll and Zip.exe to the same directory
  • Usage:
    • Zip.exe zip C:\directoryToZip
    • Zip.exe unzip C:\myzipfile.zip

You can also create and extract Zip files using standard .Net, but it is less straightforward.
Here is how to do that (add a reference to WindowsBase.dll in your project):


using System.IO;
using System.IO.Packaging;

namespace MyZip
{
    public class Zip
    {
        private static int _dataChunk = 1024;

        public static void CreateZipFile(string zipFile, string pathname)
        {
            using (Package zip = Package.Open(zipFile, FileMode.Create))
            {
                AddDirToZip(zip, pathname, "");
            }
        }

        public static void ExtractFromZipFile(string zipFile, string pathname)
        {
            using (Package zip = Package.Open(zipFile, FileMode.Open, FileAccess.Read))
            {
                foreach (PackagePart zipPart in zip.GetParts())
                {
                    Uri uri = zipPart.Uri;

                    string file = pathname + uri.OriginalString;
                    string dirName = Path.GetDirectoryName(file);

                    using (FileStream output = File.Create(file))
                    {
                        Stream input = zipPart.GetStream(FileMode.Open, FileAccess.Read);

                        byte[] buf = new byte[_dataChunk];
                        int bytes;

                        while (0 != (bytes = input.Read(buf, 0, _dataChunk)))
                        {
                            output.Write(buf, 0, bytes);
                        }

                        output.Close();
                    }
                }
            }
        }

        private static void AddDirToZip(Package zip, string pathname, string relDir)
        {
            foreach (string dir in Directory.GetDirectories(pathname))
            {
                string newRelDirName = relDir + Path.GetFileName(dir) + "\\";
                AddDirToZip(zip, dir, newRelDirName);
            }

            foreach (string file in Directory.GetFiles(pathname))
            {
                AddFileToZip(zip, file, relDir);
            }
        }

        private static void AddFileToZip(Package zip, string file, string relDir)
        {
            string filename = Path.GetFileName(file);
            string relPath = relDir + filename;
            Uri uri = PackUriHelper.CreatePartUri(new Uri(relPath, UriKind.Relative));
            PackagePart zipPart = zip.CreatePart(uri, System.Net.Mime.MediaTypeNames.Text.Plain);

            using (FileStream input = new FileStream(file,
                FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                Stream zipStream = zipPart.GetStream();

                byte[] buf = new byte[_dataChunk];
                int bytes;

                while (0 != (bytes = input.Read(buf, 0, _dataChunk)))
                {
                    zipStream.Write(buf, 0, bytes);
                }
            }
        }
    }
}