"That design might work for a stateful desktop app, but it isn't appropriate for the stateless web."
"O/RM isn't appropriate for stateless environments like HTTP!"
"This component wasn't made for the stateless environment of HTTP!"
"...but HTTP is stateless!"
If you've done any sort of web development, you've probably heard proclamations like these. You may have even made them yourself.
But what do they really mean? Do they add any value to the conversation?
Stateless refers to an architecture where each HTTP request is fundamentally detached from requests that came before, and unrelated to requests that will follow.
In a stateless world, the browser initiates a TCP connection on port 80 - traditionally, or port 443 if it's a secure connection - and then sends some basic commands, such as the desired document (e.g /images/coolpicture.jpg), along with this-request preferences like the user's desired language.
With no prior information about the caller - acting only on the newly generated information in the request (e.g. the document requested, along with user submitted form values) - the server sends the results.
> GET /images/coolpicture.jpg
< the binary data for /images/coolpicture.jpg..
After the single request is serviced, the connection is torn down in this stateless scenario. The desired goal was to service each request as quickly as possibly, freeing the resource-heavy, finite-quantity connection to service other callers.
Maximum output with minimum resources.
This served the early web very well. Mirsky's Worst of the Web could be served out to thousands of anonymous consumers with gusto on minimal hardware, fulfilling the liberal information sharing origins of HTTP.
For a historic analogy, think of the 411 telephone service - you dial the number and establish the connection. You tell the operator the person whose number you require, and they provide a number in response. The call is disconnected, freeing the line and the operator for the next caller.
This is stateless in that the service relies upon no contextual information preceding the call to provide the service, allowing a small number of operators and connections to handle a large number of lookup requests, needing no resources beyond a simple phone book.
A stateful 411, on the other hand, would be one where you called 411 and left the phone off the hook, maintaining the connection for perhaps days at a time. With each number lookup request, they would try to interpret what you really mean based upon the requests that came before.
"Earlier you asked for a bait store on Main street, and now you're looking for a tackle store. I'm going to guess that you probably want one on or near Main street. The number is..."
Such a stateful connection wouldn't even require you to maintain the call - they could just pull up your records based upon the calling phone number, immediately having the history of your interactions to draw from in a stateful manner, regardless of the transience of the individual call.
The stateless definition of HTTP was used to contrast with existing services like telnet and FTP, where a TCP connection (itself a stateful protocol) was made, after which a state was maintained and modified from command to command -- whether you were logged in, what directory you were in, what application was running, and so on.
The state was alive and changing until the connection was dropped, with a block of server resources dedicated to keeping alive a world just for you.
That design worked for those services because connections were generally "higher value" per request - a long running file transfer that couldn't serve many clients anyways, as a function of the large number of bytes per request; a professor running some batch jobs; etc.
Most readers will know that almost all websites these days appear to be stateful.
You log on. It presents data that is specific to you, using preferences that are individual to you. As you do things, the environment changes and adapts, incorporating your interactions into following requests.
This isn't just an illusion, or a bastardization of the web: THESE WEBSITES ARE STATEFUL.
So how did the web sneak up and become stateful on everyone? Well, generally via the magic of cookies (alternately via URL-appended session identifiers to simulate cookies), an addition to the HTTP protocol that was first implemented by Netscape back in 1995.
A session cookie is often nothing more than a unique identifier (preferably with enough entropy that users can't guess each other session identifiers, for instance a randomly generated GUID), passed to the server on each request, allowing the web server to tie requests together, building a set of session data to provide state for a given client - The logon form changes the home page render changes the topic listing changes the calendar selector changes the news view, and so on, with each page having available a set of stateful information about the client, forming a sort of virtual "persistent connection" over many individual, seemingly isolated HTTP requests.
Ignoring the fact that in the modern world HTTP connections are reused (given that a client will often request dozens or more documents to build a single page - or in the case of Digg about 37,528 - it was found to be cheaper to just let the client reuse a built connection for multiple requests), often people differentiate HTTP from being "stateful" because it doesn't maintain a constant connection for the entire session.
Yet what is a connection? In this case it would be TCP, a "stateful" protocol. TCP is stateful in that it changes based upon what has happened before, and each packet for the duration of a connection relies upon those before them getting through okay.
You can establish a connection, let it sit for a while, and occasionally pass data back and forth.
TCP is stateful in contrast to IP (or its very light encapsulation, UDP), which is individual packets that live or die by themselves, with no consciousness of packets that came before, or those that will follow.
But wait, isn't it TCP/IP? TCP on top of IP?
Why yes, it is. TCP is fundamentally "IP with cookies", allowing it to maintain session state, tying many stateless packets together into a nice, clean stateful correspondence. This differs little from HTTP with cookies, a fundamentally stateful protocol when coupled in virtually any post-1996 implementation, where the idea of sessions and statefulness are the norm.
So why does everyone keep yabbering nonsense about HTTP being stateless (pedantically true, but practically irrelevant and entirely misleading)? Why do so many people talk about the web being stateless in the face of endless contradictory evidence?
I think it's just a cop out: People want to validate their crappy web apps - possibly due to laziness or a desire to migrate back to fat apps - so they clutch onto the justification that it's a fundamental limitation of the platform that limits their abilities, constrains their design or forces them into hackish implementations.
In reality, the web that we've been developing against for the past 10 years has allowed tremendous statefulness, including building up and maintaining enormous quantities of server-side state for every session (just like a fat app or a DCOM component): Just because that isn't appropriate for a very high volume, low value-per-transaction anonymous user website should in no way guide you in your implementation of a low user count, very high value-per-transaction vertical market web app.
You have the ability, and the mandate, to do what's right for the problem, and no one solution or dogma fits all web needs.
Way back in junior high I had a good friend who was a huge fan of military aircraft.
His bedroom walls were covered with huge, hard to procure and often expensive posters of these deadly devices. His desk featured an actual (albeit non-functional) 20mm shell, of the variety used in the depleted-uranium spewing gatling gun.
His favourite military fighter jet happened to be the F-15 Eagle.
Feeling a little left out, I started pouring over his resources, carefully reading his encyclopedia's of fighter aircraft, absorbing all of their attributes. I decided that my favourite fighter jet was the F-14 Tomcat: Clearly its ability to land on carriers, its swing-wing engineering, and the long range phoenix missiles it supported, made it the superior aircraft.
There was no way the F-15 Eagle compared, I argued. The F-14 Tomcat was obviously the choice of those in the know. The enlightened ones, if you will.
Yet the reality -- and I think my friend Brian always knew it -- is that I chose the F-14 primarily because it wasn't the F-15. After picking a natural alternative, I started building layers and layers of justifications for my decision.
I see the same sort of thing fairly typically in software development: Big up front design versus agile designs; Getters/Setters versus fields; namespace naming guidelines of type A or type B; variable naming standards; stored procedure naming standards (or the religious "stored procedure versus dynamic SQL" argument that rages on in teams across the lands); the sorts of types to use for primary keys; the languages and platforms to choose; whether or not to use XML, and what to use it for.
So many times, it seems, people choose their positions based not on actual analysis and honest beliefs, but rather because they're countering someone else in their team -- especially when attempting to undermine authority, actual or perceived -- or they battling someone else in their organization (that dastardly team in Sector G that's trying to get kudos by setting the development guidelines!), or they're deriding someone in the industry.
Often They're just trying to be different and difficult, and the beauty of software development is that there are many, many right ways to do it, and it's easy to find allies in discussion groups to assure one that everyone else is idiots, and their new position is the One True Way.
It's easy to appeal to authority, given that there's some big name or organization that, in some form, promotes just about every software development practice and standard imaginable (Microsoft is a particularly good example of this, as throughout the organization they follow so many standards and practices, that one can easily find an example conforming with their dogma, using it as an example that it's the "Microsoft way", ignoring the many exceptions).
Of course all of this doesn't preclude disagreement on standards and processes and techniques -- people often truly disagree because they legitimately and rationally believe something different. In a full of intelligent, self-directed professionals, such disparate beliefs and conclusions can be enormously beneficial. The problem is when interpersonal issues materialize as technical disagreements.
While it's evident that Microsoft is staffed with a lot of top-notch people, history has empirically demonstrated that they have quite a few dregs as well: Just recall how disastrously the whole .NET thing was handled circa-2000.
For those who forgot, suddenly every product (including those finished or on the verge of being finished) became a part of the .NET vision, even if they had absolutely no interaction with the .NET technology stack: Windows Server.NET, Exchange.NET, Messenger.NET, SQL Server -- all a part of the .NET generation -- just as Microsoft declared everything in the generation before a part of the DNA vision (I still hear developers talking about "Microsoft DNA", not really sure what they're talking about).
As a developer who was heavily involved with the betas of what we call .NET today - a runtime and a framework, and the associated tools, for building next generation solutions - I really had no idea what .NET was in Microsoft parlance. Just as ActiveX got muddled into a meaningless term, .NET was being hijacked to basically mean "buy whatever is new or coming out soon".
Eventually that insanity stopped, and .NET collapsed down to a sortof virtual-machine runtime, a framework, and a set of tools. .NET 1.0 was one runtime, one framework, and Visual Studio.NET 2002. .NET 1.1 was a new runtime, a new framework, and Visual Studio.NET 2003. .NET 2.0 was a new runtime, an expanded framework, and Visual Studio 2005 (note the dropping of .NET on the naming, given that Visual Studio, as always, also makes non-.NET applications). There are countless assemblies and extension libraries available targeting each of them, and of course I can make libraries tomorrow that target .NET 1.0, .NET 1.1, or .NET 2.0, and it doesn't magically evolve them into .NET 3.0.
Well it looks like Microsoft is at it again. They've decided that Vista's technology platform, WinFX (which will be partially backported), is so great that it can't be just a set of assemblies or systems that the .NET runtime interacts with. No, it must be .NET 3.0! So now if you have the .NET 2.0 runtime, the .NET 2.0 Framework, targeting it with Visual Studio 2005, and you add in the WinFX framework...voila, you have .NET 3.0.
Insanity. Absolute, unbelievable insanity. Perhaps there's some amazing explanation -- for instance that their April Fools project ran a little long, and they just got the output out -- but I suspect it is just more of the same that we saw circa-2000. Some short-term euphoria over a gonna-be-released-soon project has them screwing with the terminology yet again.
Already the boards are full of "So....does this mean WinFX comes with LINQ?" (LINQ is one of the technologies promised for the next real wave of .NET)
[The static location of this piece can be found at this address]
For the past while I've been using Visual Studio Team Edition for Software Developers, one of its benefits over the Professional Edition being the inclusion of static code analysis functionality right in the IDE.
This functionality comes via the FxCop codeset, which is an excellent -- albeit unpolished -- freely available tool for analyzing the probable code quality of Intermediate Language assemblies, testing code to ensure compliance with naming standards, best practices, and highlighting areas of code that are suspect. While it's less than pleasant starting FxCop analysis from scratch on long existing project -- to be met with hundreds upon hundreds of error messages -- it's a painless process if you add it to your quality checks early on.
The standalone FxCop is largely the same as the VSTE version, and in some ways is superior. For instance that it retains the ability to actually pass configuration settings to rules, rather than accepting whatever the defaults for the rule are.
One of the few differences between the standalone application and the VSTE-included version are the addition of several new maintenance checks in the Team Edition code, one of the most useful being the cyclomatic complexity checks. Cyclomatic complexity, for those who haven't come across it before, is often used to roughly gauge the complexity of a piece of code, to determine likely candidates for refactoring, and to identify what will likely become a maintenance problem in the future. Finding the most complex pieces of code often brings you to the buggiest code as well.
Given that I still use FxCop, both the .NET 1.1 and .NET 2.0 versions (not least because the integrated version offers no ability to configure settings for rules, instead only allowing you to wholesale enable or disable. This eliminates the ability to set thresholds for tests such as the cyclomatic complexity rules), the lack of consistency between the two versions was an annoying gap.
So I implemented a simple cyclomatic counting rule for the standalone FxCop. While in there, I added checks for statement count (the number of intermediate language "statements", which can be indicative of overly complex methods), and callout count (e.g. callouts to other methods, again which can be an indicator of overly complex/convoluted methods).
As one added benefit, I added the ability to log all of these metrics to an SQL-capable OleDB destination (e.g. SQL Server, Access, etc). If you configured an OLEDB connection string, as detailed below, you can do data analysis after a run to create pretty reports of the complexity distributions of your projects, and so on.
yafla FxCop
Rules for .NET 1.1 (e.g. FxCop 1.32)
yafla FxCop
Rules for .NET 2.0 (e.g. FxCop 1.35)
Like any tool of this type, there is only a moderate correlation between the metrics measured and actual code quality or maintainability: It is entirely possible that the optimal implementation is a highly-complex, lengthy method. This tool only provides guidance, helping to determine which code should get a complexity analysis, however from there experience and good judgement have to be applied to determine if it's really a fault. If you're using the .NET 2.0 version of FxCop, make use of the SuppressMessage attribute on methods that are necessarily highly complex.
Drop yaflaRules.dll in your FxCop Rules subdirectory (e.g. C:\\program files\\Microsoft FxCop 1.32\\Rules).
If you want more advanced settings, configure FxCop with your targets and selected rules and then save the project file. Open the newly created .FxCop file in an editor (for instance notepad) and find the <Settings /> element. Expand it to an opening and closing tag (e.g. <Settings></Settings>), and between it add
<Rule TypeName="MethodComplexity"></Rule>
Between the Rule element add any of the following entries as Name attributes of an Entry element (as exampled following) -
Connection String - an OleDb connection string
determining where it will log metrics. e.g.
Provider=SQLNCLI;Server=(local);Database=Analysis;Trusted_Connection=yes;
Target Table - The target table for metric
logging. Default -
MethodComplexity
Cyclomatic Critical
Error - Level at which a critical error is
triggered. Default - 60
Cyclomatic Error - Level at which an error is
triggered. Default - 50
Cyclomatic Critical Warning - Level at which a
critical warning is triggered. Default - 45
Cyclomatic Warning - Level at which a warning is
triggered. Default - 40
Cyclomatic Information - Level at which an
infromation event is triggered. Default - 20
Cyclomatic Recommended - Recommended level.
Default - 20
Statements Critical Error - Statement count at
which a critical error is triggered. Default - 500
Statements Error - Statement count at which an
error is triggered. Default - 350
Statements Critical Warning - Statement count at
which a critical warning is triggered. Default - 250
Statements Warning - Statement count at which a
warning is triggered. Default - 200
Statements Information - Statement count at which
an information event is triggered. Default - 150
Statements Recommended - Recommended maximum
statement count per method. Default - 100
Callouts Critical Error - Callout count at which a
critical error is triggered. Default - 100
Callouts Error - Callout count at which an error
is triggered. Default - 75
Callouts Critical Warning - Callout count at which
a critical warning is triggered. Default - 50
Callouts Warning - Callout count at which a
warning is triggered. Default - 40
Callouts Information - Callout count at which an
information event is triggered. Default - 30
Callouts Recommended - Recommended maximum callout
count per method. Default - 30
For instance, you might end up with a <Settings> element that looks like the following:
<Settings><Rule TypeName="MethodComplexity"><Entry Name="Connection String">Provider=SQLNCLI;Server=(local);Database=Analysis;Trusted_Connection=yes;</Entry><Entry Name="Callouts Warning">100</Entry><Entry Name="Cyclomatic Critical Warning">500</Entry></Rule></Settings>
If you opt to take advantage of metrics logging, the destination table (which will be default will be MethodComplexity, unless overridden with the Target Table name entry) requires the following columns:
ContainingType - text (e.g.
nvarchar(255))
MethodName - text (e.g. nvarchar(255))
Cyclomatic - int
Statements - int
Callouts - int
e.g.
CREATE TABLE [dbo].[MethodComplexity](
[ContainingType] [nvarchar](255) COLLATE
SQL_Latin1_General_CP1_CI_AS NOT NULL,
[MethodName] [nvarchar](255) COLLATE
SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Cyclomatic] [int] NOT NULL,
[Statements] [int] NOT NULL,
[Callouts] [int] NOT NULL
) ON [PRIMARY]
Hopefully someone finds this interesting. It scratched my itch.
I've been playing with Team Foundation Server, Whidbey (Visual Studio 2005), and Yukon (SQL Server 2005) since early in the beta cycles. All three of them are remarkable products, with enormous advances over their predecessors (in the case of TFS, I'm spuriously considering Visual SourceSafe the predecessor, although TFS is a elephant compared to the mouse of VSS), and all of them should be critical components for anyone developing in the Microsoft camp.
All three of them also happen to be a little unpolished, with odd little quirks and errata, hilariously incomplete documentation, and a tendency towards resource hoggishness.
One thing I've found remarkable, however, given that the three of them have been in final form for anywhere from two months to over half a year, is how little real information and first-hand accounts are available online. I'm continually hitting roadblocks where there are marginal functions or incomplete documentation, and it's surprizing to find zero references to the same problems or questions on any of the normal forums (e.g. Google Groups, online searches, etc). Among the development community, outside of the desperate-to-get-anointed-free-support-MVP crowd, they just don't have the aura of excitement they probably deserve.
Given that there are literally millions of developers and technology hobbyists out there, it's usually the case that any problems one faces are well trodden, and a quick search on the newsgroup usually yields exactly the answer one needs, so this dearth of time-travel support really is disconcerting.
The only conclusion I can draw is that there simply aren't that many developers seriously using these technologies. Visual Studio 2005 is of course seeing some use, but there are still huge armies of developers sticking with 2003 (given the break between .NET 1.1 and 2.0). A lot of SQL shops are still taking a wait-and-see approach with 2005. Team Foundation Server, primarily because of the cost of the Team editions, and the cost of a TFS Server license if you grow past a 5-user team, seems to be fairly rare.
Before I go about possibly reinventing the wheel, I thought it worthwhile to ask: Could anyone point me to .NET / Windows server modules for SXIP 2.0 and/or OpenID? They're both fairly trivial identity solutions, so if I can't find one I'll implement one or both. Not only for personal needs, but because I can see some uses for them in client projects.
Thank you kindly.
ASP.NET has improved dramatically with v2.0, to the point of making ASP.NET v1.x look like a bit of a hack job. One of the great improvements covered in this entry is the addition of Master Pages.
Master Pages allows you to define a template layer (and coupled back-end code) to be used on content pages using that master page. For instance a master page might define all linked scripts, CSS, and script blocks, along with a navigation header and footer that exist on all pages on the site (or at least those pages using the master page). An ugly example sits a subdirectory away -- at the root pages for yafla, where the navigation header and footer exist in a master page.
ex. MasterPage.Master
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>My Master Page - This Title Will Be
Overridden
By the Title Element
In Content
Pages</title>
<link rel=stylesheet type="text/css"
href="stylesheet_in_all_pages.css" />
</head>
<body>
<h3>This is my universal
header!</h3>
<form id="form1" runat="server">
<div>
<asp:contentplaceholder id="ContentPlaceHolder1"
runat="server">
</asp:contentplaceholder>
</div>
</form>
<h3>© 2015 Robot
Inc.</h3>
</body>
</html>
You can also define code for the various events in the master page,
which will run on the pertinent content pages.
Content pages then define what will fill the content block (or multiple content blocks as the case may be), and of course implement their own back-end code.
<%@ Page
Language="C#" MasterPageFile="~/MasterPage.master"
AutoEventWireup="true" CodeFile="Default2.aspx.cs"
Inherits="Default2"
Title="This is my overridden title" %>
<asp:Content ID="Content1"
ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
This is my content for this content page
</asp:Content>
Very trivial.
Of course this isn't the only way that this result could be achieved -- I could derive from a page object that imperatively creates all of the common elements, or I could use multiple user controls that defined the basics, but neither of those solutions, or similar workarounds, seem as elegant as master pages to me. There are equal or superior solutions in other platforms, however I'm sticking to the topic of ASP.NET in this entry so they are irrelevant.
The one hiccup I faced in the use of master pages was my desire to have meta keywords (which exist in the header) vary by page, despite the fact that the meta keyword is basically dead. I want the keywords to vary, similar to the way I can declaratively override the title in content pages. Unfortunately this required some code workarounds, which in my case included adding a public property on the master page, MetaKeywords, with a default keyword list, which I then added to the header in the PreRender stage of the master page (the following example is simplified for demonstrations sake, however a real implementation would scan the headers to ensure that the pertinent header doesn't already exist before adding it).
public string MetaKeywords = "default keywords";
protected void Page_PreRender(object
sender, EventArgs e)
{
SetMetaValues(this.Page.Header,
"keywords", MetaKeywords);
}
public static void
SetMetaValues(System.Web.UI.HtmlControls.HtmlHead head, string
name, string content)
{
HtmlMeta metaValue =
null;
metaValue = new
HtmlMeta();
metaValue.Attributes.Add("name",
name);
metaValue.Attributes.Add("content",
content);
head.Controls.Add(metaValue);
return true;
}
Any content page could access its Master property to set the property, and the meta keywords would be appropriately set when the page was rendered. By using the MasterType directive the Master property of the page automatically resolves to the proper type.
Unfortunate that a declarative mechanism wasn't added for arbitrary header elements in the content pages.
The goal of master pages, of course, is to avoid the scourge of copy/paste coding: Unnecessarily having a single line of code in multiple places is an evil in software development, yet it's often the easy, thoughtless solution, yielding volumes of redundant code that invariably diverges and causes maintenance problems for years to come, reducing the quality and agility of the codebase.
I despise copy/pasted code. It truly is a peeve of mine.
When analyzing the quality of code bases, one of the first checks I usually perform is to use one of the automated code duplication checkers (available for most languages). There is a remarkable correlation between code duplication rates and code quality.
The benefit of master pages isn't limited to a single master template, however, but instead you can actually layer multiple master pages. For instance on the yafla site the services category pages use the Services master page, adding additional service specific back-end code and layout, while it uses the web site wide master page. It mirrors the templated way in which many websites are developed.
The downside of layered master pages is that the GUI team apparently didn't have time to build multiple level parsing into the web designer -- wherever you're working on content pages that have more than one level of master pages above them, you are limited to the source view. To attempt otherwise yields a "Design view does not support creating or editing nested master pages. To create or edit nested master pages, use the Source view." Unfortunate, but not deadly.
As an aside, one of the big improvements with ASP.NET v2 is better support of per-page development, similar to classic ASP and competitors such as PHP. This solves one of the primary problems many had with ASP.NET, which is that they didn't prefer to work within the "web site as a monolithic application" model that ASP.NET v1 pretty much enforced. Strangely the improvements bringing these benefits has been met with little fanfare, and few are even aware of it. I do plan on doing a feature on it shortly.
Tagged: [Software Development], [Programming], [Software-Development]