<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2373396595325606587</id><updated>2011-11-27T15:58:52.178-08:00</updated><category term='object-oriented'/><category term='ASMX'/><category term='GoDaddy'/><category term='web'/><category term='CAB'/><category term='HttpSessionState'/><category term='MSMQ'/><category term='MVP'/><category term='datamining'/><category term='thick client'/><category term='OO'/><category term='SOA'/><category term='application'/><category term='loose-coupling'/><category term='ASP.NET'/><category term='composite application'/><category term='C#'/><category term='SOAP'/><category term='procedural'/><category term='desktop'/><category term='DotNetNuke'/><category term='CMS'/><category term='webtop'/><category term='architecture'/><category term='Design Patterns'/><category term='MessageQueue'/><category term='NullReferenceException'/><category term='.NET'/><category term='database'/><title type='text'>CodeRonin</title><subtitle type='html'>Without a teacher, yet still a student...</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://code-ronin.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://code-ronin.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>travis heseman</name><uri>http://www.blogger.com/profile/04061311168580780466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>10</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2373396595325606587.post-2872716247947005464</id><published>2009-12-29T14:27:00.001-08:00</published><updated>2009-12-29T14:44:32.528-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DotNetNuke'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Quick Hack to Restrict Countries in DotNetNuke's Address Control</title><content type='html'>Today, I needed to restrict the countries that were listed in DotNetNuke's Address control for a custom module I was writing. I only wanted to display the North American countries of Canada, Mexico and United States.  After surfing the net for 5 minutes, nothing was rising to the top as a solution so I hacked my own solution out.&lt;br /&gt;&lt;br /&gt;Assuming you have a web control with the following declaration,&lt;br /&gt;&lt;pre&gt;...&lt;br /&gt;&amp;lt;%@ Register TagPrefix="dnn" &lt;br /&gt;                TagName="Address" &lt;br /&gt;                Src="~/Controls/Address.ascx" %&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;dnn:Address id="addressControl" runat="server" /&amp;gt;&lt;br /&gt;...&lt;/pre&gt;&lt;br /&gt;you can restrict the countries in the code-behind like so:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;using System.Linq;&lt;br /&gt;...&lt;br /&gt;private static readonly string[] Countries &lt;br /&gt;              = new[] { "Canada", "Mexico", "United States" };&lt;br /&gt;...&lt;br /&gt;protected override void OnPreRender(EventArgs e)&lt;br /&gt;{&lt;br /&gt;  base.OnPreRender(e);&lt;br /&gt;&lt;br /&gt;  var list = this.addressControl.FindControl("cboCountry") &lt;br /&gt;                                            as CountryListBox;&lt;br /&gt;  foreach (var item in list.Items.Cast&amp;lt;ListItem&amp;gt;().ToArray())&lt;br /&gt;  {&lt;br /&gt;    if (!Countries.Contains(item.Text))&lt;br /&gt;      list.Items.Remove(item);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2373396595325606587-2872716247947005464?l=code-ronin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-ronin.blogspot.com/feeds/2872716247947005464/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2373396595325606587&amp;postID=2872716247947005464' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/2872716247947005464'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/2872716247947005464'/><link rel='alternate' type='text/html' href='http://code-ronin.blogspot.com/2009/12/quick-hack-to-restrict-countries-in.html' title='Quick Hack to Restrict Countries in DotNetNuke&apos;s Address Control'/><author><name>travis heseman</name><uri>http://www.blogger.com/profile/04061311168580780466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2373396595325606587.post-8875189921246753046</id><published>2009-12-08T07:30:00.000-08:00</published><updated>2009-12-08T08:33:37.798-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='HttpSessionState'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='NullReferenceException'/><title type='text'>Global.asax Session_End NullReferenceException</title><content type='html'>I had an issue pop up on an ASP.NET web app this morning consisting primarily of NullReferenceExceptions being thrown when Global.asax runs some cleanup code from its Session_End handler.  Basically, the cleanup code is disposing some IDisposable items stored in session state before the session blinks out of existence. The manner in which the session was being accessed was the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;System.Web.HttpContext.Current.Session[...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This throws a NullReferenceException since HttpContext.Current is null.  Null?  Why is the context null?  It didn't seem logical at first, but after a minute of thought, it came to me.  Of course there's no context, the session expires on a timeout.  Although, the lifecycle of a session begins with a request, it doesn't end with one.  Thus, when a session times out there is no request or response available, and thus no context.&lt;br /&gt;&lt;br /&gt;Well, how do I access the session then?&lt;br /&gt;&lt;br /&gt;Easy. Global.asax has a Session property.  It is not null at this point, and this allows you to work with the session before the session is garbage collected.  I had to change my cleanup code interface, but I the resolution consisted of Global.asax passing the session to the cleanup code rather than having the cleanup code acquire it itself through the current HttpContext.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;protected void Session_End(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt; // HttpContext.Current.Session throws a&lt;br /&gt; // NullReferenceException since no HttpContext&lt;br /&gt; // exists when a session expires, thus the client&lt;br /&gt; // code can't access the HttpSessionState object this&lt;br /&gt; // way and needs it to be passed directly from&lt;br /&gt; // Global.asax (i.e. this.Session)&lt;br /&gt;&lt;br /&gt; this.Controller.Cleanup(this.Session);&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/2373396595325606587-8875189921246753046?l=code-ronin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-ronin.blogspot.com/feeds/8875189921246753046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2373396595325606587&amp;postID=8875189921246753046' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/8875189921246753046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/8875189921246753046'/><link rel='alternate' type='text/html' href='http://code-ronin.blogspot.com/2009/12/globalasax-sessionend.html' title='Global.asax Session_End NullReferenceException'/><author><name>travis heseman</name><uri>http://www.blogger.com/profile/04061311168580780466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2373396595325606587.post-2347723488852465856</id><published>2009-11-15T09:51:00.001-08:00</published><updated>2009-12-29T14:47:08.392-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='DotNetNuke'/><category scheme='http://www.blogger.com/atom/ns#' term='GoDaddy'/><category scheme='http://www.blogger.com/atom/ns#' term='CMS'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Manually Installing DotNetNuke on GoDaddy</title><content type='html'>These steps detail the process of manually installing DotNetNuke to the root of an empty GoDaddy hosted domain.  &lt;br /&gt;&lt;ol&gt;&lt;br /&gt; &lt;li&gt;Acquire DotNetNuke&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;   &lt;li&gt;Go to http://www.dotnetnuke.com/ and login (note: if you're not registered, then you'll have to register before you can login)&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Click the "DOWNLOAD" icon at the top of the page&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Download the edition of your choice (I chose Community)&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Unzip the file to the directory of your choice&lt;/li&gt;&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt; &lt;/li&gt;&lt;br /&gt; &lt;li&gt;Virtual directory setup&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;   &lt;li&gt;Login to GoDaddy&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Hover over "Hosting" on the menu bar and click the "My Hosting Account" link&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Click "Manage Account" next to the site to which you want to install DNN. This will take you to GoDaddy's Hosting Control Center.&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Hover over the "Content" tab and click the "File Manager" link&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Select the "[Root]" row by clicking on it or checking the checkbox&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Click the "Permissions" icon on the toolbar&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Check the "Read", "Write", and "Reset all children to inherit" and click OK (this needs to occur because DotNetNuke writes to its directory and sub-directories for many reasons, the most common is module installation)&lt;/li&gt;&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt; &lt;/li&gt;&lt;br /&gt; &lt;li&gt; Database setup&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;   &lt;li&gt;In GoDaddy's Hosting Control Center, hover over the "Databases" tab and click the "SQL Server" link&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Click the "Create Database" button&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;In the description type something descriptive like "www.mydomain.com DotNetNuke Database"&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Check the DSN and ASP Schema checkboxes (this tells GoDaddy's database creation script to create a Data Source Name for the database and to Create the tables for the ASP Schema - e.g. aspnet_Applications, aspnet_Roles, etc.)&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;(Optional) If you want remote admin access to the database, check the "Yes" radio button under "Allow Direct Database Access" (this allows external access to the database - e.g. you can connect to it using SQL Server Management Studio on your own PC)&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Enter a name for the database in the "SQL Server Database/User Name" field - e.g. mydomaindnn (this will be the name of the database and the user name for used for DNN's access)&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Pick a good password, but be mindful of GoDaddy's password requirements.  Write down this password as you'll need it to edit the web.config in a future step.&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;(Optional) If you'd like to create an user with read-only access this database, fill out the remaining fields correlating to "Read-Only User"&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Click OK (You'll be taken back to teh SQL Server databases page and the status of your database will be "Pending Setup" - this should only be the case for a few minutes)&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Click the "Edit/View Details" icon for your database in the "Actions" column.  This page contains the values you'll need for changing the web.config in the next step.&lt;/li&gt;&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt; &lt;/li&gt;&lt;br /&gt; &lt;li&gt;Web.Config&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;   &lt;li&gt;On your PC, go to the directory where you unzipped the DNN installation files.&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Rename web.config to original-web.config&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Rename release.config to web.config&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Open web.config in your favorite editor&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;In the conectionStrings section, fully comment out the SQLExpress connection string (it contains "Data Source=.\SQLExpress"), and uncomment the SQL Server connection string (it contains "Server=(local)").  Change the SQL Server connection string include the database values seen on GoDaddy (Server is Host Name; Database is Database Name; uid is User Name and pwd is the password you entered for this user when you set up the datbase).&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Repeat the above step for the connections strings found in the appSettings section just a few lines below.&lt;/li&gt;&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt; &lt;/li&gt;&lt;br /&gt; &lt;li&gt;Upload via FTP&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;   &lt;li&gt;Configure your FTP clint (FileZilla, or CuteFtp, or the like) to point to ftp.mydomain.com, with your GoDaddy username/passoword (or another FTP user for whom you've register FTP credentials through the GoDaddy Control Center).&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Connect&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Once connected, you can delete everything in the root except the _db_backups and _hcc_thumbs directories.&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;This upload the contents of the directory to which you unzipped the DNN installation files.&lt;/li&gt;&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt; &lt;/li&gt;&lt;br /&gt; &lt;li&gt;You are ready to install&lt;br /&gt;  &lt;ul&gt;&lt;br /&gt;   &lt;li&gt;Navigate to the www.mydomain.com&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Follow the steps to walk through creating your site (Note: choosing the Auto option is recommended.)&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;For SMTP settings, use &lt;br/&gt;&lt;br /&gt;server: &lt;b&gt;relay-hosting.secureserver.net&lt;/b&gt;&lt;br/&gt;&lt;br /&gt;basic&lt;br/&gt;&lt;br /&gt;username: emailuser@mydomain.com&lt;br/&gt;&lt;br /&gt;password: [emailuser's email password]&lt;br/&gt;&lt;br /&gt;* Make sure to Test the settings before you move on!&lt;br /&gt;  &lt;/ul&gt;&lt;br /&gt; &lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2373396595325606587-2347723488852465856?l=code-ronin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-ronin.blogspot.com/feeds/2347723488852465856/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2373396595325606587&amp;postID=2347723488852465856' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/2347723488852465856'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/2347723488852465856'/><link rel='alternate' type='text/html' href='http://code-ronin.blogspot.com/2009/11/manually-installing-dotnetnuke-on.html' title='Manually Installing DotNetNuke on GoDaddy'/><author><name>travis heseman</name><uri>http://www.blogger.com/profile/04061311168580780466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2373396595325606587.post-7087757033376835211</id><published>2009-08-31T14:37:00.000-07:00</published><updated>2009-09-01T14:33:28.821-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MVP'/><category scheme='http://www.blogger.com/atom/ns#' term='Design Patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><title type='text'>Simple MVP in ASP.NET</title><content type='html'>Model View Presenter (MVP)&lt;br /&gt;&lt;br /&gt;The model:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public interface IEntity&lt;br /&gt;{&lt;br /&gt;    SomeType SomeProperty1 { get; set;}&lt;br /&gt;    SomeOtherType SomeProperty2 { get; set;}&lt;br /&gt;    YetAnotherType SomeProperty3 { get; set;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The interface (the view contract - insulates the presenter from the implementation):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public interface IEntityView&lt;br /&gt;{&lt;br /&gt;    event EventHandler Init;&lt;br /&gt;    event EventHandler Load;&lt;br /&gt;    event EventHandler Unload;&lt;br /&gt;    IEntity Entity { get; set; }&lt;br /&gt;    bool Visible { get; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The code behind (the view implementation):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public partial class EntityDisplayPage : Page, IEntityView&lt;br /&gt;{&lt;br /&gt;    protected EntityDisplayPage()&lt;br /&gt;        : base()&lt;br /&gt;    {&lt;br /&gt;        new EntityPresenter(this); // wire the MVP pattern&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    #region IEntityView Members&lt;br /&gt;&lt;br /&gt;    public IEntity Entity { get; set; }&lt;br /&gt;&lt;br /&gt;    #endregion&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The presenter (doesn't know the view implementation, manipulates it through the view contract defined as an interface):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class EntityPresenter&lt;br /&gt;{&lt;br /&gt;    public EntityPresenter(IEntityView view)&lt;br /&gt;        : base()&lt;br /&gt;    {&lt;br /&gt;        if (null == view)&lt;br /&gt;            throw new ArgumentNullException("view");&lt;br /&gt;        this.view = view;&lt;br /&gt;&lt;br /&gt;        this.View.Init += (sender, e) =&gt; { /* some business logic on init */ };&lt;br /&gt;        this.View.Load += (sender, e) =&gt; &lt;br /&gt;        {&lt;br /&gt;            /* some business logic on load*/ &lt;br /&gt;            this.View.Entity = GetEntityToDisplay();&lt;br /&gt;        };&lt;br /&gt;        this.View.Unload += (sender, e) =&gt; { /* some business logic on unload*/ };&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     private readonly IEntityView  view;&lt;br /&gt;     public IEntityView  View&lt;br /&gt;     {&lt;br /&gt;         get { return view; }&lt;br /&gt;     }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And that's how you get good separation through a simple MVP implementation in ASP.NET.  I think it speaks for itself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2373396595325606587-7087757033376835211?l=code-ronin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-ronin.blogspot.com/feeds/7087757033376835211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2373396595325606587&amp;postID=7087757033376835211' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/7087757033376835211'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/7087757033376835211'/><link rel='alternate' type='text/html' href='http://code-ronin.blogspot.com/2009/08/simple-mvp-in-aspnet.html' title='Simple MVP in ASP.NET'/><author><name>travis heseman</name><uri>http://www.blogger.com/profile/04061311168580780466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2373396595325606587.post-9144954702787401744</id><published>2009-08-04T12:45:00.000-07:00</published><updated>2009-08-04T14:40:15.873-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SOAP'/><category scheme='http://www.blogger.com/atom/ns#' term='SOA'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><category scheme='http://www.blogger.com/atom/ns#' term='ASMX'/><title type='text'>Embrace Your Technology</title><content type='html'>Recently, I was working with a fairly progressive client that was spending a lot of time working its internal architecture into an SOA solution. I was brought on in January as a consultant to build a greenfield application of modest complexity, but probably more complex than the apps they'd built internally to date. They placed several strict requirements on certain technological aspects of development, which I could easily handle. However, they did declare one that gave me some fits.&lt;br /&gt;&lt;br /&gt;A few months before I arrived, their internal staff had started to develop a web service to centralize contact information for the entire agency. During the initial phases of my project, I was told that my app was to integrate with this new contact web service when in comes online in March. (Those of you with inter-team experience already know where I'm going.) March came and went; no contact web service. I kept hearing rumors of re-work from members of the team, which made me very nervous.  My project manager (also a consultant) and I began to think of contingencies as this was probably going to bite us and delay our release.  We were then told May; the web service would be up and running by May. May came and went. Our thoughts eventually turned entirely to "It's never going to be delivered.  We're going to have to implement something ourselves to make our release date."&lt;br /&gt;&lt;br /&gt;Our app was due to make it's initial production release the first of July.  We had to have that contact piece to go live. But we had nothing. At the end of May, we decided to write our own and merge it with theirs later. I wrote our solution for contacts the following week and we moved into production the first week of July.  Whew!&lt;br /&gt;&lt;br /&gt;Sometime that month, they finally pushed their contact web service to production and I was able to begin integration with my app on July 30th. They were using an ASMX web service, so I figured that it'd be easy enough to create a proxy and adapt it to our solution. I silently thought to myself, "I bet I can have this integrated in just a couple days." &lt;br /&gt;&lt;br /&gt;I was in for shock though. When I dug into the WSDL, I was both disgusted and astonished.  The schema was flat.  They were using SOAP to usher strings (and only strings) back and forth to the web service. The schema had no depth at all anywhere - everything was a string, a string that had to be parsed manually. If an operation returned person information, it returned a string which contained the information in XML form, but still a string that'd have to be parsed or accessed via XPath. There other issues that come about by having such a poorly defined schema.  For example, if everything is a string, I can send anything I want to the service including the current version of Webster's dictionary or the New York City phone book.  And oh yeah! I was delayed for this? It took them months and months to build this? Less than twenty-five operations, no schema... What were they doing all that time?&lt;br /&gt; &lt;br /&gt;It's as if they intended to build a house, took as long as building a sky scraper and wound up with a tool shed. They decided to leverage a very powerful tool, an air nailer, but used it as a hammer. When they went to drive a nail, they turned the air nailer sideways and drove it with the side of the air nailer. In this way, not only did they negate the benefits of the air nailer, but they also endured a loss of productivity.&lt;br /&gt;&lt;br /&gt;I had no doubt; this was going to be painful. To add some extra salt to the wound, some features my application needed were simply not implemented. So, on top of having to break it to my users that some of their favorite contact functions were going away, now I have to write custom parsing logic to extract information from the operations, rather than let .NET's out-of-the-box SOAP digestion handle if for me.  Which is going to delay the whole process by days.  I cringe when I think of parsing XML myself for any reason; especially when so many good people worked hard to write frameworks for SOAP and various other specifications so I wouldn't have to. &lt;br /&gt;&lt;br /&gt;Thus, I plead to the masses, "Embrace Your Technology".  It seems more and more commonplace that my clients not only fail to understand the technologies they choose, but choose technologies they don't understand.  From ASP.NET to Oracle to Visual Studio to web services, they don't understand what these choices provide and end up misusing them negating the benefit.  They stand up and claim "We're using SOAP web services!", but have no idea what that the specification does for them.  They miss the benefits a specification provides by allowing them to leverage pre-established frameworks to forgo the normal rigmarole that other web service specifications impose on them, allowing them the ability to move on with more important development without writing custom XML parsing and operation security for invalid data.&lt;br /&gt;&lt;br /&gt;I will contend that too many folks are content to acquire a technology without acquiring a book on the subject. Heaven forbid someone even try reading the book before acquiring the technology.  This is so unfortunate as there are typically some really great minds on these technologies that share their expertise through writing books and whitepapers.&lt;br /&gt;&lt;br /&gt;I'll take it a step further and sideways; I will contend that too many folks that barely need a firecracker acquire a nuclear bomb.  Some acquire Oracle when a text file will suffice for the moment; even when an RDBMS is necessary, MySQL will do the trick before you bring in the heavy hitter.  Remember, your real need is persistence of data, not the acquisition of Oracle. A good friend watched his startup fail because his colleagues spent their capital on Oracle rather than a prototype of their idea - a simple case of confusing needs.&lt;br /&gt;&lt;br /&gt;So to all you hard-working folk out there, embrace the technology you choose and choose the technology you need.  Perhaps, you'll deliver a functioning house on time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2373396595325606587-9144954702787401744?l=code-ronin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-ronin.blogspot.com/feeds/9144954702787401744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2373396595325606587&amp;postID=9144954702787401744' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/9144954702787401744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/9144954702787401744'/><link rel='alternate' type='text/html' href='http://code-ronin.blogspot.com/2009/08/embrace-your-technology.html' title='Embrace Your Technology'/><author><name>travis heseman</name><uri>http://www.blogger.com/profile/04061311168580780466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2373396595325606587.post-2982580037097047283</id><published>2008-09-17T17:27:00.001-07:00</published><updated>2010-10-30T08:41:01.729-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MSMQ'/><category scheme='http://www.blogger.com/atom/ns#' term='MessageQueue'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>MSMQ Transactional Message Processing using Multiple Receive Queues</title><content type='html'>Here's the situation:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;You want to asynchronously process messages on a queue in a transactional manner, such that the message is only taken from the queue upon successfully processing it.  This allows messages that failed to be properly processed to be processed again later with a chance at success.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;You want to have multiple workers processing these messages to improve efficiency and performance of long-running processing on large numbers of messages.&lt;/li&gt;&lt;li&gt;When you shutdown, you want all the processing code to complete before allowing the process to exit.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;The first item can be accomplished with using MessageQueue's BeginPeek method and PeekCompleted event.  This will allow you to peek at the queue and and begin a transaction before actually receiving the message.  Beginning a transaction before receiving the message allows you to abort the transaction should an error occur during processing, leaving the message on the queue to be processed again later (hopefully with a higher chance of success).  The following event handler shows the boilerplate code to accomplish this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;private void queue_PeekCompleted(object sender, PeekCompletedEventArgs e)&lt;br /&gt;{&lt;br /&gt;    var queue = (MessageQueue)sender;&lt;br /&gt;&lt;br /&gt;    var transaction = new MessageQueueTransaction();&lt;br /&gt;    transaction.Begin();&lt;br /&gt;    try&lt;br /&gt;    {&lt;br /&gt;       var message = queue.Receive(transaction);&lt;br /&gt;       // process the message here&lt;br /&gt;       transaction.Commit();&lt;br /&gt;    }&lt;br /&gt;    catch (Exception ex)&lt;br /&gt;    {&lt;br /&gt;       // abort if processing fails&lt;br /&gt;       transaction.Abort();&lt;br /&gt;    }&lt;br /&gt;    finally&lt;br /&gt;    {&lt;br /&gt;       // start watching for another message&lt;br /&gt;       queue.BeginPeek();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The second item can be accomplished by creating multiple receiving queues and telling them to start watching for incoming messages.  The following snippets of code demonstrate how to accomplish this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;private readonly MessageQueue[] Receivers; // member&lt;br /&gt;...&lt;br /&gt;this.Receivers = Enumerable.Range(0, (count &lt;= 0) ? 1 : count)&lt;br /&gt;   .Select(i =&gt;&lt;br /&gt;   {&lt;br /&gt;       var queue = new MessageQueue(path, QueueAccessMode.Receive)&lt;br /&gt;       {&lt;br /&gt;           Formatter = new BinaryMessageFormatter()&lt;br /&gt;       };&lt;br /&gt;       queue.MessageReadPropertyFilter.SetAll();&lt;br /&gt;       return queue;&lt;br /&gt;   })&lt;br /&gt;   .ToArray();&lt;br /&gt;&lt;br /&gt;// begin watching&lt;br /&gt;foreach (var queue in this.Receivers)&lt;br /&gt;{&lt;br /&gt;   queue.PeekCompleted += queue_PeekCompleted;&lt;br /&gt;   queue.BeginPeek();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;// closing&lt;br /&gt;foreach (var queue in this.Receivers)&lt;br /&gt;{&lt;br /&gt;   queue.PeekCompleted -= queue_PeekCompleted;&lt;br /&gt;   queue.Close(); // stop peeking&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The third item can be accomplished by simply incrementing and decrementing a counter when processing begins and ends respectively; then you simply block until that counter reaches zero.  You'll want to place the decrement in a finally block to ensure that the counter is decremented even if processing throws an exception. Assuming you have a Counter class that implements thread safe increment and decrement operations (see bottom), you can create a member named "ProcessingCounter", and your PeekCompleted handler has the following line to do the processing,&lt;br /&gt;&lt;pre&gt;this.Handle(queue.Receive(transaction));&lt;/pre&gt;  &lt;br /&gt;your Handle method would look like this,&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;private void Handle(Message message)&lt;br /&gt;{&lt;br /&gt;   this.ProcessingCounter.Increment();&lt;br /&gt;   try&lt;br /&gt;   {&lt;br /&gt;       // process message here;&lt;br /&gt;   }&lt;br /&gt;   finally&lt;br /&gt;   {&lt;br /&gt;       this.ProcessingCounter.Decrement();&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and you could block after your MessageQueue.Close() calls like this&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;while (this.ProcessingCounter.Value &gt; 0)&lt;br /&gt;    Thread.Sleep(100);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The following abstract class puts it all together.  Simply implement the Process method and away you go!&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public abstract class MessageProcessor&amp;lt;TMessage&amp;gt;&lt;br /&gt;{&lt;br /&gt;    private readonly MessageQueue[] Receivers;&lt;br /&gt;    private readonly Counter ProcessingCounter = new Counter();&lt;br /&gt;    private bool IsClosing;&lt;br /&gt;&lt;br /&gt;    public MessageProcessor(string path)&lt;br /&gt;        : this(path, 1) { }&lt;br /&gt;&lt;br /&gt;    public MessageProcessor(string path, int count)&lt;br /&gt;        : base()&lt;br /&gt;    {&lt;br /&gt;        if (string.IsNullOrEmpty(path))&lt;br /&gt;            throw new ArgumentNullException("path");&lt;br /&gt;&lt;br /&gt;        if (!MessageQueue.Exists(path))&lt;br /&gt;            MessageQueue.Create(path, true);&lt;br /&gt;&lt;br /&gt;        this.Receivers = Enumerable.Range(0, (count &lt;= 0) ? 1 : count)&lt;br /&gt;            .Select(i =&gt;&lt;br /&gt;            {&lt;br /&gt;                var queue = new MessageQueue(path, QueueAccessMode.Receive)&lt;br /&gt;                {&lt;br /&gt;                    Formatter = new BinaryMessageFormatter()&lt;br /&gt;                };&lt;br /&gt;                queue.MessageReadPropertyFilter.SetAll();&lt;br /&gt;                return queue;&lt;br /&gt;            })&lt;br /&gt;            .ToArray();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void Close()&lt;br /&gt;    {&lt;br /&gt;        this.IsClosing = true;&lt;br /&gt;&lt;br /&gt;        this.OnClosing();&lt;br /&gt;&lt;br /&gt;        foreach (var queue in this.Receivers)&lt;br /&gt;        {&lt;br /&gt;            queue.PeekCompleted -= queue_PeekCompleted;&lt;br /&gt;            queue.Close();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        while (this.IsProcessing)&lt;br /&gt;            Thread.Sleep(100);&lt;br /&gt;&lt;br /&gt;        this.IsClosing = this.IsOpen = false;&lt;br /&gt;        this.OnClosed();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public bool IsOpen { get; private set; }&lt;br /&gt;&lt;br /&gt;    protected bool IsProcessing&lt;br /&gt;    {&lt;br /&gt;        get { return this.ProcessingCounter.Value &gt; 0; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected virtual void OnClosing() { }&lt;br /&gt;    protected virtual void OnClosed() { }&lt;br /&gt;    protected virtual void OnOpening() { }&lt;br /&gt;    protected virtual void OnOpened() { }&lt;br /&gt;&lt;br /&gt;    public void Open()&lt;br /&gt;    {&lt;br /&gt;        if (this.IsOpen)&lt;br /&gt;            throw new Exception("This processor is already open.");&lt;br /&gt;&lt;br /&gt;        this.OnOpening();&lt;br /&gt;&lt;br /&gt;        foreach (var queue in this.Receivers)&lt;br /&gt;        {&lt;br /&gt;            queue.PeekCompleted += queue_PeekCompleted;&lt;br /&gt;            queue.BeginPeek();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        this.IsOpen = true;&lt;br /&gt;        this.OnOpened();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected abstract void Process(TMessage @object);&lt;br /&gt;&lt;br /&gt;    private void Handle(Message message)&lt;br /&gt;    {&lt;br /&gt;        Trace.Assert(null != message);&lt;br /&gt;&lt;br /&gt;        this.ProcessingCounter.Increment();&lt;br /&gt;        try&lt;br /&gt;        {&lt;br /&gt;            this.Process((TMessage)message.Body);&lt;br /&gt;        }&lt;br /&gt;        finally&lt;br /&gt;        {&lt;br /&gt;            this.ProcessingCounter.Decrement();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void queue_PeekCompleted(object sender, PeekCompletedEventArgs e)&lt;br /&gt;    {&lt;br /&gt;        var queue = (MessageQueue)sender;&lt;br /&gt;&lt;br /&gt;        var transaction = new MessageQueueTransaction();&lt;br /&gt;        transaction.Begin();&lt;br /&gt;        try&lt;br /&gt;        {&lt;br /&gt;            // if the queue closes after the transaction begins,&lt;br /&gt;            // but before the call to Receive, then an exception&lt;br /&gt;            // will be thrown and the transaction will be aborted&lt;br /&gt;            // leaving the message to be processed next time&lt;br /&gt;            this.Handle(queue.Receive(transaction));&lt;br /&gt;            transaction.Commit();&lt;br /&gt;        }&lt;br /&gt;        catch (Exception ex)&lt;br /&gt;        {&lt;br /&gt;            transaction.Abort();&lt;br /&gt;            Trace.WriteLine(ex.Message);&lt;br /&gt;        }&lt;br /&gt;        finally&lt;br /&gt;        {&lt;br /&gt;            if (!this.IsClosing)&lt;br /&gt;                queue.BeginPeek();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Incidentally, the following is my implementation of a thread-safe counter.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Counter&lt;br /&gt;{&lt;br /&gt;    private readonly object SyncRoot = new object();&lt;br /&gt;    private int value;&lt;br /&gt;&lt;br /&gt;    public int Value&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            lock (this.SyncRoot)&lt;br /&gt;            {&lt;br /&gt;                return value;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int Decrement()&lt;br /&gt;    {&lt;br /&gt;        lock (this.SyncRoot)&lt;br /&gt;        {&lt;br /&gt;            return --value;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int Increment()&lt;br /&gt;    {&lt;br /&gt;        lock (this.SyncRoot)&lt;br /&gt;        {&lt;br /&gt;            return ++value;&lt;br /&gt;        }&lt;br /&gt;    }&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/2373396595325606587-2982580037097047283?l=code-ronin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-ronin.blogspot.com/feeds/2982580037097047283/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2373396595325606587&amp;postID=2982580037097047283' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/2982580037097047283'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/2982580037097047283'/><link rel='alternate' type='text/html' href='http://code-ronin.blogspot.com/2008/09/msmq-transactional-message-processing.html' title='MSMQ Transactional Message Processing using Multiple Receive Queues'/><author><name>travis heseman</name><uri>http://www.blogger.com/profile/04061311168580780466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2373396595325606587.post-2716554009874607382</id><published>2008-02-05T20:50:00.001-08:00</published><updated>2008-09-14T10:56:29.507-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='application'/><category scheme='http://www.blogger.com/atom/ns#' term='object-oriented'/><category scheme='http://www.blogger.com/atom/ns#' term='procedural'/><category scheme='http://www.blogger.com/atom/ns#' term='loose-coupling'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><title type='text'>A System of Interactions</title><content type='html'>According to Newton's Third Law of Motion, our world is a system of continuously interacting objects.  Essentially, whenever an action is taken by an object, hosts of other objects react, each in turn evoking more reactions ad infinitum; and thus, our world goes round.  OK, I know what you're thinking. "Is a code-monkey really going to give me a lesson in classical physics?"  The answer is, of course, no.  I'm using Newton's Third Law as an analogy to the execution of an object-oriented application.&lt;br /&gt;&lt;br /&gt;You see, procedural applications tend to execute in a manner easily defined in a decision tree; take this action, check a condition, take the action associated with that condition, check another condition, take another action, so on and so forth.  I associate the simplest form of this type of execution almost directly with the world's most beloved pricing game from &lt;i&gt;The Price Is Right&lt;/i&gt;, &lt;i&gt;Plinko&lt;/i&gt;.  In Plinko, one drops some input in at the top of the board, the Plinko chip, and it makes its way down a grid of pegs until it falls into one of a number of buckets determined by its path through the pegs.  As the chip falls between any two pegs, it collides with another peg, forcing it to go either to the left or the right before it can fall again to the next peg where it will make another decision to go left or right.  If one had the ability to observe the conditions of the falling chip (gravitational forces, rotation, etc.) at each peg, one could easily deduce the route the chip will take throught the pegs.  Thus, enters the decision tree notion I mentioned early; based on the conditions surrounding any given input, a procedural application's execution is easily seen to be deterministic.&lt;br /&gt;&lt;br /&gt;Because of the deterministic nature of procedural applications, this type of app doesn't lend itself to pluggability or extendability without modification. Not to say that every app should strive for this quality; I've certainly written many apps that didn't need to be extended (sometimes you just need a script; sometimes the number of use cases for the app is finite; there could be many reasons).  However, it's undeniable that considering an app with this form of execution, a developer is more likely to create rigid dependencies on the deterministic nature thereof.  For example, a developer modifying or extending some action F may assume that actions A, C and E have already been taken, programming in such a way that F is very dependent on A, C and E having done their work, and in the process, hampering F's ability to be reused when A, C and E have not all executed.  For this reason, procedural applications can be difficult to extend or modify.  In order to add a decision (or a whole new sub-tree) into the decision tree, a developer has to explicity know the addition's position in the tree; in a worst case scenario, where all the actions in the tree are tightly coupled to the actions taken before them, the logic for significant portions of the tree would have to be updated to take into account the addition.  &lt;br /&gt;&lt;br /&gt;Conversely, well-designed object-oriented applications execute in a manner remarkably similar to the system of objects existing in the physical reality of our world.  Where objects in our reality monitor each other through physical media provided by the laws of physics, objects in OO apps monitor each other through some kind of event mechanism or messaging framework allowing both to react to actions taken in their space of existence.&lt;br /&gt;&lt;br /&gt;Objects in the most loosely-coupled applications don't even need to be aware of each other, they could let the application (or some mechanism therein) do that for them; basically the application broadcasts the fact that some action has been taken and all those that want to react should do so now.  A developer adding or modifying functionality need know nothing about the application as a whole, just simply, how to listen for an action to be taken and what to do then.  Because the application is unaware of whom is listening and what actions they might take, execution of an OO application is seen to be somewhat non-deterministic (at least in the abstract).  &lt;br /&gt;&lt;br /&gt;The non-deterministic nature of OO applications forces developers to decouple the action(s) on which they're working from the rest of the application.  Developers can't be explicitly sure what actions have already been taken, neither can they be sure what actions will be taken after theirs executes, eliminating their ability to create a dependency on them.  This significantly reduces each object's awareness of the rest of the application, making its development small and focused, enhancing readability and maintainability, not to mention the ability for new developers to contribute value right away without needing "ramp-up" time to learn how the whole app works.&lt;br /&gt;&lt;br /&gt;So, no physics lesson.  However, if you're doing OO development, you might give some thought to how you think about how pieces of your app interact.  Ask yourself, "Am I constructing my objects to interact in a similar fashion as objects do in our physical reality?"  "Am I thinking about creating and using objects in my logical construct the way nature creates and uses them in her physical construct?"  If so, you're probably headed down the right track from a maintainablity and extendability point of view.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2373396595325606587-2716554009874607382?l=code-ronin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-ronin.blogspot.com/feeds/2716554009874607382/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2373396595325606587&amp;postID=2716554009874607382' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/2716554009874607382'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/2716554009874607382'/><link rel='alternate' type='text/html' href='http://code-ronin.blogspot.com/2008/02/system-of-interactions.html' title='A System of Interactions'/><author><name>travis heseman</name><uri>http://www.blogger.com/profile/04061311168580780466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2373396595325606587.post-3016189458393126863</id><published>2008-01-30T15:11:00.000-08:00</published><updated>2008-02-03T19:30:09.374-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='application'/><category scheme='http://www.blogger.com/atom/ns#' term='composite application'/><category scheme='http://www.blogger.com/atom/ns#' term='desktop'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><category scheme='http://www.blogger.com/atom/ns#' term='CAB'/><title type='text'>Composite Gravitation</title><content type='html'>&lt;strong&gt;&lt;i&gt;What do the users really want?&lt;/i&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;In application development, responsible developers realize that they are, in fact, developing for users, and we (yes, sometimes I too am responsible) ask regularly ourselves what users really want and need. In our hearts, we really do want to get it right; we want to deliver "added value"; it gives us great pleasure when we can show some users some new feature and with growing eyes and smiles an, "Oh, cool!" is uttered appreciatively. With that said, all too often we, as developers, are given (sometimes tacitly) the task to sift through the requirements docs, the discussions, the diagrams, the charts and all the other mediums through which our users are asking for something, and get down to the heart of what they want. However, sometimes even we cannot "see the pattern". &lt;i&gt;Composite gravitation&lt;/i&gt; is a name I've given to one such pattern.&lt;br /&gt;&lt;br /&gt;Composite gravitation can be manifest in many ways, but is most apparent when users start to grumble about having too many different applications to use in order to complete their tasks. I can sympathize with their angst; I certainly wouldn't want to be required to leave my IDE regularly in order to complete this task or that. On top of it all, who wants to learn and use a multitude of applications, each with its own look and feel? The desires of the users begin to gravitate toward some sort of consolidation and optimization, even though they may not understand what that means or voice it accurately.  In any case, it's not long before you start hearing terms like "integrated desktop", "dashboard", "container application" and "portal".&lt;br /&gt;&lt;br /&gt;In most cases, this situation results from introducing applications separately and disparately, generally with very narrow use-cases and without attention to the look and feel of previously introduced apps. It's a natural enough progression that could occur for various reasons; for example, they could have been developed at different times, or with different technologies, or perhaps they seem functionally unrelated, 3rd-party apps could be thrown into the mix, or a combination of these.&lt;br /&gt;&lt;br /&gt;The grumbling of users is not the only symptom of composite gravitation. It can be seen when companies begin to require all internal applications to be web applications stating many justifications, and I use that term loosely, including that it consolidates the number of applications down to each user's browser. It can also be observed when IT and development shops begin to realize the maintenance nightmare that's been created by the existence of a myriad of disparate applications, each with separate yet remarkably similar code-bases, with run-time counterparts that soak up desktop and server resources utilizing no algorithm for sharing with sibling apps. In any case, the reasons are swelling, not dwindling, and something must be done.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Enter the &lt;i&gt;Composite Application&lt;/i&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;A composite application is exactly what its name entails, an application composed of applications.  You see composite applications in many forms, but generally each has a single extensible shell built using a modular and pluggable architecture. When new business use-cases arise, new modules can be built and plugged into the existing application in a seamless manner. Each module is built so as to maximize maintainability and efficiency, typically through code reuse, extensible menu structures and toolbars, connection management, authentication services or other resources.&lt;br /&gt;&lt;br /&gt;If done correctly, composite applications greatly benefit both worlds, users and IT/Development. The users get their applications in a consolidated and cohesive package with intuitive look and feel, while the common shell and extensible, pluggable architecture eases the maintenance headaches generally involved with disparate applications.&lt;br /&gt;&lt;br /&gt;For more information on composite applications, Atanu Banerjee of Microsoft Corporation has a great MSDN article entitled &lt;a href="http://msdn2.microsoft.com/en-us/architecture/bb220803.aspx"&gt;What Are Composite Applications?&lt;/a&gt;, and for you Windows hackers, let me also direct you to the &lt;a href="http://msdn2.microsoft.com/en-us/library/aa480450.aspx"&gt;Smart Client - Composite UI Application Block&lt;/a&gt;, a framework for building composite applications in .NET, graciously provided by the Microsoft &lt;i&gt;Patterns and Practices&lt;/i&gt; group.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2373396595325606587-3016189458393126863?l=code-ronin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-ronin.blogspot.com/feeds/3016189458393126863/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2373396595325606587&amp;postID=3016189458393126863' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/3016189458393126863'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/3016189458393126863'/><link rel='alternate' type='text/html' href='http://code-ronin.blogspot.com/2008/01/composite-gravitation.html' title='Composite Gravitation'/><author><name>travis heseman</name><uri>http://www.blogger.com/profile/04061311168580780466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2373396595325606587.post-4009401223252024652</id><published>2008-01-29T15:44:00.000-08:00</published><updated>2009-09-24T18:23:54.738-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='application'/><category scheme='http://www.blogger.com/atom/ns#' term='webtop'/><category scheme='http://www.blogger.com/atom/ns#' term='desktop'/><category scheme='http://www.blogger.com/atom/ns#' term='thick client'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Web Applications are Overrated</title><content type='html'>For years we've been told that web apps are the way of the future.  But are they really?  The answer is "probably not". Think about where web applications are going.  You don't know where they're going?  We'll let's think about where they've been.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;The Stateless Abyss&lt;/strong&gt;&lt;br /&gt;For almost 20 years now, web applications have lived in the stateless depths of the &lt;i&gt;World Wide Web&lt;/i&gt;, thanks to that wonderful little protocol, HTTP.  HTTP's request/response nature allowed for simple and straight-forward communication back in the day, when all we really wanted was to ask another machine on the web for a resource.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;The Dynamic Shift&lt;/strong&gt;&lt;br /&gt;As the Web grew in popularity, we started placing more and more demands on the resources for which we asked. We started making the dog do tricks. We wanted more "interactivity"; static text and images were no longer enough; we now needed the ability to customize the resource and create "dynamic content" with each request. We wanted to conduct business and stream media all over a stateless channel, where each transaction had no knowledge of previous ones.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;The Stateful Revolution&lt;/strong&gt;&lt;br /&gt;Soon, having the dog roll over or shake hands was not enough as well. The time came for stateful transactions. We now want to keep track of whom is logged in, what he/she has done and where he/she has been.  We want &lt;strong&gt;state&lt;/strong&gt;.  Only now, we're making the dog walk a tight-rope and juggle chainsaws. We've begun to do things like stuff variable values in responses and re-collect them on the next request or write database records to keep track of transaction state on the web server; all to mimic stateful transactions.  Dozens of web frameworks have surfaced, many having a mechanism built-in to get around the stateless nature of HTTP.  We now have technologies that effectively mix web and desktop technologies, something I like to call "webtop clients", manifest in &lt;a href="http://www.adobe.com/products/flashplayer/"&gt;Adobe Flash Player&lt;/a&gt;, &lt;a href="http://java.sun.com/applets/"&gt;Java Applets&lt;/a&gt;, and &lt;a href="http://silverlight.net/"&gt;Microsoft Silverlight&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;The Pattern&lt;/strong&gt;&lt;br /&gt;Do you see the pattern? Since the inception of the Web, web applications have evolved significantly; from static content to dynamic content, from statelessness to statefulness, from browser-only technology to browser-desktop mixture technologies. However, their evolution is not toward something new and unseen, but to something all too familiar, the thick client.&lt;br /&gt;&lt;br /&gt;With today's frameworks for desktop application development and deployment, the justification for many of today's web applications, especially web apps internal to a company, are no longer legit.  Take a look at Apple's &lt;a href="http://www.apple.com/itunes/"&gt;iTunes&lt;/a&gt;; everything Apple flows through that application.  As for deployment, Microsoft's &lt;a href="http://msdn2.microsoft.com/en-us/library/t71a733d(VS.80).aspx"&gt;ClickOnce&lt;/a&gt; for .NET Windows applications and &lt;a href="http://sparkle.andymatuschak.org/"&gt;Sparkle&lt;/a&gt; for Cocoa applications make deployment and updates so easy, it's ridiculous. Don't get me wrong, web applications do have their place, but the number of scenarios for which they are the optimal choice is dwindling; sure they can be used for nearly everything, but what's the point?  It all comes down to the following question: in a particular situation, what problem does a web application solve that a desktop application does not?  When you realize how many times you can't find any, you'll realize that web applications are overrated.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2373396595325606587-4009401223252024652?l=code-ronin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-ronin.blogspot.com/feeds/4009401223252024652/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2373396595325606587&amp;postID=4009401223252024652' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/4009401223252024652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/4009401223252024652'/><link rel='alternate' type='text/html' href='http://code-ronin.blogspot.com/2008/01/web-applications-are-overrated.html' title='Web Applications are Overrated'/><author><name>travis heseman</name><uri>http://www.blogger.com/profile/04061311168580780466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2373396595325606587.post-3682571201493752984</id><published>2008-01-18T12:24:00.001-08:00</published><updated>2008-09-17T17:40:48.931-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='application'/><category scheme='http://www.blogger.com/atom/ns#' term='datamining'/><category scheme='http://www.blogger.com/atom/ns#' term='SOA'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>Dataundermining</title><content type='html'>Suppose &lt;span style="font-style: italic;"&gt;Bob&lt;/span&gt; is your investment agent. Bob uses many tools and algorithms to determine the best investment for your money; he always seems to make good decisions and you profit from his skill and judgment. Bob's investing ability is surpassed only by his technological ineptitude. He knows nothing of computers, yet remains meticulous with his record-keeping. After every day of investing, he places a paper log of his investment activities in the second drawer of his desk.&lt;br /&gt;&lt;br /&gt;Suppose further that &lt;span style="font-style: italic;"&gt;Sue&lt;/span&gt; is your accountant. She, too, is great at what she does, as long as she is provided with complete and accurate information. The trouble resides with you and your inability to provide her with the information she requires on the part of your investments (since Bob takes care of that for you). Luckily, Bob is a trusting soul, and he has provided you with a key to his office in case of emergencies. You know about the log in his desk drawer and realize how much easier your life would be if Sue could obtain your investment activity information straight from the source. In a shady move, you give a copy of the key to Sue and inform her about the information in this log. Periodically, Sue lets herself into Bob's office and searches the log for investment activity he's done on your part. She uses this information to keep accurate tabs on your investments without having to badger you.&lt;br /&gt;&lt;br /&gt;A while later, Bob completes a computer course at the local college. He now recognizes the productivity benefits of electronic record keeping and transfers the records from the log to his PC. Bob realizes that maintaining a physical copy of his records has no merit and only takes time away from doing his job; he shreds the log, and against his nutritionist's better judgment, fills the desk drawer with soda and snacks.&lt;br /&gt;&lt;br /&gt;Later that evening, Sue comes to inspect the log. She's alarmed to find the log has been replaced with soda, candy bars and other goodies. You get a phone call from an upset accountant, demanding that you have Bob continue to put the daily log in his desk drawer, "... or we're back where we started", she says.&lt;br /&gt;&lt;br /&gt;This is an example of data&lt;span style="font-style: italic;"&gt;under&lt;/span&gt;mining; Bob and his record-keeping system have been undermined, and by my count, you have roughly four options:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Demand Bob continue with his paper logging practices so as not to keep Sue from efficiently keeping track of your investment activity. (This would also lead to a confession about your giving Sue the proverbial &lt;span style="font-style: italic;"&gt;key to the kingdom&lt;/span&gt; and her sneaking data out from under Bob.)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Revert back to your having to keep Sue manually apprised of Bob's investment activities on your behalf.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Do nothing, letting Bob continue on oblivious to the situation and forcing Sue to attempt her job with incomplete information.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Force Bob and Sue to communicate with each other.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;I personally don't like any of options 1 through 3.&lt;br /&gt;&lt;br /&gt;Option 1 requires Bob to either abandon his path of evolution or do double the work by keeping both his electronic records and his physical records up-to-date. Neither suit your needs from an investment point of view. You want Bob to be able to grow and evolve with the rest of the world; after all, he's making your bank. You also want Bob to spend the maximum amount of time and resources furthering your investment success; maintaining two forms of records detracts from his ability to do that.&lt;br /&gt;&lt;br /&gt;Option 2 requires extra effort on your part. Your keeping Sue updated on your investment activities is just not in the cards. The speed and accuracy with which you could deliver information to Sue severely handicaps her ability to keep your financial records straight. After all, let's face it, you're no expert on your investments, that's why you have Bob.&lt;br /&gt;&lt;br /&gt;Option 3 is what I call &lt;span style="font-style: italic;"&gt;classic&lt;/span&gt;. You recognize that you have a broken system, but too much effort is involved in keeping everybody happy, so you just sit on your hands and accept the fact that one piece of the equation is just not going to be broken.&lt;br /&gt;&lt;br /&gt;Clearly, I saved the appropriate option for last.&lt;br /&gt;To me, option 4 is the option that should have been chosen in the first place. When Bob and Sue were hired, they should have been informed of the need for your financial information to be shared and communicated to other entities (including each other) upon request by one of those entities. In short, they need to open themselves to integration.&lt;br /&gt;&lt;br /&gt;The preceding is an analogy for a problem with which I've wrestled countless times. A shop creates an application to solve a particular business problem. This application requires a database for persisting data relating to the problem domain, and consequently one is created along-side the application's development. Sooner or later, this application is deployed, beginning its life in the wild. We'll refer to this application as "Application 1".&lt;br /&gt;&lt;br /&gt;Later, another application is developed to solve one of the many other problems the business faces. Nothing is really special about this application, which we will call "Application 2" for lack of a better name, except that in order to fulfill its use-cases efficiently, it requires information created and managed by Application 1. No problem, we'll just hook in to Application 1's database and retrieve some of this data. Herein lies our problem. We've just undermined Application 1 in the same way that Sue undermined Bob in our analogy. We don't yet notice any adverse effects, and Application 2 is completed and released to the wild.&lt;br /&gt;&lt;br /&gt;Sometime later, the business has acquired a list of new features and use-cases for Application 1, along with some obsolete use-cases that can be removed. After inspection of the requirements, a design for the new version of Application 1 has been formulated. This design calls for a few additions to existing structures and a few changes in how existing data is structured, greatly simplifying the application development work.&lt;br /&gt;&lt;br /&gt;Suppose the development team quickly realizes that Application 2 depends on the data structures of Application 1's database. Data&lt;span style="font-style: italic;"&gt;under&lt;/span&gt;mining at its finest. Updating Application 1 is not going to be as easy as the design suggested, and they have a very tough choice to make. Do they update Application 2 as a part of the Application 1 upgrade? Sounds like scope-creep to me, and the business may not have budgeted for multiple applications to come under the knife. Do they re-think their approach and make updates to Application 1's database and use-cases to "work-around" the dependency that's been created? This would keep the data structures that are depended upon by Application 2 intact, but could potentially create other illogical data structures and increase the overhead &amp;amp; complexity for the interaction of Application 1 and its database. It would also make further evolution of Application 1 more difficult. I hope its clear to see this scenario resembles option 1 of our analogy.&lt;br /&gt;&lt;br /&gt;Suppose again the development team realizes the dependency situation on Application 1's database. Another solution consists of an attempt to remove the integration piece from Application 2 and for the user to consult Application 1 prior to use, but this brings about a greater potential for error and a loss of productivity. In this case, we're closely matching option 2 of our analogy.&lt;br /&gt;&lt;br /&gt;In a different light, suppose the development team is oblivious to processes outside of Application 1. Development ensues, the structures are modified, the application update is complete and Application 1 v2.0 is released. Now, the effects of data&lt;span style="font-style: italic;"&gt;under&lt;/span&gt;mining are manifest after the fact. Application 2 crashes. What happened? It's rather simple; Application 2 is looking in Application 1's database for a data structure shaped like v1.0 and got a data structure shaped like v2.0. It follows naturally that issues should arise from a scenario like this, a scenario very closely mirroring option 3 from our analogy.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_agDlAHcUuG4/R5ELWj0SrAI/AAAAAAAAAAQ/gMBgrcvvV-o/s1600-h/Undermined.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5156915530515524610" style="margin: 0px 10px 10px 0px; float: left;" alt="" src="http://4.bp.blogspot.com/_agDlAHcUuG4/R5ELWj0SrAI/AAAAAAAAAAQ/gMBgrcvvV-o/s400/Undermined.jpg" border="0" /&gt;&lt;/a&gt;&lt;br/&gt;&lt;br /&gt;The illustration above exemplifies how dataundermining becomes a problem. Application 2 has accessed Application 1's database to fulfill its use-cases, inhibiting Application 1's ability to evolve. The most robust solution uses techniques of SOA to allow each application to communicate without acquiring illegitimate access to the other's data store. Whether that be through text-based communication, like Web Services, or binary-based communication like CORBA, DCOM or RMI, the best scenario for all involved requires letting each application be solely responsible for its own data, and building into each app the communication abilities for access to its data.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2373396595325606587-3682571201493752984?l=code-ronin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-ronin.blogspot.com/feeds/3682571201493752984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2373396595325606587&amp;postID=3682571201493752984' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/3682571201493752984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2373396595325606587/posts/default/3682571201493752984'/><link rel='alternate' type='text/html' href='http://code-ronin.blogspot.com/2008/01/dataundermining.html' title='Dataundermining'/><author><name>travis heseman</name><uri>http://www.blogger.com/profile/04061311168580780466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_agDlAHcUuG4/R5ELWj0SrAI/AAAAAAAAAAQ/gMBgrcvvV-o/s72-c/Undermined.jpg' height='72' width='72'/><thr:total>1</thr:total></entry></feed>
