<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DEQBSHozeSp7ImA9WhRQGUg.&quot;"><id>tag:blogger.com,1999:blog-12198751</id><updated>2011-12-15T21:39:19.481+08:00</updated><category term="mobile" /><category term="interop" /><category term="smtp" /><category term="cryptography" /><category term="private key" /><category term="html5" /><category term="web hosting" /><category term="jndi" /><category term="storage" /><category term="tomcat" /><category term="ikvm" /><category term="http" /><category term="4.0" /><category term="freemarker" /><category term="form" /><category term="c#" /><category term="multipart" /><category term="ssma" /><category term="base64" /><category term="cryptoapi" /><category term="configuration" /><category term="jetty" /><category term="pkcs12" /><category term="System.Runtime.Caching" /><category term="e4x" /><category term="video" /><category term="servlet" /><category term="windows" /><category term="channel factory" /><category term=".net" /><category term="jasper" /><category term="entlib" /><category term="prediction" /><category term="web.config" /><category term="wcf" /><category term="javafx" /><category term="xml" /><category term="setTimeout" /><category term="linq" /><category term="iis" /><category term="browser war" /><category term="jeans" /><category term="java" /><category term="mysql" /><category term="silverlight" /><category term="php" /><category term="cookies" /><category term="ajax" /><category term="ckeditor" /><category term="azure" /><category term="sorting" /><category term="jsp" /><category term="view engine" /><category term="mhtml" /><category term="post" /><category term="sql azure" /><category term="mxhr" /><category term="sql server" /><category term="flex" /><category term="filter" /><category term="null" /><category term="mvc" /><category term="setInterval" /><category term="memory cache" /><category term="javax.sql.DataSource" /><category term="sql" /><category term="pfx" /><category term="jpa" /><category term="upload" /><category term="html" /><category term="ShineDraw" /><category term="asp.net" /><category term="ria" /><category term="caching" /><category term="code generator" /><category term="JavaScript" /><category term="app.config" /><category term="json" /><category term="google" /><category term="unity" /><category term="hostmonster issue" /><title>Star Tech Corner</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://killustar.blogspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>51</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/StarTechCorner" /><feedburner:info uri="startechcorner" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;DUQHSXk_cCp7ImA9WhZQGUw.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-853558758462067355</id><published>2011-04-27T22:58:00.004+08:00</published><updated>2011-04-27T23:15:38.748+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-27T23:15:38.748+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="view engine" /><category scheme="http://www.blogger.com/atom/ns#" term="mvc" /><category scheme="http://www.blogger.com/atom/ns#" term="freemarker" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="ikvm" /><title>ASP.NET MVC View Engine with FreeMarker</title><content type="html">FreeMarker is a quite success template engine in Java world. It is used with Apache Struts, an MVC framework, as a view engine. ASP.NET MVC is using Web Form as the default view engine. The problem is the view become a spaghetti of HTML and C# snippets fairly quickly, just like classic ASP and PHP. &lt;a href="http://freemarkernet.codeplex.com/"&gt;FreeMarker.Net&lt;/a&gt; is a project that ports the FreeMaker as an ASP.NET MVC view engine&lt;br /&gt;
&lt;br /&gt;
The idea is compiling FreeMarker to .Net assembly with IKVM and create a wrapper to wrap .Net objects so that FreeMarker can understand.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Compiling FreeMarker&lt;/b&gt;&lt;br /&gt;
It is a strength forward process. It can be done with one command:&lt;br /&gt;
&lt;code&gt;ikvmc freemarker.jar -target:library&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Separating Necessary IKVM Libraries&lt;/b&gt;&lt;br /&gt;
Only the following libraries is required for development:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;IKVM.OpenJDK.Beans.dll&amp;nbsp;&lt;/li&gt;
&lt;li&gt;IKVM.OpenJDK.Charsets.dll&lt;/li&gt;
&lt;li&gt;IKVM.OpenJDK.Core.dll&lt;/li&gt;
&lt;li&gt;IKVM.OpenJDK.SwingAWT.dll&lt;/li&gt;
&lt;li&gt;IKVM.OpenJDK.Text.dll&lt;/li&gt;
&lt;li&gt;IKVM.OpenJDK.Util.dll&lt;/li&gt;
&lt;li&gt;IKVM.Runtime.dll&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;b&gt;Wrapping .Net Object&lt;/b&gt;&lt;br /&gt;
FreeMarker does not directly deal with the objects. Instead, it deals with the &lt;code&gt;TemplateModel&lt;/code&gt; objects. There are a few template models to be implemented:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;TemplateBooleanModel&lt;/li&gt;
&lt;li&gt;TemplateDateModel&lt;/li&gt;
&lt;li&gt;TemplateHashModel&lt;/li&gt;
&lt;li&gt;TemplateMethodModelEx&lt;/li&gt;
&lt;li&gt;TemplateNumberModel&lt;/li&gt;
&lt;li&gt;TemplateScalarModel&lt;/li&gt;
&lt;li&gt;TemplateSequenceModel&lt;/li&gt;
&lt;/ul&gt;FreeMarker provides an &lt;code&gt;ObjectWrapper&lt;/code&gt; interface to wrap the raw objects into &lt;code&gt;TemplateModel&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;NetObjectModel&lt;/code&gt; is actually is a &lt;code&gt;TemplateHashModel&lt;/code&gt;&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;public class NetObjectModel : StringModel, 
                              TemplateModel, 
                              TemplateHashModel {
        Dictionary&amp;lt;string, PropertyInfo&amp;gt; props = 
            new Dictionary&amp;lt;string, PropertyInfo&amp;gt;();
        Dictionary&amp;lt;string, MethodModel&amp;gt; methods = 
            new Dictionary&amp;lt;string, MethodModel&amp;gt;();
        Dictionary&amp;lt;string, ExtensionMethodModel&amp;gt; extensionMethods = 
            new Dictionary&amp;lt;string, ExtensionMethodModel&amp;gt;();

        public NetObjectModel(object data, 
                              NetObjectWrapper wrapper)
            : base(data, wrapper){
            var type = data.GetType();
            foreach (var p in type.GetProperties()) {
                props.Add(p.Name, p);
            }
            foreach (var m in type.GetMethods()) {
                if (!methods.ContainsKey(m.Name)) {
                    methods.Add(m.Name, 
                                new MethodModel(data,  
                                                wrapper, 
                                                m.Name));
                }
            }
        }

        public virtual TemplateModel get(string key) {
            if (props.ContainsKey(key)) {
                return wrapper.wrap(props[key].GetGetMethod()
                                              .Invoke(data, null));
            }
            else if (methods.ContainsKey(key)) {
                return methods[key];
            }
            else if (wrapper.ExtensionTypes.Count &amp;gt; 0) {
                if (!extensionMethods.ContainsKey(key)) {
                    extensionMethods[key] = 
                        new ExtensionMethodModel(data, 
                                                 wrapper, 
                                                 key);
                }
                return extensionMethods[key];
            }
            else {
                return TemplateModel.__Fields.NOTHING;
            }            
        }

        public virtual bool isEmpty() {
            return props.Count == 0;
        }
    }
&lt;/pre&gt;&lt;br /&gt;
To adapt the ASP.NET objects, three more template model are created:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;HttpApplicationStateModel&lt;/li&gt;
&lt;li&gt;HttpRequestModel&lt;/li&gt;
&lt;li&gt;HttpSessionStateModel&lt;/li&gt;
&lt;/ul&gt;They are similar but not in the same interface, all of them are like this:&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;public class HttpApplicationStateModel : NetObjectModel, 
                                         TemplateModel, 
                                         TemplateHashModel {

        public HttpApplicationStateModel(
             object data, 
             NetObjectWrapper wrapper)
            : base(data, wrapper) {
            
        }

        public override TemplateModel get(string key) {
            HttpApplicationStateBase dic = 
                data as HttpApplicationStateBase;
            if (dic.Keys.Cast&amp;lt;string&amp;gt;().Contains(key)) {
                return wrapper.wrap(dic[key]);
            }
            else {
                return base.get(key);
            }
        }

        public override bool isEmpty() {
            IDictionary&amp;lt;string, object&amp;gt; dic = 
                data as IDictionary&amp;lt;string, object&amp;gt;;
            return dic.Count == 0 &amp;&amp; base.isEmpty();
        }
    }&lt;/pre&gt;&lt;b&gt;View Engine&lt;/b&gt;&lt;br /&gt;
The view engine is fairly simple, it simply initialize the FreeMarker configuration.&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;public class FreemarkerViewEngine : VirtualPathProviderViewEngine {
        Configuration config;
        public FreemarkerViewEngine(string rootPath, 
                                    string encoding = "utf-8") {
            config = new Configuration();
            config.setDirectoryForTemplateLoading(
                new java.io.File(rootPath));
            AspNetObjectWrapper wrapper = 
                new AspNetObjectWrapper();

            config.setObjectWrapper(wrapper); 
            base.ViewLocationFormats = 
                new string[] { "~/Views/{1}/{0}.ftl" };
            config.setDefaultEncoding(encoding);
            base.PartialViewLocationFormats = 
                base.ViewLocationFormats;
        }

        protected override IView CreatePartialView(
            ControllerContext controllerContext, 
            string partialPath) {
            return new FreemarkerView(config, partialPath);
        }

        protected override IView CreateView(
            ControllerContext controllerContext, 
            string viewPath, 
            string masterPath) {
            return new FreemarkerView(config, viewPath);
        }
    }
&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;View&lt;/b&gt;&lt;br /&gt;
&lt;code&gt;FreemarkerView&lt;/code&gt; implements &lt;code&gt;IView&lt;/code&gt; to render the content. It simply create the dictionary as variable bindings and invoke the FreeMarker.&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;public class FreemarkerView : IView{
    public class ViewDataContainer : IViewDataContainer {
        private ViewContext context;
        public ViewDataContainer(ViewContext context) {
            this.context = context;
        }

        public ViewDataDictionary ViewData {
            get {
                return context.ViewData;
            }
            set {
                context.ViewData = value;
            }
        }
    }

    private freemarker.template.Configuration config;
    private string viewPath;
    public FreemarkerView(freemarker.template.Configuration config, string viewPath) {
        this.config = config;
        this.viewPath = viewPath;
    }

    public void Render(ViewContext viewContext, 
                        System.IO.TextWriter writer) {
        Template temp = config.getTemplate(viewPath.Substring(2));
            
        Dictionary&amp;lt;string, object&amp;gt; data = 
                        new Dictionary&amp;lt;string, object&amp;gt;{
            {"model", viewContext.ViewData.Model},
            {"session", viewContext.HttpContext.Session},
            {"http", viewContext.HttpContext},
            {"request", viewContext.HttpContext.Request},
            {"application", viewContext.HttpContext.Application},
            {"view", viewContext},
            {"controller", viewContext.Controller},
            {"url", new UrlHelper(viewContext.RequestContext)},
            {"html", new HtmlHelper(viewContext, 
                            new ViewDataContainer(viewContext))},
            {"ajax", new AjaxHelper(viewContext, 
                            new ViewDataContainer(viewContext))},
        };

        Writer output = new JavaTextWriter(writer);
        temp.process(data, output);
        output.flush();
    }
}
&lt;/pre&gt;&lt;b&gt;Configuration&lt;/b&gt;&lt;br /&gt;
Configuration is as simple as adding the view engine to the view engine collections.&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;protected void Application_Start() {
    AreaRegistration.RegisterAllAreas();

    // ****** Optionally, you can remove all other view engines ******
    //ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new FreemarkerViewEngine(this.Server.MapPath("~/")));

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}
&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;Extension Methods&lt;/b&gt;&lt;br /&gt;
ASP.NET MVC relies on extension method quite heavily. In the &lt;code&gt;NetObjectWrapper&lt;/code&gt;, the following code is added to find the extension methods:&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;public virtual void AddExtensionNamespace(string ns) {
    var types = this.ExtensionTypes;
    foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) {
        try {
            foreach (var t in a.GetExportedTypes()) {
                if (!types.Contains(t) &amp;&amp; 
                    t.IsClass &amp;&amp; 
                    t.Name.EndsWith("Extensions") &amp;&amp; 
                    t.Namespace == ns &amp;&amp; 
                    t.GetConstructors().Length == 0) {
                    types.Add(t);
                }
            }
        }
        catch { }
    }
}
&lt;/pre&gt;In the view engine's constructor, it reads the namespaces specified under &lt;code&gt;system.web/pages&lt;/code&gt; sections in web.config:&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;PagesSection section = 
    (PagesSection)WebConfigurationManager
                      .OpenWebConfiguration("~/")
                      .GetSection("system.web/pages");
if (section != null) {
    foreach (NamespaceInfo info in section.Namespaces) {
        wrapper.AddExtensionNamespace(info.Namespace);
    }
}
&lt;/pre&gt;An &lt;code&gt;ExtensionMethodModel&lt;/code&gt; class is created to find the appropriate method to invoke:&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;foreach (var type in wrapper.ExtensionTypes) {
    MethodInfo method = type.GetMethod(
        methodName, 
        BindingFlags.Public | BindingFlags.Static, 
        Type.DefaultBinder, 
        argTypes, null);
    if (method != null) {
        cache.Add(key, method);
        return wrapper.wrap(method.Invoke(target, args.ToArray()));
    }
}
&lt;/pre&gt;&lt;b&gt;Localization&lt;/b&gt;&lt;br /&gt;
&lt;code&gt;ResourceManagerDirectiveModel&lt;/code&gt; (a &lt;code&gt;TemplateDirectiveModel&lt;/code&gt;) and &lt;code&gt;ResourceManagerModel&lt;/code&gt; are created so that we could do something like this:&lt;br /&gt;
&lt;pre class="html" name="code"&gt;&lt;@resource type="Freemarker.Net.MvcWeb.App_GlobalResources.PersonResource, Freemarker.Net.MvcWeb"/&gt;
&lt;h2&gt;${res.Title}&lt;/h2&gt;&lt;/pre&gt;&lt;code&gt;ResourceManagerDirectiveModel&lt;/code&gt; is getting the &lt;code&gt;ResourceManager&lt;/code&gt; from the resource and put it into &lt;code&gt;res&lt;/code&gt; template variable:&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;public void execute(freemarker.core.Environment env, java.util.Map parameters, TemplateModel[] models, TemplateDirectiveBody body) {
    if (parameters.containsKey("type")) {
        TemplateScalarModel scalar = 
            (TemplateScalarModel)parameters.get("type");
        var type = Type.GetType(scalar.getAsString());
        env.setVariable("res", 
            env.getObjectWrapper()
                .wrap(type.GetProperty("ResourceManager", 
                                        BindingFlags.Static | 
                                        BindingFlags.NonPublic | 
                                        BindingFlags.Public)
                        .GetGetMethod(true)
                        .Invoke(null, null)));
    }            
}
&lt;/pre&gt;&lt;b&gt;Adding More Directives&lt;/b&gt;&lt;br /&gt;
To all more directives can be added in the future, MEF is used. The directive implementation only needs to export and implement the &lt;code&gt;ImportableDirective&lt;/code&gt; interface.&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;[Export(typeof(ImportableDirective))]
    public class ResourceManagerDirectiveModel : TemplateDirectiveModel, ImportableDirective {
&lt;/pre&gt;The view engine will import them in the constructor:&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;public class FreemarkerViewEngine : VirtualPathProviderViewEngine {
        Configuration config;
        [ImportMany]
        IEnumerable&amp;lt;ImportableDirective&amp;gt; directives;
        public FreemarkerViewEngine(string rootPath, 
                                    string encoding = "utf-8") {
            // initialize the config 
            // ...
            // import the extension methods' namespaces
            // ...
            // import the directives
            var dir = new DirectoryInfo(
                Path.Combine(rootPath, "bin"));
            var catalogs = dir.GetFiles("*.dll")
                .Where(o =&amp;gt; o.Name != "freemarker.dll" &amp;&amp; 
                           !(o.Name.StartsWith("IKVM.") || 
                           o.Name.StartsWith("Freemarker.Net")))
                .Select(o =&amp;gt; new AssemblyCatalog(
                             Assembly.LoadFile(o.FullName))
            );
            var container = new CompositionContainer(
                new AggregateCatalog(
                    new AggregateCatalog(catalogs),
                    new AssemblyCatalog(
                      typeof(ImportableDirective).Assembly)
            ));
            container.ComposeParts(this);
        }
&lt;/pre&gt;&lt;br /&gt;
The source code is available in CodePlex. Please visit &lt;a href="http://freemarkernet.codeplex.com/"&gt;http://freemarkernet.codeplex.com/&lt;/a&gt; and &lt;a href="http://www.shaneng.net/Main/FreeMarkerViewEngine"&gt;http://www.shaneng.net/Main/FreeMarkerViewEngine&lt;/a&gt; for more detail.&lt;br /&gt;
&lt;br /&gt;
P.S.: The bonus of this project is we have not only a new view engine in ASP.NET MVC, but also a new template engine in .Net Framework. Maybe someday we could use FreeMarker to generate code instead of T4 in Visual Studio SDK.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-853558758462067355?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/nq0TifkUyAZe9nA99tLAtawZ6dw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/nq0TifkUyAZe9nA99tLAtawZ6dw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/nq0TifkUyAZe9nA99tLAtawZ6dw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/nq0TifkUyAZe9nA99tLAtawZ6dw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/Wh4cubAR0n4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/853558758462067355/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=853558758462067355" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/853558758462067355?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/853558758462067355?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/Wh4cubAR0n4/aspnet-mvc-view-engine-with-freemarker.html" title="ASP.NET MVC View Engine with FreeMarker" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2011/04/aspnet-mvc-view-engine-with-freemarker.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C04DQXc_eCp7ImA9WhZQFE0.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-4585632238552652972</id><published>2011-04-21T23:48:00.013+08:00</published><updated>2011-04-22T00:06:10.940+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-22T00:06:10.940+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="caching" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="System.Runtime.Caching" /><category scheme="http://www.blogger.com/atom/ns#" term="memory cache" /><category scheme="http://www.blogger.com/atom/ns#" term="entlib" /><category scheme="http://www.blogger.com/atom/ns#" term="4.0" /><title>Caching in .Net Framework 4</title><content type="html">We used to have caching in ASP.NET. For non-web application caching, Cache Application Block from Enterprise Library may be the choice. In .Net Framework 4, caching is baked into the library and no longer limited to ASP.NET.&lt;br /&gt;
&lt;br /&gt;
After adding System.Runtime.Caching assembly as the reference in your project, we can cache the data retrieved by a web services:&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;using System.Runtime.Caching;
class CrmHelper{
    static ObjectCache cache = MemoryCache.Default;
//...
    string key = string.Format("{0}/{1}", 
                          "account", "address1_addresstypecode");
    MetadataService service = new MetadataService();
    service.Credentials = CredentialCache.DefaultCredentials;

    Option[] options = null;
    if (cache.Contains(key)){
        options = (Option[])cache[key];
    else {
        var picklist = (PicklistAttributeMetadata)
            service.RetrieveAttributeMetadata(
                "account", "address1_addresstypecode");
        options = picklist.Options;
        cache.Set(key, options, new CacheItemPolicy {
            AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(2)
        });
    }
//...
}&lt;/pre&gt;&lt;code&gt;if (cache.Contains(key)) ... cache.Set(key, data)&lt;/code&gt; is going to be a repeating pattern. We could write an extension method to wrap it up:&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;public static class Extensions {
    public static T Get&amp;lt;T&amp;gt;(this ObjectCache cache, 
                           string key,
                           Func&amp;lt;T&amp;gt; retrieveFunction) {
        if (cache.Contains(key)) {
            return (T)cache[key];
        }
        else {
            var result = retrieveFunction();
            cache.Set(key, result, new CacheItemPolicy {
                AbsoluteExpiration = 
                    DateTimeOffset.Now.AddMinutes(2)
            });
            return result;
        }
    }
}
&lt;/pre&gt;The previous code is now cleaned up like this:&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;using System.Runtime.Caching;
class CrmHelper{
    static ObjectCache cache = MemoryCache.Default;
//...
    string key = string.Format("{0}/{1}", 
                          "account", "address1_addresstypecode");
    MetadataService service = new MetadataService();
    service.Credentials = CredentialCache.DefaultCredentials;&amp;nbsp;

    Option[] options = cache.Get(key, () =&amp;gt; {
        var picklist = (PicklistAttributeMetadata)
            service.RetrieveAttributeMetadata(
                "account", "address1_addresstypecode");
        return picklist.Options;
    });
//...
}
&lt;/pre&gt;It is worth to note that the Cache Application Block from Enterprise Library is deprecating (&lt;a href="http://msdn.microsoft.com/en-us/library/ff664753%28v=PandP.50%29.aspx" rel="nofollow"&gt;http://msdn.microsoft.com/en-us/library/ff664753(v=PandP.50).aspx&lt;/a&gt;):&lt;br /&gt;
&lt;blockquote&gt;Caching Application Block functionality is built into .NET Framework 4.0; therefore the Enterprise Library Caching Application Block will be deprecated in releases after 5.0. You should consider using the .NET 4.0 System.Runtime.Caching classes instead of the Caching Application Block in future development.&lt;/blockquote&gt;If you only want a quick memory cache, the new caching API might already fit your needs. However, Enterprise Library provides some other implementations of caching (e.g. database, file, etc). If you want something more fancy, Enterprise Library could be an alternative.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Refereces:&lt;/b&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/dd997357.aspx"&gt;Caching in .NET Framework Applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/dd941875.aspx"&gt;&amp;lt;system.runtime.caching&amp;gt; Element (Cache Settings)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.runtime.caching.aspx"&gt;System.Runtime.Caching Namespace&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-4585632238552652972?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/alFgDSWgVmGSZh3WK__5hlbtEp8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/alFgDSWgVmGSZh3WK__5hlbtEp8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/alFgDSWgVmGSZh3WK__5hlbtEp8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/alFgDSWgVmGSZh3WK__5hlbtEp8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/nY0Po7h0klE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/4585632238552652972/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=4585632238552652972" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/4585632238552652972?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/4585632238552652972?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/nY0Po7h0klE/caching-in-net-framework-4.html" title="Caching in .Net Framework 4" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2011/04/caching-in-net-framework-4.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4GQHY6eip7ImA9WhZSEUQ.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-5430566239727322892</id><published>2011-03-27T10:02:00.008+08:00</published><updated>2011-03-27T11:35:21.812+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-27T11:35:21.812+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="web.config" /><category scheme="http://www.blogger.com/atom/ns#" term="unity" /><category scheme="http://www.blogger.com/atom/ns#" term="configuration" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="channel factory" /><category scheme="http://www.blogger.com/atom/ns#" term="wcf" /><category scheme="http://www.blogger.com/atom/ns#" term="app.config" /><title>WCF Channel Factory and Unity 2.0</title><content type="html">&lt;a href="http://unity.codeplex.com/" rel="nofollow"&gt;Unity 2.0&lt;/a&gt; does not support &lt;a href="http://msdn.microsoft.com/en-us/library/ms576132(v=VS.100).aspx" rel="nofollow"&gt;ChannelFactory&lt;t&gt;&lt;/a&gt; in configuration natively. However, we could extend Unity. Inspired by &lt;a href="https://bitbucket.org/ctavares/unityfactoryconfig/src/63e446363b79/Unity.FactoryConfig/FactoryElement.cs" rel="nofollow"&gt;Chris Tavares's &lt;code&gt;FactoryElement&lt;/code&gt; extension&lt;/a&gt; for static factory configuration, here is the &lt;code&gt;ChannelFactory&lt;/code&gt; extension for WCF channel factory configuration in Unity.&lt;br /&gt;
&lt;br /&gt;
The element will get the generic type of the &lt;code&gt;ChannelFactory&lt;/code&gt; and calling the &lt;code&gt;CreateChannel()&lt;/code&gt; method upon the injection.&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;public class ChannelFactoryElement : InjectionMemberElement {
        private const string endpointConfigurationNamePropertyName = "endpointConfigurationName";
        private static int numFactories;
        private readonly int factoryNum;

        public ChannelFactoryElement() {
            factoryNum = Interlocked.Increment(ref numFactories);
        }

        [ConfigurationProperty(endpointConfigurationNamePropertyName, IsKey = false, IsRequired = true)]
        public string Name {
            get { return (string)base[endpointConfigurationNamePropertyName]; }
            set { base[endpointConfigurationNamePropertyName] = value; }
        }

        public override string Key {
            get { return "ChannelFactory " + factoryNum; }
        }

        public override IEnumerable&amp;lt;injectionmember&amp;gt; GetInjectionMembers(IUnityContainer container, Type fromType,
            Type toType, string name) {

            Type factoryType = typeof(ChannelFactory&amp;lt;&amp;gt;).MakeGenericType(toType);            
            var constructor = factoryType.GetConstructor(new Type[] { typeof(string) });
            var factory = constructor.Invoke(new object[]{Name});
            var method = factoryType.GetMethod("CreateChannel", Type.EmptyTypes);
            return new InjectionMember[] { new InjectionFactory(o =&amp;gt; method.Invoke(factory, null)) };
        }

    }
&lt;/pre&gt;&lt;br /&gt;
The extension will register the &lt;code&gt;factory&lt;/code&gt; as the element name of &lt;code&gt;ChannelFactoryElement&lt;/code&gt;.&lt;br /&gt;
&lt;pre class="c-sharp" name="code"&gt;public class ChannelFactoryConfigExtension : SectionExtension {
        public override void AddExtensions(SectionExtensionContext context) {
            context.AddElement&amp;lt;ChannelFactoryElement&amp;gt;("factory");
        }
    }
&lt;/pre&gt;&lt;br /&gt;
The &lt;code&gt;factory&lt;/code&gt; element uses &lt;code&gt;endpointConfigurationName&lt;/code&gt; to retrieve the client's endpoint as in &lt;a href="http://msdn.microsoft.com/en-us/library/ms574913.aspx" rel="nofollow"&gt;ChannelFactory&lt;tchannel&gt;(endpointConfigurationName)&lt;/a&gt; constructor.&lt;br /&gt;
&lt;pre class="xml" name="code"&gt;&amp;lt;unity xmlns="http://schemas.microsoft.com/practices/2010/unity"&amp;gt;
    &amp;lt;sectionExtension type="MyNamespace.ChannelFactoryConfigExtension, MyAssembly" /&amp;gt;
    &amp;lt;container&amp;gt;      
      &amp;lt;register type="MyNamespace.IMyService, MyAssembly"&amp;gt;
        &amp;lt;factory endpointConfigurationName="testing"/&amp;gt;
      &amp;lt;/register&amp;gt;
    &amp;lt;/container&amp;gt;
  &amp;lt;/unity&amp;gt;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-5430566239727322892?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/CQfDSrpq3uodcYSYNFLzDKfOt20/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/CQfDSrpq3uodcYSYNFLzDKfOt20/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/CQfDSrpq3uodcYSYNFLzDKfOt20/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/CQfDSrpq3uodcYSYNFLzDKfOt20/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/Wb_zyfTct6o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/5430566239727322892/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=5430566239727322892" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/5430566239727322892?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/5430566239727322892?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/Wb_zyfTct6o/wcf-channel-factory-and-unity-20.html" title="WCF Channel Factory and Unity 2.0" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2011/03/wcf-channel-factory-and-unity-20.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkEFR3o-eCp7ImA9WhZSEE4.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-7132238746255768583</id><published>2011-03-25T14:24:00.001+08:00</published><updated>2011-03-25T14:30:16.450+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-25T14:30:16.450+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="azure" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="ckeditor" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title>PHP-Azure Migration - Part 4</title><content type="html">Up to the last &lt;a href="http://killustar.blogspot.com/2011/03/php-azure-migration-part-3.html"&gt;post&lt;/a&gt; of the migration, we have done all data and file migration. How about the new files being uploaded from the CMS? &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;PHP&lt;/b&gt;&lt;br /&gt;
We are using a very old version of FCKEditor as our rich text editor. Since FCKEditor supports uploading file from the editor, we need to change the code in editor/filemanager/connectors/php/command.php. Here is an example of getting the folder and files for the file browser.&lt;br /&gt;
&lt;pre class="php" name="code"&gt;function GetFoldersAndFiles( $resourceType, $currentFolder ) {
        $sServerDir = MapAzureFolder( $resourceType, $currentFolder);
        $blobs = $blobStorageClient-&amp;gt;listBlobs($ContainerName, $sServerDir, '/');
 $aFolders = array();
 $aFiles = array();
 $folders = GetSessionFolders($resourceType, $currentFolder);
 foreach($blobs as $blob){
  $name = GetName($blob-&amp;gt;Name);
  if ($blob-&amp;gt;IsPrefix){
   $folders[$name] = 0;
  } else {     
   $iFileSize = $blob-&amp;gt;Size;
   if ( !$iFileSize ) {
    $iFileSize = 0 ;
   }
   if ( $iFileSize &amp;gt; 0 ) {
    $iFileSize = round( $iFileSize / 1024 ) ;
    if ( $iFileSize &amp;lt; 1 ) $iFileSize = 1 ;
   }
    $aFiles[] = '&amp;lt;File name="' . ConvertToXmlAttribute( $name ) . '" size="' . $iFileSize . '" /&amp;gt;' ;
  }
 }
 foreach($folders as $name =&amp;gt; $value){
  $aFolders[] = '&amp;lt;Folder name="' . ConvertToXmlAttribute( $name ) . '" /&amp;gt;' ;
 }
}
&lt;/pre&gt;It is worth to mention the GetSessionFolders() function. Azure Storage does not have concept of folder. All folders are actually derived by the paths of the files. So, when listing the blobs, we can use &lt;code&gt;$blob-&amp;gt;IsPrefix&lt;/code&gt; to distinguish the derived folders and the files.&lt;br /&gt;
&lt;br /&gt;
If a user want to create a folder and upload the file to the folder, we need to put the folder into the session. That is how GetSessionFolders() function come from.&lt;br /&gt;
&lt;br /&gt;
When upload a file, we have to handle the Flash format. It seems that modern browsers are intelligent enough to recognize image files like JPEG, GIF, PNG without the content type. When it comes to Flash file, it could not be played properly.&lt;br /&gt;
&lt;pre class="php" name="code"&gt;function FileUpload( $resourceType, $currentFolder, $sCommand ) {
...
 $sFileName = 'uploadedfiles/'. strtolower($resourceType).$currentFolder.$sFileName;
 $result = $blobStorageClient-&amp;gt;putBlob($ContainerName, $sFileName,  $oFile['tmp_name']);

 if(strtolower($resourceType) == 'flash') {
  $contentType = 'application/x-shockwave-flash';
  $blobStorageClient-&amp;gt;setBlobProperties($ContainerName,  $sFileName, null, array('x-ms-blob-content-type' =&amp;gt; $contentType));
 }
 $sFileUrl = $currentFolder.$sFileName;
...
}
&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;ASP.NET&lt;/b&gt;&lt;br /&gt;
For the ASP.NET portal, we have to do the same. Under /editor/filemanager/connectors/aspx/connector.aspx, we changed the first line to &lt;br /&gt;
&lt;pre class="html" name="code"&gt;&amp;lt;%@ Page Language="c#" Trace="false" Inherits="FCKAzureAdapter.Connector" AutoEventWireup="false" %&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
The connector is simply extending FredCK.FCKeditorV2.FileBrowser.Connector and override the GetFiles(), GetFolders(), and CreateFolder() methods with Azure API. Here is an example of GetFolders():&lt;br /&gt;
&lt;pre class="csharp" name="code"&gt;protected override void GetFolders(System.Xml.XmlNode connectorNode, string resourceType, string currentFolder) {
    string sServerDir = this.ServerMapFolder(resourceType, currentFolder);

    // Create the "Folders" node.
    XmlNode oFoldersNode = XmlUtil.AppendElement(connectorNode, "Folders");

    CloudBlobContainer container = GetCloudBlobContainer(containerPath);
    var dir = container.GetDirectoryReference(folder);

    var items = dir.ListBlobs();
    List&amp;lt;string&amp;gt; dirList = new List&amp;lt;string&amp;gt;();
    foreach (var x in items.OfType&amp;lt;CloudBlobDirectory&amp;gt;())
    {
        dirList.Add(x.Uri.ToString());
    }
    var dirs = dirList.Select(o =&gt; o.Substring(o.LastIndexOf('/', o.Length - 2)).Replace("/", "")).ToArray();

    Dictionary&amp;lt;string, List&amp;lt;string&amp;gt;&amp;gt; folderList = 
            HttpContext.Current.Session["Folders"] as Dictionary&amp;lt;string, List&amp;lt;string&amp;gt;&amp;gt;;
    foreach (var dir in dirs)
    {
        // Create the "Folder" node.
        XmlNode oFolderNode = XmlUtil.AppendElement(oFoldersNode, "Folder");
        var dirName = dir.Uri.GetName();
        XmlUtil.SetAttribute(oFolderNode, "name", dirName);
        if (folderList != null &amp;&amp; 
            folderList.ContainsKey(resourceType) 
            &amp;&amp; folderList[resourceType].Contains(dirName))
        {
            folderList[resourceType].Remove(dirName);
        }
    }

    if (folderList != null &amp;&amp; folderList.ContainsKey(resourceType))
    {
        foreach (var folder in folderList[resourceType].Where(o =&amp;gt; o != currentFolder &amp;&amp; o.StartsWith(currentFolder)))
        {
            var folderName = folder.Substring(currentFolder.Length).Trim('/');
            if (!folderName.Contains('/')) {
                // Create the "Folder" node.
                XmlNode oFolderNode = XmlUtil.AppendElement(oFoldersNode, "Folder");
                XmlUtil.SetAttribute(oFolderNode, "name", folderName);
            }
        }
    }
}&lt;/pre&gt;Then, we do the same for editor/filemanager/connectors/aspx/upload.aspx.&lt;br /&gt;
&lt;br /&gt;
Next time, we will discuss what other problems we have encountered.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-7132238746255768583?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/XR-TDii8htVJv736zoZ-soiRtNw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/XR-TDii8htVJv736zoZ-soiRtNw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/XR-TDii8htVJv736zoZ-soiRtNw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/XR-TDii8htVJv736zoZ-soiRtNw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/xUC0HejVMS0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/7132238746255768583/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=7132238746255768583" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/7132238746255768583?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/7132238746255768583?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/xUC0HejVMS0/php-azure-migration-part-4.html" title="PHP-Azure Migration - Part 4" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2011/03/php-azure-migration-part-4.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEMNQ3syeip7ImA9WhZTFkQ.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-6755327220225185257</id><published>2011-03-03T22:49:00.002+08:00</published><updated>2011-03-21T16:34:52.592+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-21T16:34:52.592+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="azure" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="storage" /><title>PHP-Azure Migration - Part 3</title><content type="html">In the previous &lt;a href="http://killustar.blogspot.com/2011/03/php-azure-migration-part-2.html"&gt;post&lt;/a&gt;, we have fixed all database queries to make the web site start working in SQL Server. The next task is tackling the file I/O without the file system. The file system is not a permanent storage in Windows Azure. We need to copy all resource files (file attachments of articles) from the local file system to Azure Storage.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;File Migration&lt;/b&gt;&lt;br /&gt;
The migration seemed quite straight forward. We can just copying the file to Azure Storage. However, there is no FTP or any batch utilities from Microsoft for batch upload. Given we have 4GB of files with some hierarchical directories, it is infeasible to the files one by one. We used &lt;a href="http://www.cerebrata.com/products/cloudstoragestudio/"&gt;Cloud Storage Studio&lt;/a&gt; for the migration.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;URL Redirection&lt;/b&gt;&lt;br /&gt;
The web site and the resource files are now in different servers. We can no longer use relative path. However, since the database is storing the relative path of the resource files, we either change the data (as well as semantics) to store the absolute URIs of the resource files; or redirect the browser to get the files from the correct URLs.&lt;br /&gt;
&lt;br /&gt;
As changing the data would involve a lot of testings in business logics, we decided to create a ASP.NET Module to redirect the requests. Given that&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;IIS (as well as Windows Azure) allow us to call ASP.NET Module in any web site (including PHP web site) &lt;/li&gt;
&lt;li&gt;All files were stored in a particular folder so we could derive the redirect pattern easily&lt;/li&gt;
&lt;/ol&gt;Now, all existing files have been migrated and will be redirected to new URL. Next time, let's take a look how to deal with new uploaded files.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-6755327220225185257?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/MlAht1CLJLphE5Wh38Vqoo9_g_g/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/MlAht1CLJLphE5Wh38Vqoo9_g_g/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/MlAht1CLJLphE5Wh38Vqoo9_g_g/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/MlAht1CLJLphE5Wh38Vqoo9_g_g/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/nYtQYP2fQqg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/6755327220225185257/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=6755327220225185257" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6755327220225185257?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6755327220225185257?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/nYtQYP2fQqg/php-azure-migration-part-3.html" title="PHP-Azure Migration - Part 3" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2011/03/php-azure-migration-part-3.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMEQX0_eip7ImA9WhZSEE4.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-1072245570059948470</id><published>2011-03-02T22:21:00.006+08:00</published><updated>2011-03-25T14:26:40.342+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-25T14:26:40.342+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="sql server" /><category scheme="http://www.blogger.com/atom/ns#" term="mysql" /><category scheme="http://www.blogger.com/atom/ns#" term="azure" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title>PHP-Azure Migration - Part 2</title><content type="html">In the &lt;a href="http://killustar.blogspot.com/2011/03/php-azure-migration-part-1.html"&gt;previous post&lt;/a&gt;, I have discussed the database migration from MySQL to SQL Azure. Data migration is only the first part of data processing, the next step is to modify the queries in the web site due the the syntax differences between two database systems.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Driver Adaption&lt;/b&gt;&lt;br /&gt;
In the ASP.NET frontend, we only need to replace the ADO.NET driver from MySQL to SQL Server driver and it is all done. In the PHP CMS, however, we will need to do some adaptation since PHP did not have a unified API for database driver.&lt;br /&gt;
&lt;br /&gt;
Lucky enough, it is not much work as there is a driver layer in the CMS. There were only three functions we need to change: query, fetch, total which corresponding to mysql_query, mysql_fetch_arry, and mysql_num_rows in MySQL driver and sqlsrv_query, sqlsrv_fetch_array, and sqlsrv_num_rows in SQL Server.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Query Modification&lt;/b&gt;&lt;br /&gt;
The major differences can be summarizes as follows:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Getting last inserted record identity&lt;/li&gt;
&lt;li&gt;Paging&lt;/li&gt;
&lt;li&gt;Scalar functions&lt;/li&gt;
&lt;li&gt;Aggregation functions&lt;/li&gt;
&lt;li&gt;Escaping strings&lt;/li&gt;
&lt;/ol&gt;There is a DAO layer in the CMS that storing all queries.&amp;nbsp; So, all we did is go through all queries in the DAOs and modify them.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;1. Getting last inserted record identity&lt;/b&gt;&lt;br /&gt;
MySQL PHP driver provides mysql_insert_id() to get the auto-increment column value of the last record inserted. There is no equivalent function in SQL Server PHP Driver. However, it is no more than just querying "@@IDENTITY" in SQL Server.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;2. Paging&lt;/b&gt;&lt;br /&gt;
While doing paging in MySQL only takes "LIMIT n OFFSET skip", SQL Server will need&lt;br /&gt;
&lt;pre class="sql" name="code"&gt;SELECT * FROM (
    SELECT TOP n * FROM (
        SELECT TOP z columns      -- (where z = n + skip)
        FROM table_name
        ORDER BY key ASC
    ) AS a ORDER BY key DESC
) AS b ORDER BY key ASC&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;3. Scalar functions&lt;/b&gt;&lt;br /&gt;
MySQL uses NOW() to get the current timestamp. SQL Server uses GETDATE().&lt;br /&gt;
MySQL provides RAND() so we could do random order. SQL Server provides NEWID(). Please note that NEWID() generate a GUID so the ordering is random. It does not mean NEWID() is same as RAND() in MySQL.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;4. Aggregation functions&lt;/b&gt;&lt;br /&gt;
MySQL provides many convenient aggregation functions like GROUP_CONCAT(). The queries have to be rewritten for SQL Server or we need to create the corresponding user defined aggregation functions.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;5. Escaping strings&lt;/b&gt;&lt;br /&gt;
Most developers use \' to escape single quote. In fact, both MySQL and SQL Server use two consecutive single quotes to escape a single quote. MySQL just accepting the other formats.&lt;br /&gt;
&lt;br /&gt;
Up to this moment, we discussing the data layer (if you have a data layer in PHP). There is no code change in the business layer yet. This is also an example that how we could modularize a program if we partition layers clearly regardless which platform we are using.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-1072245570059948470?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ZykFP3l4386GoJYL_d8uZRPWnHk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ZykFP3l4386GoJYL_d8uZRPWnHk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ZykFP3l4386GoJYL_d8uZRPWnHk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ZykFP3l4386GoJYL_d8uZRPWnHk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/z78R2Lm7UY0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/1072245570059948470/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=1072245570059948470" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/1072245570059948470?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/1072245570059948470?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/z78R2Lm7UY0/php-azure-migration-part-2.html" title="PHP-Azure Migration - Part 2" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2011/03/php-azure-migration-part-2.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8CQnk-eyp7ImA9WhZTFUU.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-6981027262607408792</id><published>2011-03-01T23:46:00.009+08:00</published><updated>2011-03-20T10:24:23.753+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-20T10:24:23.753+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="sql server" /><category scheme="http://www.blogger.com/atom/ns#" term="mysql" /><category scheme="http://www.blogger.com/atom/ns#" term="azure" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="ssma" /><category scheme="http://www.blogger.com/atom/ns#" term="sql azure" /><title>PHP-Azure Migration - Part 1</title><content type="html">Azure is Microsoft's cloud platform. The working models are different in a few ways. We migrated a web site to Azure. The web site consists of a PHP CMS, ASP.NET frontend, and a MySQL database.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Database Migration&lt;/b&gt;&lt;br /&gt;
There was some discussion among the team about whether using MySQL worker role or SQL Azure. As the final decision is SQL Azure, the first task we need to do is migrating the MySQL database to SQL Azure. The most obvious tools we can work on is Microsoft SQL Server Migration Assistant (SSMA) for MySQL.&lt;br /&gt;
&lt;br /&gt;
SSMA was working fine in SQL Server. It reconstructed the SQL Server equivalent schema from MySQL and copied the data from MySQL to SQL Server. However, there are a few catches when migrating to SQL Azure:&lt;br /&gt;
&lt;b&gt;1. Tables without primary keys:&lt;/b&gt;&lt;br /&gt;
For some reason, some tables in the web site did not have primary keys. The problem is a table in SQL Azure need a clustered index. No primary key means no clustered index (in SSMA sense). We have used a development SQL Server to re-create the schema first. Then, look for the tables without primary keys and add the primary keys back.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;2. Large records&lt;/b&gt;&lt;br /&gt;
The CMS designer has a concept that all data should be put into the database as the backup strategies. So, everything including images files, Flash video files, and video files  were stored in some attachment table as well. SSMA could handle most of them but not all. However, when the record is large (say 10MB), it will eventually failed and interrupted the how migration process. It is a very traditional timeout problem. We are just dealing with two:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Command timeout for SQL Server&lt;/li&gt;
&lt;li&gt;Idle timeout for MySQL&lt;/li&gt;
&lt;/ol&gt;Since we do not have control for timeout configuration and error handling strategies, we skip those tables during the migration. We dumped out the attachment into file systems. After a few tuning in the upload and download script to make the CMS working, we create a PHP script to migrate only the metadata columns in the attachment tables.&lt;br /&gt;
&lt;br /&gt;
To conclude, the migration were performed in a two steps:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Execute the SQL script to create the tables&lt;/li&gt;
&lt;li&gt;SSMA to copy most data&lt;/li&gt;
&lt;li&gt;Execute the PHP script to migrate the attachment metadata &lt;/li&gt;
&lt;/ol&gt;The performance was quite satisfactory.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-6981027262607408792?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/YH2lccaAKFxJpg7PsV8a6oeBBq0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YH2lccaAKFxJpg7PsV8a6oeBBq0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/YH2lccaAKFxJpg7PsV8a6oeBBq0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YH2lccaAKFxJpg7PsV8a6oeBBq0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/vduuQlu9QzY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/6981027262607408792/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=6981027262607408792" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6981027262607408792?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6981027262607408792?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/vduuQlu9QzY/php-azure-migration-part-1.html" title="PHP-Azure Migration - Part 1" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2011/03/php-azure-migration-part-1.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU8HQng-eip7ImA9Wx5aGE4.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-734451019041506339</id><published>2010-11-15T23:07:00.003+08:00</published><updated>2010-11-15T23:10:33.652+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-15T23:10:33.652+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="unity" /><category scheme="http://www.blogger.com/atom/ns#" term="jpa" /><category scheme="http://www.blogger.com/atom/ns#" term="jsp" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="jeans" /><category scheme="http://www.blogger.com/atom/ns#" term="interop" /><category scheme="http://www.blogger.com/atom/ns#" term="servlet" /><category scheme="http://www.blogger.com/atom/ns#" term="jndi" /><category scheme="http://www.blogger.com/atom/ns#" term="jasper" /><category scheme="http://www.blogger.com/atom/ns#" term="jetty" /><category scheme="http://www.blogger.com/atom/ns#" term="javax.sql.DataSource" /><category scheme="http://www.blogger.com/atom/ns#" term="ikvm" /><category scheme="http://www.blogger.com/atom/ns#" term="iis" /><category scheme="http://www.blogger.com/atom/ns#" term="tomcat" /><title>Jeans - Running Java Servlet and JSP Webapp in ASP.NET/IIS - Part 4</title><content type="html">Retrieving a database connection is one of the most popular problem in software development. Usually, we specify the connection string in a configure file and create the database connection with the connection string. The Java way is wrapping connection string in a javax.sql.DataSource interface and put in the application server config. The application server will put the DataSource instance into a JNDI context. So, this is what should be supported.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Bring in the Unity&lt;/b&gt;&lt;br /&gt;
Microsoft has a Dependency Injection container called Unity. Even though I am not doing dependency injection, I'd like to configure how to instantiate the data source in web.config. Instead of writing my own configuration classes, Unity is a good alternative as an object builder.&lt;br /&gt;
&lt;br /&gt;
Before doing any configuration, the JDBC driver has to be compiled into .Net assembly since it will be loaded by Unity instead of the Java class loader. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Method Injection&lt;/b&gt;&lt;br /&gt;
When configuring Unity, we could specify the methods to be called during the instance initialization. The first attempt:&lt;br /&gt;
&lt;code&gt;&amp;lt;method name="setURL"&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;param name="url" value="..." /&amp;gt;&lt;br /&gt;
&amp;lt;/method&amp;gt;&lt;/code&gt;&lt;br /&gt;
However, Unity was smart enough to know there is no such parameter name. I fired-off the object explorer to look the actual parameter name. It seemed that IKVM compiled all parameter name with str, str1, str2, etc. The configuration has to be like this:&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;&amp;lt;method name="setURL"&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;param name="str" value="..." /&amp;gt;&lt;br /&gt;
&amp;lt;/method&amp;gt;&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;JNDI Context&lt;/b&gt;&lt;br /&gt;
Having known that a data source with the name jdbc/testDS is the same as java:comp/env/jdbc/testDS, I tried to mimic the behavior with the reference of this &lt;a href="http://suppressingfire.livejournal.com/43281.html"&gt;article&lt;/a&gt;. It turned out the result is an exception:&lt;br /&gt;
&lt;code&gt;javax.persistence.PersistenceException: Exception [EclipseLink-7060] (Eclipse Persistence Services - 2.0.2.v20100323-r6872): org.eclipse.persistence.exceptions.ValidationException&lt;br /&gt;
Exception Description: Cannot acquire data source [jdbc/testDS].&lt;br /&gt;
Internal Exception: javax.naming.NameNotFoundException: Name jdbc is not bound in this Context&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:399)&lt;/code&gt;&lt;br /&gt;
So, I reverted to the simpliest way by just creating the jdbc subcontext in the initial context.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Password&lt;/b&gt;&lt;br /&gt;
The configuration was set. Time to run the test. It did not work:&lt;br /&gt;
&lt;code&gt;javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.2.v20100323-r6872): org.eclipse.persistence.exceptions.DatabaseException&lt;br /&gt;
Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: Login failed for user 'jsptest'.&lt;br /&gt;
Error Code: 18456&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:399)&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
Well, the password was copied from persistence and very simple:&lt;br /&gt;
&lt;code&gt;&amp;lt;method name="setPassword"&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;param name="str" value="passw0rd" /&amp;gt;&lt;br /&gt;
&amp;lt;/method&amp;gt;&lt;/code&gt;&lt;br /&gt;
Not sure what happened but when the user and password were put in the URL, it worked.&lt;br /&gt;
&lt;br /&gt;
The new source code has been checked-in in &lt;a href="http://jeans.codeplex.com/"&gt;http://jeans.codeplex.com/&lt;/a&gt;. The persistence.xml&amp;nbsp;has changed to use the &amp;lt;non-jta-data-source&amp;gt;jdbc/testDS&amp;lt;/non-jta-data-source&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Note: sqljdbc4.jar has been removed from the source repository. Please download it from &lt;a href="http://msdn.microsoft.com/en-us/sqlserver/aa937724.aspx"&gt;Microsoft&lt;/a&gt; and compile it with IKVM to run the JSP demo site.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-734451019041506339?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/zixfKNXhrV4W3uVLZEwrNC0mbtk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zixfKNXhrV4W3uVLZEwrNC0mbtk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/zixfKNXhrV4W3uVLZEwrNC0mbtk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zixfKNXhrV4W3uVLZEwrNC0mbtk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/0AdV6GuHPxw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/734451019041506339/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=734451019041506339" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/734451019041506339?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/734451019041506339?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/0AdV6GuHPxw/jeans-running-java-servlet-and-jsp_9347.html" title="Jeans - Running Java Servlet and JSP Webapp in ASP.NET/IIS - Part 4" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2010/11/jeans-running-java-servlet-and-jsp_9347.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEUCRnwyeip7ImA9Wx5aF0g.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-2944686135947558661</id><published>2010-11-15T00:31:00.000+08:00</published><updated>2010-11-15T00:31:07.292+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-15T00:31:07.292+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="jsp" /><category scheme="http://www.blogger.com/atom/ns#" term="filter" /><category scheme="http://www.blogger.com/atom/ns#" term="jeans" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="interop" /><category scheme="http://www.blogger.com/atom/ns#" term="servlet" /><category scheme="http://www.blogger.com/atom/ns#" term="ikvm" /><category scheme="http://www.blogger.com/atom/ns#" term="tomcat" /><category scheme="http://www.blogger.com/atom/ns#" term="iis" /><category scheme="http://www.blogger.com/atom/ns#" term="jasper" /><title>Jeans - Running Java Servlet and JSP Webapp in ASP.NET/IIS - Part 3</title><content type="html">Developing Java webapp, we use of Filter for cache control, compression, database connection and transaction handling. So, Filter is another feature I need to support.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Original Thought&lt;/b&gt;&lt;br /&gt;
In ASP.NET, IHttpModule is like a filter while IHttpHandler is like a servlet. It seemed obvious to modify the existing ServletModule to execute the filter.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Platform Mismatch&lt;/b&gt;&lt;br /&gt;
The first problem I found is the async design of ASP.NET. The IHttpModule is an event sink and need to implement the event handlers for events like BeginRequest, EndRequest. In contrast, Servlet Filter is using FilterChain to call the filter one by one. I tried to execute the filter chain in BeginRequest event. However, I won't be able to gain the access of Session. Having google around, I found the comprehensive events execute sequence in IHttpModule &lt;a href="http://www.blogger.com/%28http://msdn.microsoft.com/en-us/library/ms178473.aspx"&gt;(http://msdn.microsoft.com/en-us/library/ms178473.aspx&lt;/a&gt;):&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;BeginRequest&lt;/li&gt;
&lt;li&gt;AuthenticateRequest&lt;/li&gt;
&lt;li&gt;PostAuthenticateRequest&lt;/li&gt;
&lt;li&gt;AuthorizeRequest&lt;/li&gt;
&lt;li&gt;PostAuthorizeRequest&lt;/li&gt;
&lt;li&gt;ResolveRequestCache&lt;/li&gt;
&lt;li&gt;PostResolveRequestCache&lt;/li&gt;
&lt;li&gt;PostMapRequestHandler&lt;/li&gt;
&lt;li&gt;AcquireRequestState&lt;/li&gt;
&lt;li&gt;PostAcquireRequestState&lt;/li&gt;
&lt;li&gt;PreRequestHandlerExecute&lt;/li&gt;
&lt;li&gt;PostRequestHandlerExecute&lt;/li&gt;
&lt;li&gt;ReleaseRequestState&lt;/li&gt;
&lt;li&gt;PostReleaseRequestState&lt;/li&gt;
&lt;li&gt;UpdateRequestCache&lt;/li&gt;
&lt;li&gt;PostUpdateRequestCache&lt;/li&gt;
&lt;li&gt;EndRequest&lt;/li&gt;
&lt;li&gt;PreSendRequestHeaders&lt;/li&gt;
&lt;li&gt;PreSendRequestContent&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;/ul&gt;To get the session, the module needs to implement an event handler for PostRequestHandlerExecute event. In order to get the Session, HttpContext.Current.Handler has to be implementing IRequiresSessionState as well.&lt;br /&gt;
&lt;br /&gt;
There is code to wrap the HttpContext.Current.Handler around (&lt;a href="http://stackoverflow.com/questions/276355/can-i-access-session-state-from-an-httpmodule"&gt;http://stackoverflow.com/questions/276355/can-i-access-session-state-from-an-httpmodule&lt;/a&gt;). It worked fine but it does not solve the async problem.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;From Module to Handler&lt;/b&gt;&lt;br /&gt;
I managed to run the filter chain in PreRequestHandlerExecute event. It turned out that not only it run the JSP code, but also the source code of the JSP was also append to the result by the default handler. Response.End() has to be invoked. And then some checking for the whether it is my own servlet handler, etc. The code is a bit weird.&lt;br /&gt;
&lt;br /&gt;
Since it does not matter where the filter chain is executed, I moved to code to ServletHandler and revert the changes in ServletModule, it compiled and seemed working with the Hello World JSP. No more Response.End() and checking. Async problem is solved since executing the filter and servlet are in one place. It matched the semantics of Java webapp.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Testing&lt;/b&gt;&lt;br /&gt;
The simplest way to test is to get a filter that GZIP the output and use fiddler to see if the response is compressed. I grabbed filter from &lt;a href="http://onjava.com/pub/a/onjava/2003/11/19/filters.html"&gt;http://onjava.com/pub/a/onjava/2003/11/19/filters.html&lt;/a&gt;. Added the setting in web.xml. Somehow it does not work. It turned out it's because the default XML namespace in web.xml generated by Eclipse caused the problem. Removing the namespace made enabled the support of filter.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;XML Namespace&lt;/b&gt;&lt;br /&gt;
The default XML namespace is quite annoying. I am not sure why it is there as it is meaningless. I tried a few way to remove it with the .Net XML API but the namespace somehow stayed there. The final resort was invoking string.Replace() to get rid of it.&lt;br /&gt;
&lt;br /&gt;
The latest source code has been checked-in in &lt;a href="http://jeans.codeplex.com/"&gt;http://jeans.codeplex.com/&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-2944686135947558661?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/UzkY58CzCBwMhDtSpYDd07gEvCI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UzkY58CzCBwMhDtSpYDd07gEvCI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/UzkY58CzCBwMhDtSpYDd07gEvCI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UzkY58CzCBwMhDtSpYDd07gEvCI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/sg1vuATdr-4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/2944686135947558661/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=2944686135947558661" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/2944686135947558661?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/2944686135947558661?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/sg1vuATdr-4/jeans-running-java-servlet-and-jsp_15.html" title="Jeans - Running Java Servlet and JSP Webapp in ASP.NET/IIS - Part 3" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2010/11/jeans-running-java-servlet-and-jsp_15.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4NQHo_fip7ImA9Wx5aFUo.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-3579544313978445073</id><published>2010-11-13T00:03:00.001+08:00</published><updated>2010-11-13T00:06:31.446+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-13T00:06:31.446+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="jpa" /><category scheme="http://www.blogger.com/atom/ns#" term="jetty" /><category scheme="http://www.blogger.com/atom/ns#" term="jsp" /><category scheme="http://www.blogger.com/atom/ns#" term="jeans" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="interop" /><category scheme="http://www.blogger.com/atom/ns#" term="servlet" /><category scheme="http://www.blogger.com/atom/ns#" term="ikvm" /><category scheme="http://www.blogger.com/atom/ns#" term="tomcat" /><category scheme="http://www.blogger.com/atom/ns#" term="iis" /><category scheme="http://www.blogger.com/atom/ns#" term="jasper" /><title>Jeans - Running Java Servlet and JSP Webapp in ASP.NET/IIS - Part 2</title><content type="html">One of my mentors said, "If you do not write SQL, you cannot do application development." Even though the statement is not quite true as there are ORM frameworks handling the database communication, the statement emphasizes the importance of database. So, running a pure Hello World JSP is a poor proof of concept to ASP.NET. Running a database connected JSP is more meaningful.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Preparation&lt;/b&gt;&lt;br /&gt;
The following components were gathered in this phase:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;JPA - the current standard of doing ORM in Java.&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;&amp;nbsp;EclipseLink is the reference implementation. It should serve the purpose well.&lt;/li&gt;
&lt;/ul&gt;&lt;li&gt;SQL Server 2008 Express - As ASP.NET is being used anyway, why don't we work in the whole Microsoft ecosystem? &lt;/li&gt;
&lt;ul&gt;&lt;li&gt;SQL Server JDBC Driver - Yes, I need a JDBC Driver in .Net... &lt;/li&gt;
&lt;/ul&gt;&lt;li&gt;AdventureWorksLT - Microsoft used to provide sample databases that works with SQL Server. AdventureWorksLT is the simplified version.&lt;/li&gt;
&lt;/ul&gt;One bottleneck I have is I have no JPA experience. I used Hibernate before but JPA is a standard. The problem of being standard is there are many implementations. The only way to understand it is to read the specification. Otherwise, it's hard to understand how it works in general so that it works for most (if not all) implementations.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;META-INF/persistence.xml&lt;/b&gt;&lt;br /&gt;
One of the challenges encountered was loading the persistence.xml. Luckily, IKVM can load the resource files with by specifying -classloader:ikvm.runtime.ClassPathAssemblyClassLoader when the compiling the class files and JAR files into .Net assembly. However, since all files were compiled into a single DLL. Only the first persistence.xml IKVM read can managed to be the resource.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;META-INF/orm.xml&lt;/b&gt;&lt;br /&gt;
The problem of compiling everything into .Net assembly is losing all annotations in the classes. In the other words, you must use META-INF/orm.xml and initialize it in an IHttpModule.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Rework&lt;/b&gt;&lt;br /&gt;
The limitation of loading the two XML files is quite nasty. It would not feasible for medium size application that might have a few JAR files containing persistence.xml. Creating orm.xml manually is quite a pain as you know the information is in the classes already. To increase the flexbility, ClassPathAssemblyClassLoader is is wrapped by an URLClassLoader with the following classpaths:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;WEB-INF\classes&amp;nbsp;&lt;/li&gt;
&lt;li&gt;All JAR files in WEB-INF\lib directory&lt;/li&gt;
&lt;/ol&gt;Then, it is invoked to the current thread's context class loader to load the JAR and the precompiled JSP classes directly. It works!&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Dual Mode&lt;/b&gt;&lt;br /&gt;
Now, &lt;a href="http://jeans.codeplex.com/"&gt;Jeans&lt;/a&gt; support two ways to run a Java webapp:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Native mode - With everything compiled into a single DLL&lt;/li&gt;
&lt;li&gt;WAR mode - Run a standard Java webapp directory whose structure is the same as in WAR file given:&lt;/li&gt;
&lt;ol type="a"&gt;&lt;li&gt; JSP are precompiled into servlet and put into WEB-INF/classes folder.&lt;/li&gt;
&lt;li&gt;JSP Servlet mappings are stored in jspweb.xml to complement the original web.xml.&lt;/li&gt;
&lt;/ol&gt;&lt;/ol&gt;The consequence is quite obvious. In native mode, it takes around 50MB of memory to load the Hello World page. In WAR mode, it takes around 100MB of memory the run the same page. No benchmark for the application performance but I believed native mode should run faster.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-3579544313978445073?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/VMT9uor_MT3kycr4dlJpQaUWLcM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/VMT9uor_MT3kycr4dlJpQaUWLcM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/VMT9uor_MT3kycr4dlJpQaUWLcM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/VMT9uor_MT3kycr4dlJpQaUWLcM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/srrHUQpJCFM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/3579544313978445073/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=3579544313978445073" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/3579544313978445073?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/3579544313978445073?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/srrHUQpJCFM/jeans-running-java-servlet-and-jsp_13.html" title="Jeans - Running Java Servlet and JSP Webapp in ASP.NET/IIS - Part 2" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2010/11/jeans-running-java-servlet-and-jsp_13.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkYBR3c6fCp7ImA9Wx5aFEQ.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-6214933434176515026</id><published>2010-11-12T01:55:00.000+08:00</published><updated>2010-11-12T01:55:56.914+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-12T01:55:56.914+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="jetty" /><category scheme="http://www.blogger.com/atom/ns#" term="jsp" /><category scheme="http://www.blogger.com/atom/ns#" term="jeans" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="interop" /><category scheme="http://www.blogger.com/atom/ns#" term="servlet" /><category scheme="http://www.blogger.com/atom/ns#" term="ikvm" /><category scheme="http://www.blogger.com/atom/ns#" term="tomcat" /><category scheme="http://www.blogger.com/atom/ns#" term="iis" /><category scheme="http://www.blogger.com/atom/ns#" term="jasper" /><title>Jeans - Running Java Servlet and JSP Webapp in ASP.NET/IIS - Part 1</title><content type="html">There are two main camps in enterprise server technology market, Java and .Net Framework. Java side has Servlet, JSP, and Java ServerFace. .Net Framework side has ASP.NET (WebForm and MVC). Both sides are not compatible with each other. This article will describe how to run JSP in IIS natively without connecting to a Java web container.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;IKVM&lt;/b&gt;&lt;br /&gt;
Mr. Jeroen Frijters has implemented an JVM in .NET, IKVM.NET. It seems to be a good start.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Jetty vs. Tomcat&lt;/b&gt;&lt;br /&gt;
The first attempt was compiling Jetty to execute web applications directly. Digging into the source code and after a few trials, I found that Jetty is using Jasper library, which is Tomcat's JSP engine, to run JSP. Given that .Net does not have the concept of class file, it is unlikely .NET is able to load the create new class and load it into memory in runtime.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;JSP Precompiler&lt;/b&gt;&lt;br /&gt;
Jasper not only is the runtime engine of JSP, it also includes a JSP precompiler to compile JSP into Servlet. Using it is not hard but type the Java command is tedious. It makes sense to use IKVM to create a .Net executable. The Jasper library from Tomcat 5.5 is used:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;ant.jar&lt;/li&gt;
&lt;li&gt;catalina-ant.jar&lt;/li&gt;
&lt;li&gt;commons-el.jar&lt;/li&gt;
&lt;li&gt;commons-logging-api-1.1.1.jar&lt;/li&gt;
&lt;li&gt;jasper-compiler-jdt.jar&lt;/li&gt;
&lt;li&gt;jasper-compiler.jar&lt;/li&gt;
&lt;li&gt;jasper-runtime.jar&lt;/li&gt;
&lt;li&gt;jsp-api.jar&lt;/li&gt;
&lt;li&gt;naming-factory-dbcp.jar&lt;/li&gt;
&lt;li&gt;naming-factory.jar&lt;/li&gt;
&lt;li&gt;naming-resources.jar&lt;/li&gt;
&lt;li&gt;servlet-api.jar &lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Then compile the precompiler with this command:&lt;br /&gt;
&lt;code&gt;ikvmc -classloader:ikvm.runtime.ClassPathAssemblyClassLoader -target:exe -main:org.apache.jasper.JspC -out:bin\jspc.exe *.jar&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;JSP Compilation&lt;/b&gt;&lt;br /&gt;
The command-line arguments of JspC is the same as the Java version. The following command can be used (%1 is the directory of the target web application):&lt;br /&gt;
&lt;code&gt;jspc -uriroot "%1" -d out -compile -v -webxml "%1\WEB-INF\jspweb.xml" -trimSpaces -source 1.5 -target 1.5&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;The JSP&lt;/b&gt;&lt;br /&gt;
The first JSP is a simple Hello World page to test out the JSTL EL:&lt;br /&gt;
Hello ${param.name}!&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Web Application Compilation&lt;/b&gt;&lt;br /&gt;
Everything are still in JARs and classes. However .Net Framework needs DLLs. The following command is executed to get everything in .Net assembly:&lt;br /&gt;
&lt;code&gt;ikvmc -classloader:ikvm.runtime.ClassPathAssemblyClassLoader -recurse:%1\WEB-INF\classes -out:%1\bin\jsp.dll -reference:%1\bin\jspc.exe %1\WEB-INF\lib\*.jar&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Servlet API, in .Net&lt;/b&gt;&lt;br /&gt;
Before running the DLL, the following classes are necessary in the Servlet API to run the JSP:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;HttpServletRequest/Response&lt;/li&gt;
&lt;li&gt;ServletInputStream/OutputStream&lt;/li&gt;
&lt;li&gt;ServletRequestDispatcher&lt;/li&gt;
&lt;li&gt;ServletContext&lt;/li&gt;
&lt;li&gt;ServletConfig &lt;/li&gt;
&lt;/ul&gt;and a few more adapters to implement Java I/O.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;ASP.NET IHttpModule and IHttpHandler&lt;/b&gt;&lt;br /&gt;
The final bit is the ASP.NET module to initiate the configuration and the handler to execute the servlets/JSP.&lt;br /&gt;
&lt;br /&gt;
I have made the source code available so you don't need to go through the whole process. Please visit &lt;a href="http://jeans.codeplex.com/"&gt;http://jeans.codeplex.com/&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-6214933434176515026?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/GPgR0300yBSe8jX01VD2YGtyDFs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/GPgR0300yBSe8jX01VD2YGtyDFs/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/GPgR0300yBSe8jX01VD2YGtyDFs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/GPgR0300yBSe8jX01VD2YGtyDFs/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/AblmaaUQHAk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/6214933434176515026/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=6214933434176515026" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6214933434176515026?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6214933434176515026?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/AblmaaUQHAk/jeans-running-java-servlet-and-jsp.html" title="Jeans - Running Java Servlet and JSP Webapp in ASP.NET/IIS - Part 1" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://killustar.blogspot.com/2010/11/jeans-running-java-servlet-and-jsp.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkMCQH4-fCp7ImA9WxFXEkU.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-3923101135017036049</id><published>2010-05-20T01:01:00.000+08:00</published><updated>2010-05-20T01:01:01.054+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-20T01:01:01.054+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="video" /><category scheme="http://www.blogger.com/atom/ns#" term="browser war" /><category scheme="http://www.blogger.com/atom/ns#" term="html5" /><title>Irrational Decision</title><content type="html">Problem: HTML 5 supports &amp;lt;video&amp;gt; tag. However, standard codec that all browser vendors can agree with.&lt;/video&gt;&lt;/video&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Opera: Let's do it in Theora.&lt;/li&gt;
&lt;li&gt;MS, Apple: H.264 is industrial standard.&lt;/li&gt;
&lt;li&gt;Mozilla Foundation: H.264 is not free. Theora is free.&lt;/li&gt;
&lt;li&gt;Google: Let me buy out On2 and open it.&lt;/li&gt;
&lt;li&gt;Opera, Mozilla Foundation: OK, let's do it in On2, then.&lt;/li&gt;
&lt;li&gt;MS, Apple: ...&lt;/li&gt;
&lt;/ul&gt;Now, Opera, Firefox and Chrome will support On2. IE and Safari will still only support H.264. We still do not have the common standard codec for the &amp;lt;video&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
Question: MS and Apple were not willing to implement Theora, why they will implement On2? Why the hell Google bought out On2 in the first place?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-3923101135017036049?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/zVpcP2KMmb4v4HkzFtBxecZSPxA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zVpcP2KMmb4v4HkzFtBxecZSPxA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/zVpcP2KMmb4v4HkzFtBxecZSPxA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zVpcP2KMmb4v4HkzFtBxecZSPxA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/f3kFJzeI5us" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/3923101135017036049/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=3923101135017036049" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/3923101135017036049?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/3923101135017036049?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/f3kFJzeI5us/irrational-decision.html" title="Irrational Decision" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2010/05/irrational-decision.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkUNQX0_fip7ImA9WxBXEE0.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-404590942214915405</id><published>2010-01-21T00:11:00.006+08:00</published><updated>2010-01-21T00:18:10.346+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-21T00:18:10.346+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="sql" /><category scheme="http://www.blogger.com/atom/ns#" term="sql server" /><category scheme="http://www.blogger.com/atom/ns#" term="null" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="linq" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><category scheme="http://www.blogger.com/atom/ns#" term="sorting" /><title>Sorting NULL LAST in LINQ-to-SQL</title><content type="html">There might be some time that you need to put NULL value into last when sorting a result set. In SQL Server, you can do something like this:&lt;br /&gt;&lt;pre name="code" class="sql"&gt;&lt;br /&gt;SELECT value&lt;br /&gt;FROM foo&lt;br /&gt;ORDER BY (CASE WHEN x IS NULL THEN 1 ELSE 0 END), x&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You might want to ask the mapping of LINQ-to-SQL. Here you go:&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;&lt;br /&gt;foo.OrderBy(i =&gt; i.x == null ? 1 : 0)&lt;br /&gt;   .ThenBy(i =&gt; i.x);&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-404590942214915405?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/szNmi3NhdHcuTjoMWt96q6dU5PY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/szNmi3NhdHcuTjoMWt96q6dU5PY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/szNmi3NhdHcuTjoMWt96q6dU5PY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/szNmi3NhdHcuTjoMWt96q6dU5PY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/HVrIiMXlQjA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/404590942214915405/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=404590942214915405" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/404590942214915405?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/404590942214915405?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/HVrIiMXlQjA/null-last-in-sql-to-linq.html" title="Sorting NULL LAST in LINQ-to-SQL" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://killustar.blogspot.com/2010/01/null-last-in-sql-to-linq.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0QHQnw5cCp7ImA9WxBREU0.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-6727693585511405537</id><published>2009-12-28T00:51:00.010+08:00</published><updated>2009-12-29T23:42:13.228+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-29T23:42:13.228+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="web hosting" /><category scheme="http://www.blogger.com/atom/ns#" term="hostmonster issue" /><title>Web Hosting Experience</title><content type="html">I have recently changed by web hosting from HostMonster to Awardspace. HostMonster served me well these two years until when it was about to renew a month ago. I got notified that my my account got suspended because of "spam/phishing". After checking with the customer service, the reason was some "suspicious" files were found in my web site content.&lt;br /&gt;&lt;br /&gt;The suspicious files are actually the executables of my personal projects: the JavaScript obfuscator (JSO) and the solution to convert base64 images in data URI to MIME (Base64Html).&lt;br /&gt;&lt;br /&gt;As the projects contributes quite a lot of traffic to the website, I immediately asked for the reason of suspension. They explained that there are viruses found in the executables, blah, blah, blah... After I explained the purpose of my files and verified the MD5 were the same as in my computer, they re-activated the websites.&lt;br /&gt;&lt;br /&gt;Everything sounds OK but it already took &lt;span style="font-weight:bold;"&gt;two days&lt;/span&gt;! I believe there are better ways to handle my case:&lt;br /&gt;1. Why did they suspend all my websites? The files are only in one website.&lt;br /&gt;2. Why the whole web sites were get suspended at all? The suspicious files can be redirected to some other &lt;span style="font-weight:bold;"&gt;safe&lt;/span&gt; URL.&lt;br /&gt;3. Why spam/phishing turned out to be a possible virus notification? Why I had to ask to know the real reason of getting suspended? If they cannot distinguish the difference between phishing and virus, it is very unprofessional.&lt;br /&gt;4. There was one statement in the last email I received made me very angry: "&lt;span style="font-weight:bold;"&gt;The report that we received was from a reputable web security company, so we did take action based on their report.&lt;/span&gt;" Why couldn't they send me the report to let me take action before suspending my account (thus, suspending all websites)? The action taken is not customer friendly. Suspending account should be the last measure, not the first.&lt;br /&gt;&lt;br /&gt;After this incident, I took some time to shop around. I used Awardspace free hosting before so it sounds OK for me. The price is reasonable and it supports ASP.NET. Not sure if it is a good choice since the websites have just migrated for a few days. I did have some trouble on transferring the domain but the customer service responded quickly and nicely.&lt;br /&gt;&lt;br /&gt;I might have part 2 of this post to share the experience of website migrations.&lt;br /&gt;&lt;br /&gt;P.S.: HostMonster does have a tracked record of over sensitive scam alert (use "oversensitive scam alert" as the keyword to search in Google). So, if you are not living in US (like me), you might get into the same trouble.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-6727693585511405537?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/PHz81l2odao6CyKQc7BCPbrMFuY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/PHz81l2odao6CyKQc7BCPbrMFuY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/PHz81l2odao6CyKQc7BCPbrMFuY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/PHz81l2odao6CyKQc7BCPbrMFuY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/oIrF_p5ywr0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/6727693585511405537/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=6727693585511405537" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6727693585511405537?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6727693585511405537?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/oIrF_p5ywr0/web-hosting.html" title="Web Hosting Experience" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2009/12/web-hosting.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEcMRXo7eSp7ImA9WxNWEUs.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-6740097721108262531</id><published>2009-10-10T01:45:00.020+08:00</published><updated>2009-10-10T16:08:04.401+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-10T16:08:04.401+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="private key" /><category scheme="http://www.blogger.com/atom/ns#" term="cryptography" /><category scheme="http://www.blogger.com/atom/ns#" term="cryptoapi" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="pkcs12" /><category scheme="http://www.blogger.com/atom/ns#" term="pfx" /><title>Accessing Private Key from PFX (PKCS#12) in .Net Framework</title><content type="html">You may know that .Net Framework has X509Certificate2 class to read X.509 certificate and obtain the public key from the certificate:&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;&lt;br /&gt;var cert = new X509Certificate2("my.cer");&lt;br /&gt;var key = cert.PublicKey.Key as RSACryptoServiceProvider;&lt;br /&gt;var cipher = key.Encrypt(Encoding.UTF8.GetBytes(txtClear.Text), false);&lt;br /&gt;txtCipher.Text = Convert.ToBase64String(cipher);&lt;/pre&gt;&lt;br /&gt;However, you may not know that X509Certificate2 class can also read the first private key from a PFX file without CryptoAPI:&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;&lt;br /&gt;var cert = new X509Certificate2("my.pfx", "password");&lt;br /&gt;var key = cert.PrivateKey as RSACryptoServiceProvider;&lt;br /&gt;var clear = Convert.FromBase64String(txtCipher.Text);&lt;br /&gt;txtClear.Text = Encoding.UTF8.GetString(key.Decrypt(clear, false));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To read all private keys from PFX, use PKCS12.Read() method below. It uses CryptoAPI and it will return an array of X509Certificate2. You can get the private key to decrypt the cipher as shown above. The full sample code can be download at &lt;a href="http://www.shaneng.net/Main/PFXCSPDOTNET"&gt;Shane's Shelf&lt;/a&gt;.&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.IO;&lt;br /&gt;using System.Runtime.InteropServices;&lt;br /&gt;using System.Security.Cryptography.X509Certificates;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;&lt;br /&gt;namespace X509Cert&lt;br /&gt;{&lt;br /&gt;    public class PKCS12&lt;br /&gt;    {&lt;br /&gt;        public static X509Certificate2[] Read(string filename, string password)&lt;br /&gt;        {&lt;br /&gt;&lt;br /&gt;            FileStream stream = new FileStream(filename, FileMode.Open);&lt;br /&gt;            byte[] buffer = new byte[stream.Length];&lt;br /&gt;            stream.Read(buffer, 0, buffer.Length);&lt;br /&gt;            stream.Close();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;            WIN32.CRYPT_DATA_BLOB cryptdata = new WIN32.CRYPT_DATA_BLOB();&lt;br /&gt;            cryptdata.cbData = buffer.Length;&lt;br /&gt;            cryptdata.pbData = Marshal.AllocHGlobal(cryptdata.cbData);&lt;br /&gt;            Marshal.Copy(buffer, 0, cryptdata.pbData, buffer.Length);&lt;br /&gt;            IntPtr hMemStore = WIN32.PFXImportCertStore(ref cryptdata, password, WIN32.CRYPT_USER_KEYSET);&lt;br /&gt;            Marshal.FreeHGlobal(cryptdata.pbData);&lt;br /&gt;&lt;br /&gt;            uint provinfosize = 0;&lt;br /&gt;&lt;br /&gt;            List&amp;lt;X509Certificate2&amp;gt; certs = new List&amp;lt;X509Certificate2&amp;gt;();&lt;br /&gt;&lt;br /&gt;            IntPtr certHandle = IntPtr.Zero;&lt;br /&gt;            while ((certHandle = WIN32.CertEnumCertificatesInStore(hMemStore, certHandle)) != IntPtr.Zero)&lt;br /&gt;            {&lt;br /&gt;&lt;br /&gt;                if (WIN32.CertGetCertificateContextProperty(certHandle, WIN32.CERT_KEY_PROV_INFO_PROP_ID, IntPtr.Zero, ref provinfosize))&lt;br /&gt;                {&lt;br /&gt;&lt;br /&gt;                    IntPtr info = Marshal.AllocHGlobal((int)provinfosize);&lt;br /&gt;&lt;br /&gt;                    if (WIN32.CertGetCertificateContextProperty(certHandle, WIN32.CERT_KEY_PROV_INFO_PROP_ID, info, ref provinfosize))&lt;br /&gt;                    {&lt;br /&gt;                        var certData = new X509Certificate2(certHandle).Export(X509ContentType.SerializedCert);&lt;br /&gt;                        certs.Add(new X509Certificate2(certData));&lt;br /&gt;                    }&lt;br /&gt;                    Marshal.FreeHGlobal(info);&lt;br /&gt;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            Marshal.FreeHGlobal(hMemStore);&lt;br /&gt;            return certs.ToArray();&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public class WIN32&lt;br /&gt;    {&lt;br /&gt;        public const uint CRYPT_USER_KEYSET = 0x00001000;&lt;br /&gt;        public const uint CERT_KEY_PROV_INFO_PROP_ID = 0x00000002;&lt;br /&gt;&lt;br /&gt;        [DllImport("crypt32.dll", SetLastError = true)]&lt;br /&gt;        public static extern IntPtr PFXImportCertStore(ref CRYPT_DATA_BLOB pPfx, [MarshalAs(UnmanagedType.LPWStr)] String szPassword, uint dwFlags);&lt;br /&gt;&lt;br /&gt;        [DllImport("CRYPT32.DLL", EntryPoint = "CertEnumCertificatesInStore", CharSet = CharSet.Auto, SetLastError = true)]&lt;br /&gt;        public static extern IntPtr CertEnumCertificatesInStore(IntPtr storeProvider, IntPtr prevCertContext);&lt;br /&gt;&lt;br /&gt;        [DllImport("CRYPT32.DLL", CharSet = CharSet.Auto, SetLastError = true)]&lt;br /&gt;        public static extern bool CertGetCertificateContextProperty(IntPtr pCertContext, uint dwPropId, IntPtr pvData, ref uint pcbData);&lt;br /&gt;&lt;br /&gt;        [DllImport("advapi32.dll", EntryPoint = "CryptAcquireContext", CharSet = CharSet.Auto, SetLastError = true)]&lt;br /&gt;        public static extern bool CryptAcquireContext(ref IntPtr phProv, string szContainer, string szProvider, uint dwProvType, uint dwFlags);&lt;br /&gt;&lt;br /&gt;        [StructLayout(LayoutKind.Sequential)]&lt;br /&gt;        public struct CRYPT_DATA_BLOB&lt;br /&gt;        {&lt;br /&gt;            public int cbData;&lt;br /&gt;            public IntPtr pbData;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public WIN32()&lt;br /&gt;        {&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above code is altered base on http://www.cnblogs.com/rainlake/archive/2005/09/15/237997.html&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-6740097721108262531?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/JulQzsOCJO1EkyNwGzsJ2RzO1Z4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/JulQzsOCJO1EkyNwGzsJ2RzO1Z4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/JulQzsOCJO1EkyNwGzsJ2RzO1Z4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/JulQzsOCJO1EkyNwGzsJ2RzO1Z4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/TuVNw5AMKMs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/6740097721108262531/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=6740097721108262531" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6740097721108262531?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6740097721108262531?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/TuVNw5AMKMs/accessing-private-key-from-pfx-pkcs12.html" title="Accessing Private Key from PFX (PKCS#12) in .Net Framework" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2009/10/accessing-private-key-from-pfx-pkcs12.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEIDRHc9fip7ImA9WxNbF0k.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-7961143808597720971</id><published>2009-05-21T21:55:00.010+08:00</published><updated>2009-11-21T02:42:55.966+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-21T02:42:55.966+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="mxhr" /><category scheme="http://www.blogger.com/atom/ns#" term="base64" /><category scheme="http://www.blogger.com/atom/ns#" term="mhtml" /><category scheme="http://www.blogger.com/atom/ns#" term="ajax" /><title>MXHR</title><content type="html">Digg is creating a multipart capable XHR library. Good try but not very useful. Here is why:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;MXHR response is not compressed by default.&lt;br /&gt;This is illustrated very well in their text only &lt;a href="http://demos.digg.com/stream/streamDemo.html"&gt;demo&lt;/a&gt;. MXHR stream is always slower than normal mode. I opened up the packet sniffer. The normal stream is compressed while the MXHR is not.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Data URI scheme is broken.&lt;br /&gt;The web is broken by IE for a long time. IE6 and IE7 will be still around for a few years. Image data cannot be served in cross-browser manner. The same performance can be achieved by spriting: putting the tiny-images into one image (overhead is small if the image is large enough), preload the combined imaged, and use background-position to get the coordination.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Multipart is not streaming&lt;br /&gt;Well, it is under DUI.Stream namespace. However, it is not streaming. The server is packing up the data into one response. The library is processing the data on-the-fly but the received data is accumulating.&lt;br /&gt;1. If the size of the response is not large (i.e. can be stream in a couple seconds), enveloping the message in JSON or XML could be easier.&lt;br /&gt;2. If the size of the response is large (i.e. comet style), the responseText is going to become very large. We call it memory leak.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;HTTP is broken because it is not intended to serve for Web 2.0. That's why there are so many facilities in CSS and JavaScript to patch the protocol. MXHR is definitely not the answer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-7961143808597720971?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/4xYO7YQt-zkSXGrIbUjvLl3wLjk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4xYO7YQt-zkSXGrIbUjvLl3wLjk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/4xYO7YQt-zkSXGrIbUjvLl3wLjk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4xYO7YQt-zkSXGrIbUjvLl3wLjk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/2u3dQP9Yw0w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/7961143808597720971/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=7961143808597720971" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/7961143808597720971?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/7961143808597720971?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/2u3dQP9Yw0w/mxhr.html" title="MXHR" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2009/05/mxhr.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4HRHc7cSp7ImA9WxJRGE8.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-6676309438938145569</id><published>2009-05-04T23:19:00.011+08:00</published><updated>2009-05-20T21:58:55.909+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-20T21:58:55.909+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="e4x" /><title>E4X Alternative - JSOM</title><content type="html">I have been expecting the popularity of E4X for a long time. However, as long as Internet Explorer is the most popular browser, "new" standards will never get popular (Microsoft is suffered from Windows XP legacy, too). Instead of waiting for E4X, some developers use XPath to query the DOM nodes while the other traverse the DOM node by node. However, it would be good if we could access XML content in JavaScript Object Model (JSOM):&lt;br /&gt;&lt;pre name="code" class="javascript"&gt;&lt;br /&gt;xml='&amp;lt;invoice date="01-20-2000" number="123"&amp;gt;' +&lt;br /&gt;    '  &amp;lt;address country="US"&amp;gt;' +&lt;br /&gt;    '    &amp;lt;name&amp;gt;John Smith&amp;lt;/name&amp;gt;' +&lt;br /&gt;    '    &amp;lt;street&amp;gt;123 George St.&amp;lt;/street&amp;gt;' +&lt;br /&gt;    '    &amp;lt;city&amp;gt;Mountain View&amp;lt;/city&amp;gt;' +&lt;br /&gt;    '    &amp;lt;state&amp;gt;CA&amp;lt;/state&amp;gt;' +&lt;br /&gt;    '    &amp;lt;zip&amp;gt;94041&amp;lt;/zip&amp;gt;' +&lt;br /&gt;    '  &amp;lt;/address&amp;gt;' +&lt;br /&gt;    '&amp;lt;/invoice&amp;gt;';&lt;br /&gt;invoice = JSOM(xml);&lt;br /&gt;alert(invoice.$number);&lt;br /&gt;alert(invoice.address.name);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There is a project, IEE4X, in SourceForge for the same purpose but it does not work quite well for a few cases. So, I grabbed the idea and implement the JSOM library myself. The code can be download at &lt;a href="http://www.shaneng.net/Main/JSOM"&gt;Shane's Shelf&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-6676309438938145569?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/_qub77Bz58OtRZGZlGW9k7ryJxc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_qub77Bz58OtRZGZlGW9k7ryJxc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/_qub77Bz58OtRZGZlGW9k7ryJxc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_qub77Bz58OtRZGZlGW9k7ryJxc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/_9SmR3li9A0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/6676309438938145569/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=6676309438938145569" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6676309438938145569?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6676309438938145569?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/_9SmR3li9A0/e4x-alternative-jsom.html" title="E4X Alternative - JSOM" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2009/05/e4x-alternative-jsom.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUANQnsyeyp7ImA9WxJTE04.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-2001753411850698870</id><published>2009-04-22T00:02:00.009+08:00</published><updated>2009-04-22T01:09:53.593+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-22T01:09:53.593+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ajax" /><category scheme="http://www.blogger.com/atom/ns#" term="xml" /><category scheme="http://www.blogger.com/atom/ns#" term="json" /><title>XML vs. JSON - Size Comparison</title><content type="html">I was wondering how compact is JSON comparing to XML. I grabbed an arbitrary XML from the web and convert it to an equivalent JSON format. It turns out that JSON is around two-third of the XML size.&lt;br /&gt;&lt;br /&gt;&lt;table class="MsoTableProfessional" style="border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0"&gt;  &lt;tbody&gt;&lt;tr style="page-break-inside: avoid;"&gt;   &lt;td style="padding: 0in 5.4pt; width: 386.35pt;" valign="top" width="644"&gt;   &lt;p class="MsoNormal"&gt;&lt;span lang="EN-US"&gt;XML: 596 bytes&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;   &lt;/td&gt;   &lt;td style="padding: 0in 5.4pt; width: 386.35pt;" valign="top" width="644"&gt;   &lt;p class="MsoNormal"&gt;&lt;span lang="EN-US"&gt;JSON: 402 bytes&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;   &lt;/td&gt;  &lt;/tr&gt;  &lt;tr style="page-break-inside: avoid;"&gt;   &lt;td style="padding: 0in 5.4pt; width: 386.35pt;" valign="top" width="644"&gt;&lt;pre&gt;&lt;span lang="EN-US"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span lang="EN-US"&gt;&amp;lt;message&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 12pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;header&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 24pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;to&amp;gt;companyReceiver&amp;lt;/to&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 24pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;from&amp;gt;companySender&amp;lt;/from&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 24pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;type&amp;gt;saveInvoice&amp;lt;/type&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 12pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;/header&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 12pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;info&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 24pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;saveInvoice&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 0.5in;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;invoice date="01-20-2000" number="123"&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;address country="US"&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;name&amp;gt;John Smith&amp;lt;/name&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;street&amp;gt;123 George St.&amp;lt;/street&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;city&amp;gt;Mountain View&amp;lt;/city&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;state&amp;gt;CA&amp;lt;/state&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;zip&amp;gt;94041&amp;lt;/zip&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;/address&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;billTo country="US"&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;name&amp;gt;Company A&amp;lt;/name&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;street&amp;gt;100 Main St.&amp;lt;/street&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;city&amp;gt;Washington&amp;lt;/city&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;state&amp;gt;DC&amp;lt;/state&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;zip&amp;gt;20015&amp;lt;/zip&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;/billTo&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;items&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;item number="1"&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 1in;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;name&amp;gt;IBM A20 Laptop&amp;lt;/name&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 1in;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;quantity&amp;gt;1&amp;lt;/quantity&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 1in;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;USPrice&amp;gt;2000.00&amp;lt;/USPrice&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;/item&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;/items&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;/invoice&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 24pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;/saveInvoice&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 12pt;"&gt;&lt;span lang="EN-US"&gt;&amp;lt;/info&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span lang="EN-US"&gt;&amp;lt;/message&amp;gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td style="padding: 0in 5.4pt; width: 386.35pt;" valign="top" width="644"&gt;&lt;pre&gt;&lt;span lang="EN-US"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span lang="EN-US"&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 12pt;"&gt;&lt;span lang="EN-US"&gt;header:{&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 24pt;"&gt;&lt;span lang="EN-US"&gt;to:"companyReceiver",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 24pt;"&gt;&lt;span lang="EN-US"&gt;from:"companySender",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 24pt;"&gt;&lt;span lang="EN-US"&gt;type:"saveInvoice”&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 12pt;"&gt;&lt;span lang="EN-US"&gt;},&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 12pt;"&gt;&lt;span lang="EN-US"&gt;info:{&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 24pt;"&gt;&lt;span lang="EN-US"&gt;saveInvoice:{&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 0.5in;"&gt;&lt;span lang="EN-US"&gt;invoice:{date:"01-20-2000,number:"123",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;address:{country:"US",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;name:"John Smith",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;street:"&lt;st1:street st="on"&gt;&lt;st1:address st="on"&gt;123 George St&lt;/st1:address&gt;&lt;/st1:street&gt;.",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;city:"&lt;st1:city st="on"&gt;&lt;st1:place st="on"&gt;Mountain View&lt;/st1:place&gt;&lt;/st1:city&gt;",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;state:"CA",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;zip:"94041"&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;},&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;billTo:{country="US",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;name:"Company A",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;street:"&lt;st1:street st="on"&gt;&lt;st1:address st="on"&gt;100 Main St&lt;/st1:address&gt;&lt;/st1:street&gt;.",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;city:"&lt;st1:state st="on"&gt;&lt;st1:place st="on"&gt;Washington&lt;/st1:place&gt;&lt;/st1:state&gt;",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;state:"DC",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;zip:"20015"&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;},&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;items:[&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;{&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 1in;"&gt;&lt;span lang="EN-US"&gt;name:"IBM A20 Laptop",&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 1in;"&gt;&lt;span lang="EN-US"&gt;quantity:1,&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 1in;"&gt;&lt;span lang="EN-US"&gt;USPrice:2000.00&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 60pt;"&gt;&lt;span lang="EN-US"&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 48pt;"&gt;&lt;span lang="EN-US"&gt;]&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 0.5in;"&gt;&lt;span lang="EN-US"&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 24pt;"&gt;&lt;span lang="EN-US"&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-left: 12pt;"&gt;&lt;span lang="EN-US"&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span lang="EN-US"&gt;}&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;  &lt;/tr&gt; &lt;/tbody&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-2001753411850698870?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/EynlJobHR1jVzYzsNtLdhygbAoU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EynlJobHR1jVzYzsNtLdhygbAoU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/EynlJobHR1jVzYzsNtLdhygbAoU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EynlJobHR1jVzYzsNtLdhygbAoU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/y3ne3OPatdQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/2001753411850698870/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=2001753411850698870" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/2001753411850698870?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/2001753411850698870?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/y3ne3OPatdQ/xml-vs-json-size.html" title="XML vs. JSON - Size Comparison" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://killustar.blogspot.com/2009/04/xml-vs-json-size.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4MQXk_eip7ImA9WxJRGE8.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-7571806877572054221</id><published>2009-03-14T11:53:00.010+08:00</published><updated>2009-05-20T21:59:40.742+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-20T21:59:40.742+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="http" /><category scheme="http://www.blogger.com/atom/ns#" term="cookies" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="wcf" /><title>Secure WCF Services with Authentication Service</title><content type="html">We can use WCF Authentication Service to authenticate users with ASP.NET membership provider. However, other WCF services are not protected by the authentication service out-of-the-box. That is, WCF services is not using ASP.NET forms authentication.&lt;br /&gt;&lt;br /&gt;Fortunately, it is not hard to enable it. The magic point is set the HttpContext.Current.User in Global.asax&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;public class Global : System.Web.HttpApplication&lt;br /&gt;  {&lt;br /&gt;      // other methods snipped...&lt;br /&gt;      protected void Application_AuthenticateRequest(object sender, EventArgs e)&lt;br /&gt;      {&lt;br /&gt;          HttpCookie ticketCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];&lt;br /&gt;          if (null == ticketCookie)&lt;br /&gt;          {&lt;br /&gt;              return;&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(ticketCookie.Value);&lt;br /&gt;          if (null != ticket)&lt;br /&gt;          {&lt;br /&gt;              HttpContext.Current.User = new GenericPrincipal(new FormsIdentity(ticket), null);&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;  }&lt;/pre&gt;In the service you want to protect, set the requirement mode to allowed or required.&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]&lt;br /&gt;   public class PrimeService : IPrimeService&lt;/pre&gt;Then, throw in the following checking at the beginning of the method.&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;            if (!HttpContext.Current.User.Identity.IsAuthenticated)&lt;br /&gt;          {&lt;br /&gt;              throw new FaultException&amp;lt;SecurityAccessDeniedException&amp;gt;(new SecurityAccessDeniedException());&lt;br /&gt;          }&lt;/pre&gt;That would be good enough for Silverlight client. For .Net WCF client, you need to handle the HTTP cookies by yourself (Authentication Service is using the authentication ticket in cookies). Detail discussion can be found in the &lt;a href="http://www.shaneng.net/Main/SecureWCFServicesWithAuthenticationService"&gt;article&lt;/a&gt; in Shane's Shelf.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-7571806877572054221?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/adPDfVmznMMxSS26-QsR3rnBDl4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/adPDfVmznMMxSS26-QsR3rnBDl4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/adPDfVmznMMxSS26-QsR3rnBDl4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/adPDfVmznMMxSS26-QsR3rnBDl4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/rfWzrgpWiF0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/7571806877572054221/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=7571806877572054221" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/7571806877572054221?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/7571806877572054221?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/rfWzrgpWiF0/secure-wcf-services-with-authentication.html" title="Secure WCF Services with Authentication Service" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://killustar.blogspot.com/2009/03/secure-wcf-services-with-authentication.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcARn8_cSp7ImA9WxJRGE8.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-8103540787412224205</id><published>2009-03-03T02:21:00.014+08:00</published><updated>2009-05-20T22:00:47.149+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-20T22:00:47.149+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="http" /><category scheme="http://www.blogger.com/atom/ns#" term="post" /><category scheme="http://www.blogger.com/atom/ns#" term="silverlight" /><category scheme="http://www.blogger.com/atom/ns#" term="multipart" /><category scheme="http://www.blogger.com/atom/ns#" term="form" /><category scheme="http://www.blogger.com/atom/ns#" term="upload" /><title>Silverlight Multi-part File Upload Form Post</title><content type="html">Silverlight not actually very web friendly. It lacks of built-in support for the current HTML form post protocol. So, we have to write it our own. I have written an extension class and two serializer classes, one for normal form post (&lt;code&gt;DataContractQueryStringSerializer&lt;/code&gt;) while the other for multipart upload form post (&lt;code&gt;DataContractMultiPartSerializer&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;If you only want to have the working code, just copy the code below. Detail explanation is available at &lt;a href="http://www.shaneng.net/Main/Multi-PartFormPost"&gt;Multi-Part Form Post&lt;/a&gt; in Shane's Shelf&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;&lt;br /&gt;  public static class Extensions&lt;br /&gt;  {&lt;br /&gt;      public static void PostFormAsync(this HttpWebRequest request, object parameters, AsyncCallback callback)&lt;br /&gt;      {&lt;br /&gt;          request.Method = "POST";&lt;br /&gt;          request.ContentType = "application/x-www-form-urlencoded";&lt;br /&gt;          request.BeginGetRequestStream(new AsyncCallback(asyncResult =&amp;gt;&lt;br /&gt;          {&lt;br /&gt;              Stream stream = request.EndGetRequestStream(asyncResult);&lt;br /&gt;              DataContractQueryStringSerializer ser = new DataContractQueryStringSerializer();&lt;br /&gt;              ser.WriteObject(stream, parameters);&lt;br /&gt;              stream.Close();&lt;br /&gt;              request.BeginGetResponse(callback, request);&lt;br /&gt;          }), request);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public static void PostMultiPartAsync(this HttpWebRequest request, object parameters, AsyncCallback callback)&lt;br /&gt;      {&lt;br /&gt;          request.Method = "POST";&lt;br /&gt;          string boundary = "---------------" + DateTime.Now.Ticks.ToString();&lt;br /&gt;          request.ContentType = "multipart/form-data; boundary=" + boundary;&lt;br /&gt;          request.BeginGetRequestStream(new AsyncCallback(asyncResult =&amp;gt;&lt;br /&gt;          {&lt;br /&gt;              Stream stream = request.EndGetRequestStream(asyncResult);&lt;br /&gt;&lt;br /&gt;              DataContractMultiPartSerializer ser = new DataContractMultiPartSerializer(boundary);&lt;br /&gt;              ser.WriteObject(stream, parameters);&lt;br /&gt;              stream.Close();&lt;br /&gt;              request.BeginGetResponse(callback, request);&lt;br /&gt;          }), request);&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public class DataContractQueryStringSerializer&lt;br /&gt;  {&lt;br /&gt;      public void WriteObject(Stream stream, object data)&lt;br /&gt;      {&lt;br /&gt;          StreamWriter writer = new StreamWriter(stream);&lt;br /&gt;          if (data != null)&lt;br /&gt;          {&lt;br /&gt;              if (data is Dictionary&amp;lt;string, string&amp;gt;)&lt;br /&gt;              {&lt;br /&gt;                  foreach (var entry in data as Dictionary&amp;lt;string, string&amp;gt;)&lt;br /&gt;                  {&lt;br /&gt;                      writer.Write("{0}={1}&amp;amp;", entry.Key, entry.Value);&lt;br /&gt;                  }&lt;br /&gt;              }&lt;br /&gt;              else&lt;br /&gt;              {&lt;br /&gt;                  foreach (var prop in data.GetType().GetFields())&lt;br /&gt;                  {&lt;br /&gt;                      foreach (var attribute in prop.GetCustomAttributes(true))&lt;br /&gt;                      {&lt;br /&gt;                          if (attribute is DataMemberAttribute)&lt;br /&gt;                          {&lt;br /&gt;                              DataMemberAttribute member = attribute as DataMemberAttribute;&lt;br /&gt;                              writer.Write("{0}={1}&amp;amp;", member.Name ?? prop.Name, prop.GetValue(data));&lt;br /&gt;                          }&lt;br /&gt;                      }&lt;br /&gt;                  }&lt;br /&gt;                  foreach (var prop in data.GetType().GetProperties())&lt;br /&gt;                  {&lt;br /&gt;                      if (prop.CanRead)&lt;br /&gt;                      {&lt;br /&gt;                          foreach (var attribute in prop.GetCustomAttributes(true))&lt;br /&gt;                          {&lt;br /&gt;                              if (attribute is DataMemberAttribute)&lt;br /&gt;                              {&lt;br /&gt;                                  DataMemberAttribute member = attribute as DataMemberAttribute;&lt;br /&gt;                                  writer.Write("{0}={1}&amp;amp;", member.Name ?? prop.Name, prop.GetValue(data, null));&lt;br /&gt;                              }&lt;br /&gt;                          }&lt;br /&gt;                      }&lt;br /&gt;                  }&lt;br /&gt;              }&lt;br /&gt;              writer.Flush();&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public class DataContractMultiPartSerializer&lt;br /&gt;  {&lt;br /&gt;      private string boundary;&lt;br /&gt;      public DataContractMultiPartSerializer(string boundary)&lt;br /&gt;      {&lt;br /&gt;          this.boundary = boundary;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      private void WriteEntry(StreamWriter writer, string key, object value)&lt;br /&gt;      {&lt;br /&gt;          if (value != null)&lt;br /&gt;          {&lt;br /&gt;              writer.Write("--");&lt;br /&gt;              writer.WriteLine(boundary);&lt;br /&gt;              if (value is FileInfo)&lt;br /&gt;              {&lt;br /&gt;                &lt;br /&gt;                  FileInfo f = value as FileInfo;&lt;br /&gt;                  writer.WriteLine(@"Content-Disposition: form-data; name=""{0}""; filename=""{1}""", key, f.Name);&lt;br /&gt;                  writer.WriteLine("Content-Type: application/octet-stream");&lt;br /&gt;                  writer.WriteLine("Content-Length: " + f.Length);&lt;br /&gt;                  writer.WriteLine();&lt;br /&gt;                  writer.Flush();&lt;br /&gt;                  Stream output = writer.BaseStream;&lt;br /&gt;                  Stream input = f.OpenRead();&lt;br /&gt;                  byte[] buffer = new byte[4096];&lt;br /&gt;                  for (int size = input.Read(buffer, 0, buffer.Length); size &amp;gt; 0; size = input.Read(buffer, 0, buffer.Length))&lt;br /&gt;                  {&lt;br /&gt;                      output.Write(buffer, 0, size);&lt;br /&gt;                  }&lt;br /&gt;                  output.Flush();&lt;br /&gt;                  writer.WriteLine();&lt;br /&gt;              }&lt;br /&gt;              else&lt;br /&gt;              {&lt;br /&gt;                  writer.WriteLine(@"Content-Disposition: form-data; name=""{0}""", key);&lt;br /&gt;                  writer.WriteLine();&lt;br /&gt;                  writer.WriteLine(value.ToString());&lt;br /&gt;              }&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      public void WriteObject(Stream stream, object data)&lt;br /&gt;      {&lt;br /&gt;          StreamWriter writer = new StreamWriter(stream);&lt;br /&gt;          if (data != null)&lt;br /&gt;          {&lt;br /&gt;              if (data is Dictionary&amp;lt;string, object&amp;gt;)&lt;br /&gt;              {&lt;br /&gt;                  foreach (var entry in data as Dictionary&amp;lt;string, object&amp;gt;)&lt;br /&gt;                  {&lt;br /&gt;                      WriteEntry(writer, entry.Key, entry.Value);&lt;br /&gt;                  }&lt;br /&gt;              }&lt;br /&gt;              else&lt;br /&gt;              {&lt;br /&gt;                  foreach (var prop in data.GetType().GetFields())&lt;br /&gt;                  {&lt;br /&gt;                      foreach (var attribute in prop.GetCustomAttributes(true))&lt;br /&gt;                      {&lt;br /&gt;                          if (attribute is DataMemberAttribute)&lt;br /&gt;                          {&lt;br /&gt;                              DataMemberAttribute member = attribute as DataMemberAttribute;&lt;br /&gt;                              WriteEntry(writer, member.Name ?? prop.Name, prop.GetValue(data));&lt;br /&gt;                          }&lt;br /&gt;                      }&lt;br /&gt;                  }&lt;br /&gt;                  foreach (var prop in data.GetType().GetProperties())&lt;br /&gt;                  {&lt;br /&gt;                      if (prop.CanRead)&lt;br /&gt;                      {&lt;br /&gt;                          foreach (var attribute in prop.GetCustomAttributes(true))&lt;br /&gt;                          {&lt;br /&gt;                              if (attribute is DataMemberAttribute)&lt;br /&gt;                              {&lt;br /&gt;                                  DataMemberAttribute member = attribute as DataMemberAttribute;&lt;br /&gt;                                  WriteEntry(writer, member.Name ?? prop.Name, prop.GetValue(data, null));&lt;br /&gt;                              }&lt;br /&gt;                          }&lt;br /&gt;                      }&lt;br /&gt;                  }&lt;br /&gt;              }&lt;br /&gt;          }&lt;br /&gt;          writer.Write("--");&lt;br /&gt;          writer.Write(boundary);&lt;br /&gt;          writer.WriteLine("--");&lt;br /&gt;          writer.Flush();&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;&lt;/string,&gt;&lt;/string,&gt;&lt;/string,&gt;&lt;/string,&gt;&lt;/pre&gt;The usage is as follows:&lt;br /&gt;First a PHP file&lt;br /&gt;&lt;pre name="code" class="php"&gt;&lt;br /&gt;&amp;lt;?php&lt;br /&gt;print_r($_REQUEST);&lt;br /&gt;$src = $_FILES['y']['tmp_name'];&lt;br /&gt;$dest = "C:\\Windows\\Temp\\".$_FILES['y']['name'];&lt;br /&gt;echo $src;&lt;br /&gt;echo "\r\n";&lt;br /&gt;echo $dest;&lt;br /&gt;echo @copy($src, $dest);&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;/pre&gt;Then the Page control&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;    public partial class Page : UserControl&lt;br /&gt;  {&lt;br /&gt;      public Page()&lt;br /&gt;      {&lt;br /&gt;          InitializeComponent();&lt;br /&gt;          // Create a request object&lt;br /&gt;          HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri("http://localhost/rms/test.php"));&lt;br /&gt;          OpenFileDialog dlg = new OpenFileDialog();&lt;br /&gt;          if (dlg.ShowDialog().Value)&lt;br /&gt;          {&lt;br /&gt;              request.PostMultiPartAsync(new Dictionary&amp;lt;string, object&amp;gt; { { "x", "1" }, { "y", dlg.File } }, new AsyncCallback(asyncResult =&amp;gt;&lt;br /&gt;              {&lt;br /&gt;                  HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);&lt;br /&gt;&lt;br /&gt;                  Stream responseStream = response.GetResponseStream();&lt;br /&gt;                  StreamReader reader = new StreamReader(responseStream);&lt;br /&gt;                  this.Dispatcher.BeginInvoke(delegate&lt;br /&gt;                  {&lt;br /&gt;                      // output is a TextBlock&lt;br /&gt;                      output.Text = reader.ReadToEnd();&lt;br /&gt;                      response.Close();&lt;br /&gt;                  });&lt;br /&gt;              }));&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;  }&lt;/string,&gt;&lt;/pre&gt;Since it is able to serialize data contract, you could actually replace&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;new Dictionary&amp;lt;string, object&amp;gt; { { "x", "1" }, { "y", dlg.File } }&lt;/string,&gt;&lt;/pre&gt;&lt;br /&gt;with&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;new Point(){X=1, Y=2}&lt;/pre&gt;&lt;br /&gt;given the point class is like this:&lt;br /&gt;&lt;pre name="code" class="c-sharp"&gt;    [DataContract]&lt;br /&gt;  public class Point&lt;br /&gt;  {&lt;br /&gt;      [DataMember]&lt;br /&gt;      public int X { get; set; }&lt;br /&gt;      [DataMember(Name="y")]&lt;br /&gt;      public int Y { get; set; }&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-8103540787412224205?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/eLkEvbmg9wwLpi5YLMa5RgYXIUM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/eLkEvbmg9wwLpi5YLMa5RgYXIUM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/eLkEvbmg9wwLpi5YLMa5RgYXIUM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/eLkEvbmg9wwLpi5YLMa5RgYXIUM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/Z8548crVt6Y" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/8103540787412224205/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=8103540787412224205" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/8103540787412224205?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/8103540787412224205?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/Z8548crVt6Y/silverlight-multi-part-form-post.html" title="Silverlight Multi-part File Upload Form Post" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://killustar.blogspot.com/2009/03/silverlight-multi-part-form-post.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkAMRH0yfyp7ImA9WxRaFU8.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-4879172717525213529</id><published>2008-12-17T22:37:00.001+08:00</published><updated>2008-12-17T22:39:45.397+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-17T22:39:45.397+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="windows" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="iis" /><title>Slow Performance of PHP in Windows IIS</title><content type="html">There are a few reasons that PHP runs slowly in Windows. Not because of the request delegation from IIS to PHP via FastCGI or CGI. In fact, the delegation is quite robust in FastCGI.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://forums.iis.net/t/1153459.aspx"&gt;TCP/IP connection problem between database and web server&lt;/a&gt; may be a reason. Some Windows specific behaviors may be another. One of the Windows specific behaviors is the invocation of gethostbyaddr() function.&lt;br /&gt;&lt;br /&gt;Here is the high level behavior of gethostbyaddr() function making the invocation slow:&lt;br /&gt;&lt;a href="http://forums11.itrc.hp.com/service/forums/questionanswer.do?threadId=28927"&gt;Original&lt;/a&gt;&lt;br /&gt;&lt;span class="Text"&gt;&lt;blockquote&gt;            gethostbyaddr() on windows waits for WINS resolution timeout even when you disable WINS on the server.&lt;br /&gt;&lt;br /&gt;This means that your DNS server may return "No hostname found" and windows gethostbyaddr() will just sit there, never having asked a WINS server anything, for 3 - 4 seconds. I watched it do just that with a sniffer.&lt;/blockquote&gt;&lt;/span&gt;Here is the root cause:&lt;br /&gt;&lt;a href="http://www.codeguru.com/cpp/i-n/internet/network/comments.php/c6239/?thread=66186"&gt;Original&lt;/a&gt;&lt;br /&gt;&lt;blockquote&gt;Difference between gethostbyaddr and getnameinfo.&lt;br /&gt;&lt;br /&gt;Originally posted by: Gregor The Eye&lt;br /&gt;&lt;br /&gt;First at all, sorry for my english. It's not my native language, and, unfortunately, i havn't time to verify all text i enter.&lt;br /&gt;&lt;br /&gt;Now, to subject. Computer may have many "names", and two main name groups of names computers have is a "DNS" names and a "NetBIOS" names.&lt;br /&gt;&lt;br /&gt;The "NetBIOS" name is aname you assign to your computer using system-&gt;network identification-&gt;network ID. This name&lt;br /&gt;is displayed in you "network neighborhood" and generally used in local networks to identify computers.&lt;br /&gt;&lt;br /&gt;The "DNS" name is a name given to machine in internet. This name is a synonim to machine IP (becouse IP is\s not huma-friendly). "DNS" name is stored in special internet services (so-called "DNS" servers), not at the computer itself. Generally, computer can be shut off, but it's DNS name will be available and produce correct IP.&lt;br /&gt;&lt;br /&gt;For example, if I install OS to new computer and call it "EYE", it will have a NetBIOS name "EYE", and, connecting it to my local network, i can access it in "network neighborhood". But, from the internet, i can't access to it using "EYE" name, and instead i must use it's IP - for example - 113.54.25.14. But if i need over users to access it from internet using a human-friendly name, i pay amount of money to sertain organization, and they register a name for it - for example - "www.GregorTheEye.com". Then, user can access my machine using this name, fopr example, writing:&lt;br /&gt;&lt;br /&gt;ping www.GregorTheEye.com&lt;br /&gt;&lt;br /&gt;or&lt;br /&gt;&lt;br /&gt;telnet www.GregorTheEye.com&lt;br /&gt;&lt;br /&gt;So, we have 2 names for machine - NetBIOS name and DNS name.&lt;br /&gt;In my local network, following commands will be valid:&lt;br /&gt;&lt;br /&gt;ping 113.54.25.14&lt;br /&gt;ping EYE&lt;br /&gt;ping www.GregorTheEye.com&lt;br /&gt;&lt;br /&gt;Third command will be valid ONLY if my computer is conected to internet.&lt;br /&gt;&lt;br /&gt;And from the internet, valid commands are:&lt;br /&gt;&lt;br /&gt;ping 113.54.14&lt;br /&gt;ping www.GregorTheEye.com&lt;br /&gt;&lt;br /&gt;As you can see, NetBIOS name is not available from internet.&lt;br /&gt;&lt;br /&gt;The main difference between NetBIOS name and DNS name is that DNS name will be available only if computer is connected to the internet and has i's name registered on it. NetBIOS name will be always available to computers that directly connected to target one.&lt;br /&gt;&lt;br /&gt;To get DNS name, you must send a request to DNS server (it's IP is written in the system registry if you computer is connected to internet). If DNS server is unavailable, it will take default timeout time to discover this. If it's available, DNS server will return you human-friendly name of target machine if it's exist in database.&lt;br /&gt;&lt;br /&gt;To obtain a NetBIOS name, you must send UDP packe to target machine and wait for responce. Becouse UDP is not a guaranteed-to-deliver protocol, responce may ot came, came corrupted, came many times etc. Your program must patiently wait for responce (it's not from 137 port - you must SEND UDP packet to 137 port of target machine, but responce will be returned to the port you specify. Only Win95 no OSR2 has a bug there responce always returns to port 137).&lt;br /&gt;&lt;br /&gt;And, finally to Win32 functions -&lt;br /&gt;&lt;br /&gt;gethostbyaddr() will first try to connect DNS server, and, if it's unavailable, will try to get NetBIOS name. So, it will get machine name one method or another in most cases.&lt;br /&gt;&lt;br /&gt;getnameinfo() will ONLY try a DNS name, no attempts to get a NetBIOS name(). That's the difference.&lt;br /&gt;&lt;br /&gt;If you write a program that must get names of local machines as well as names of internet machines (for example, network or port scanner), you MUST use gethostbyaddr(). If you write a program that will operate only with names of remote machines (IRC client, for example) - you MUST use getnameinfo() becouse Microsoft instructs so in lates MSDN releases.&lt;br /&gt;&lt;br /&gt;If you have any questions - feel free to contact me. Also, if i write something wrony, i will be very thankfull if you send ma a hint.&lt;br /&gt;&lt;br /&gt;Best regards, Gregor The Eye. &lt;/blockquote&gt;However, there is no getnameinfo() function in PHP. One of the nice feature in PHP documentation is the commenting. A commenter contributed a &lt;a href="http://www.php.net/manual/en/function.gethostbyaddr.php#46869"&gt;gethostbyaddr_timeout&lt;/a&gt;() function that only query the DNS server. You need to pass the DNS address (instead of using the OS specified DNS) to the function, though.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-4879172717525213529?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/FaTs1YfUgTpOoxBXTuxnJfw5gqc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FaTs1YfUgTpOoxBXTuxnJfw5gqc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/FaTs1YfUgTpOoxBXTuxnJfw5gqc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FaTs1YfUgTpOoxBXTuxnJfw5gqc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/4gP79C_eAiQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/4879172717525213529/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=4879172717525213529" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/4879172717525213529?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/4879172717525213529?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/4gP79C_eAiQ/slow-performance-of-php-in-windows-iis.html" title="Slow Performance of PHP in Windows IIS" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2008/12/slow-performance-of-php-in-windows-iis.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEEGQn89fSp7ImA9WxRaEU0.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-5405287336412587570</id><published>2008-12-08T00:55:00.000+08:00</published><updated>2008-12-13T01:23:43.165+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-13T01:23:43.165+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ria" /><category scheme="http://www.blogger.com/atom/ns#" term="silverlight" /><category scheme="http://www.blogger.com/atom/ns#" term="javafx" /><title>Flash vs. Silverlight vs. JavaFX</title><content type="html">In RIA, Flash is the dominant player while Silverlight and JavaFX are new. However, Silverlight is in a better position than JavaFX.&lt;br /&gt;&lt;br /&gt;.Net is a huge platform like Java. However, Microsoft trimmed .Net into a 5MB version to become a Silverlight runtime. On the other hand, Sun only added scripting syntax to Java and reuse the whole Java runtime as well as the branding. In fact, Java applet and Java web start has become the burden of the Java family. Users perceive Java is slow, insecure, and has compatibility issues whether or not any of them are true. So, it is no longer a technology issue. It is a marketing issue.&lt;br /&gt;&lt;br /&gt;In fact, when it comes to marketing, Microsoft does it far much better than everyone one else in the technology industry.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-5405287336412587570?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/RY3tJPCgkSQRT0EL11y0kjquoGY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/RY3tJPCgkSQRT0EL11y0kjquoGY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/RY3tJPCgkSQRT0EL11y0kjquoGY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/RY3tJPCgkSQRT0EL11y0kjquoGY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/_LDXvLBf5Kw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/5405287336412587570/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=5405287336412587570" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/5405287336412587570?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/5405287336412587570?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/_LDXvLBf5Kw/flash-vs-silverlight-vs-javafx.html" title="Flash vs. Silverlight vs. JavaFX" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2008/12/flash-vs-silverlight-vs-javafx.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEIASX87cCp7ImA9WxRaEU0.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-3119776074631123969</id><published>2008-11-01T14:58:00.000+08:00</published><updated>2008-12-13T01:22:28.108+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-13T01:22:28.108+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="smtp" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="windows" /><category scheme="http://www.blogger.com/atom/ns#" term="iis" /><title>Improving ASP.NET Web Performance with Local SMTP Server</title><content type="html">Implementing public web site, we usually need an SMTP server for registration verification, reset password notification, etc. We usually utilize existing email infrastructure to send the email. However, we may not have the control of the infrastructure. The SMTP server may locate overseas or may go down sometime. In these cases, the response of the web server seems slow. We can setup the SMTP in local server to resolve the problem.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;How does SMTP works?&lt;/span&gt;&lt;br /&gt;There is a special record in DNS called MX record (Mail eXchange record).  Using a@b.com as an example. The SMTP server inquires the MX record of b.com to resolve the SMTP server that will receive the email.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;SMTP Setup in Windows&lt;/b&gt;&lt;br /&gt;Windows comes with SMTP service in XP, 2003, Vista, and 2008. It can be configured with the following steps&lt;br /&gt;1. In IIS Manage, right click &lt;b&gt;Default SMTP Virtual Server&lt;/b&gt;, choose &lt;b&gt;Properties&lt;/b&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_8-RIefqxgGo/SQwDwqNJDII/AAAAAAAAAsE/cNFUeN9RTcM/s1600-h/smtp1.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 300px;" src="http://2.bp.blogspot.com/_8-RIefqxgGo/SQwDwqNJDII/AAAAAAAAAsE/cNFUeN9RTcM/s400/smtp1.jpg" alt="" id="BLOGGER_PHOTO_ID_5263586198987279490" border="0" /&gt;&lt;/a&gt;2. Choose &lt;b&gt;Access&lt;/b&gt; tab and click the &lt;b&gt;Relay &lt;/b&gt;button at the bottom in &lt;b&gt;Relay restriction section&lt;/b&gt;. An SMTP relay lets an SMTP client use an SMTP server to forward e-mail messages to a remote domain. By default, it is disabled so you cannot send any email.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_8-RIefqxgGo/SQwDwjsySMI/AAAAAAAAAsM/Kg1C5URLlP4/s1600-h/smtp2.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 361px; height: 400px;" src="http://3.bp.blogspot.com/_8-RIefqxgGo/SQwDwjsySMI/AAAAAAAAAsM/Kg1C5URLlP4/s400/smtp2.jpg" alt="" id="BLOGGER_PHOTO_ID_5263586197240957122" border="0" /&gt;&lt;/a&gt;3. Add your local host address to allow your local application to send email. You can even add your internal network address so your other machine can use this as the SMTP relay.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_8-RIefqxgGo/SQwDw8b28PI/AAAAAAAAAsU/PqC7wnQimX8/s1600-h/smtp3.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 384px; height: 374px;" src="http://4.bp.blogspot.com/_8-RIefqxgGo/SQwDw8b28PI/AAAAAAAAAsU/PqC7wnQimX8/s400/smtp3.jpg" alt="" id="BLOGGER_PHOTO_ID_5263586203880845554" border="0" /&gt;&lt;/a&gt;4. Optionally, you can prohibit the external connection by configuring the &lt;b&gt;Connection control&lt;/b&gt; section.&lt;br /&gt;&lt;br /&gt;Since SMTP will queue the email and return, it responses instantly. The web performance also improves instantly, too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-3119776074631123969?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/G3G6uGW3TMfocNlwJfYLKqm9yO8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/G3G6uGW3TMfocNlwJfYLKqm9yO8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/G3G6uGW3TMfocNlwJfYLKqm9yO8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/G3G6uGW3TMfocNlwJfYLKqm9yO8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/ljx3yNKJkMA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/3119776074631123969/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=3119776074631123969" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/3119776074631123969?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/3119776074631123969?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/ljx3yNKJkMA/improving-aspnet-web-performance-with.html" title="Improving ASP.NET Web Performance with Local SMTP Server" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_8-RIefqxgGo/SQwDwqNJDII/AAAAAAAAAsE/cNFUeN9RTcM/s72-c/smtp1.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2008/10/improving-aspnet-web-performance-with.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0AARXo8eCp7ImA9WxRWFU8.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-213362322053286470</id><published>2008-10-21T20:55:00.000+08:00</published><updated>2008-11-01T15:49:04.470+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-01T15:49:04.470+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="http" /><category scheme="http://www.blogger.com/atom/ns#" term="html" /><title>HTML Robustness and Query String Parameter Naming</title><content type="html">All the way back to 10 years ago, HTML was designed for non-professionals to publish information to the world wide web. The parsing and interpreting rules are loose. Web browsers implemented a lot of heuristics to display as much information as possible.&lt;br /&gt;&lt;br /&gt;Now, we have been entered the age of Web 2.0 for a few years. The legacy loosen rules remains. One of the rules I have discovered today is the HTML entity. HTML entity without semicolon will be interpreted as if it has semicolon. For example, &amp;amp;lt will be interpreted as &amp;amp;lt;.&lt;br /&gt;&lt;br /&gt;The problem is when writing a web application. You will need to concatenate query string in &amp;lt;a&amp;gt; tag like &amp;lt;a href="list.php?a=x&amp;amp;b=y"&amp;gt;. The query string parameters cannot be named as one of those HTML entities. So, &amp;lt;a href="list.php?a=x&amp;amp;&lt;b&gt;lt&lt;/b&gt;=y"&amp;gt; does not work. The URL will be interpreted as list.php?a=x&lt;b&gt;&amp;lt;&lt;/b&gt;=y. Certainly, no one would use &lt;b&gt;lt &lt;/b&gt;as the name of a query string parameter. However, some other common names including euro, copy, pound, cent, uml, not, micro, times and divide will not work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-213362322053286470?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/fvDPEpu19P3nVs77RoZRzOy6HHI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/fvDPEpu19P3nVs77RoZRzOy6HHI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/fvDPEpu19P3nVs77RoZRzOy6HHI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/fvDPEpu19P3nVs77RoZRzOy6HHI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/-BGBV9hoWZM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/213362322053286470/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=213362322053286470" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/213362322053286470?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/213362322053286470?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/-BGBV9hoWZM/html-robustness-and-art-of-naming.html" title="HTML Robustness and Query String Parameter Naming" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2008/10/html-robustness-and-art-of-naming.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcCQns4eip7ImA9WxJTFkk.&quot;"><id>tag:blogger.com,1999:blog-12198751.post-6246153128896397872</id><published>2008-09-25T13:37:00.002+08:00</published><updated>2009-04-25T15:21:03.532+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-25T15:21:03.532+08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="http" /><title>Developer Friendliness</title><content type="html">People usually said that Firefox is more developer friendly than IE or Safari. Currently, there are two embarrassing incidents that flip my perception of Firefox.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1. Text box inside DIV&lt;/span&gt;&lt;br /&gt;For some weird reason in Firefox 2, the cursor caret in the text box disappears when the text box is inside a DIV. Web developer has to work with the CSS hack to get the caret back&lt;br /&gt;&lt;pre name="code" class="css"&gt;overflow: hidden; position:fixed;&lt;br /&gt;&lt;/pre&gt;It is OK if web developer have the total control of the HTML. However, we have web components in .Net and Java Server Face. If such components do not aware of this, user may had a bad experience.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2. Length Required with POST&lt;/span&gt;&lt;br /&gt;Content-length is an HTTP header letting the server know how much data it should be expecting in the request. However, Firefox does not send such header to the server with posting with XMLHTTPRequest. Developer need to write it out explicitly&lt;br /&gt;&lt;pre name="code" class="js"&gt;xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')&lt;br /&gt;xhr.setRequestHeader('Content-length', '0');&lt;/pre&gt;XMLHTTPRequest is the key doing AJAX! The HTTP request is usually uncachable when using POST. Why on earth developers need to code it themselves?&lt;br /&gt;&lt;br /&gt;I think implementing the standard is important. It relieves the web developers headache. On the other hand, please do not introduce new problem to the developers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12198751-6246153128896397872?l=killustar.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Sz7CvQh90sNtlvYIWk0XYzrvwuA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Sz7CvQh90sNtlvYIWk0XYzrvwuA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Sz7CvQh90sNtlvYIWk0XYzrvwuA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Sz7CvQh90sNtlvYIWk0XYzrvwuA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/StarTechCorner/~4/llBjWmdxE90" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://killustar.blogspot.com/feeds/6246153128896397872/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12198751&amp;postID=6246153128896397872" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6246153128896397872?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12198751/posts/default/6246153128896397872?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/StarTechCorner/~3/llBjWmdxE90/developer-friendliness.html" title="Developer Friendliness" /><author><name>Shane Ng</name><uri>https://profiles.google.com/117637545519135641920</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh6.googleusercontent.com/-2kXXw2YAqIk/AAAAAAAAAAI/AAAAAAAAB94/cIh3WO8V9Z4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://killustar.blogspot.com/2008/09/developer-friendliness.html</feedburner:origLink></entry></feed>

