July 2006 Entries
I recently introduced an internal tool for writing Subversion Hooks that we developed for internal use at VelocIT. We made the source code and binaries available to the general public and there was enough interest (hey, it only takes two or three and I’m sold!) that we decided to make an Open Source project out of it hosted on SourceForge. This is our company’s first Open Source project and we hope many will find it useful.
I thought about using CodePlex to try it out, but in this particular case I figured it would be less of a pain to host it in a Subversion repository since this is a tool for Subversion users after all.
At a Glance
- Mailing List
- captainhook-devs at lists dot sourceforge dot net
- Commits Mailing List
- captainhook-commits at lists dot sourceforge dot net
- Subversion Repository
- https://svn.sourceforge.net/svnroot/captainhook/trunk
- License
- BSD
The mailing list might not be ready at the time you read this If you would like to have Subversion commit access, please join the mailing list and send an email to the list with your SourceForge username. Please only request access if you are planning to work on it in the next few months. I will probably cap it after some point just to keep the process sane (not that I am expecting a flood of requests, but you never know).
And of course CaptainHook is licensed as BSD. Any code contributions should have their copyright for the contribution assigned to VelocIT, the maintainer of the project (an informal email ackowledgement is sufficient). As I mentioned before, assigning copyright when contributing is a standard operating procedure and nothing insidious. You do not lose your own rights to use the code elsewhere since the code is licensed as BSD.
Enjoy!
Update Corrected my pop-culture iconography mix up.
Thomas Eyde brings up a great point in the comments section of Scott Hanselman's post about SandCastle and the death of NDoc.
It's sad to see good projects die, especially when programmer support is a main reason.
But on the other side, it's not that easy to join these projects. How many of them advertise? How often do we see "Developers wanted on [your favorite project]"?
I think these projects must advertise what they need. Do they need C++ expertise? Java? C#? UI design? How do we know what to do? Where are the tasks listed? How do we assign to them?
Open Source project that are lacking in developer support probably need to tear off a page from the corporate playbook and be a bit more savvy about recruiting developers. I often hear developers wistfully day dreaming that if they just open source their pet project, legions of developers will take up the banner and join in.
Ha! Hardly! Recruitment is necessary and fundamental to an OS project. So much so that SourceForge has a tab dedicated to the topic in the admin section of a project.
Seeking skilled developers (and people who are interested in the goals of your project) is one of the most important activities performed by project administrators.
Sourceforge also provides a Help Wanted page to recruit volunteers for a project. There is also a discussion forum dedicated to recruitment.
But by no means limit yourself to SourceForge. Consider putting a free ad in CraigsList or in the forums of other developer communities.
The second comment on Scott's post by Martin Bennedik questions the donation model.
But I don't understand the donation model. Who is supposed to donate the $5? Say I am an employee working for my company, and I am using NDoc. Should I donate the $5 personally? Or should I ask my company to pay the 5$?
When I wrote my original challenge to donate to Open Source projects, I wanted to avoid a
Suzanne Summers Sally Struthers
For just five dollars you can feed the kids of those poor Open Source Developers plea. As Scott points out, monetary contributions are far down on the totem pole of contributions. For the subtext project, I added a
Contribution page that discusses several ways in which developers contribute should they feel the desire.
I think it is the project leader's job to make the barrier to contribution as low as possible.
Technorati : Open Source, Software
Del.icio.us : Open Source, Software
My colleague Jon Galloway has been on a spree of unleashing useful tools lately. I released a few of my own as well.
One thing in common with these little tools and utilities is that we are hosting them on our new company tools site, http://tools.veloc-it.com/.
These tools are little scripts and apps that we use internally to increase our efficiency. Many of them relieve us from the drudgery of boring repetitive tasks. Quite likely, these are the tools that many of you would write yourself if you only had the time (some of them you probably already have written. I swear we did not plagiarize you). We hope you find them useful. There aren’t that many now, but over time we will add to the catalog.
You might be wondering, if these tools make us more efficient, potentially giving us a competitive advantage, why are we giving them away? Good question. I hadn’t thought of that. Jon! Take down the site!
Well the truth of the matter is that it is a way for us to show off how amazingly smart we are so we can get more clients, fame, and adulation.
Uhhh... Ok. Maybe not.
Let’s be realistic. Decision makers and CEOs are not dinking around the web looking for an Abstract Boilerplate HttpHandler.
Not only that, most of what we post here is not supremely innovative rocket science and going to make someone rich. If we had such a tool, you think I’d be blogging about software tools? No way man! I’d be regaling you with stories about the funny looking crab that walked up to the porch of my immense beach house in Hawaii. But I digress.
Part of the reason is certainly to increase exposure of our company, as I know we do some kick-butt work. Over time, we hope that our site will reflect the quality of the work we do for our clients. But our primary reason is the same reason we’ve been posting such tools even before there was a VelocIT. We enjoy helping other developers benefit from what we’ve learned. Likewise, we appreciate the tools and learning shared by other developers that have benefitted us. In the end, I think this sharing elevates the entire profession.
This is just too funny not to link to. Scott Adams, the author of Dilbert, had an epiphany recently which led him to solve the problems in the Middle East.
Recently we had a heat wave in California. My air conditioner broke because, I assume, it is not designed to operate in hot weather. That was the bad news. The good news is that I solved the problems in the Middle East. Allow me to explain.
Please, allow him.
UPDATE: CaptainHook is now an Open Source project on SourceForge.
One potent tool for team communications on a project, especially one with distributed developers, is the simple commit email. Setting up Subversion to send out an email when a developer commits changes to the repository is fairly easy. The Subversion distribution comes with a PERL script (commit-email.pl) that works quite well for this purpose.
At least it did for me until we changed mail servers to one that did not allow SMTP relay. Try as I might, I could not get the script to authenticate with our SMTP credentials. I downloaded various other PERL modules that were supposed to be able to authenticate with no luck. I read the RFC 2554 (seat of your pants reading) and authenticated manually via telnet and compared that to the SMTP logs for the components and realized that for some reasons, these scripts were doing the wrong thing.
That’s when it occurred to me that I could probably write a simple .NET app in a minute that could send out the commit email. As I got started though, I realized that it might be nice to write something that would allow others to easily handle other Subversion hooks. Hence CaptainHook was born.
Hooks in Subversion are scripts (or executables) that are triggered by an event in the subversion version control life cycle. The following are the five hooks supported by Subversion. For a short discussion on how to install the hooks, read this post by Pete Freitag.
Note that (except for post-commit and post-revprop-change) the return value of the script controls whether or not the commit should continue. If the script returns 0, the commit continues. If it returns anything other than 0, the commit is stopped.
| Script Name |
Triggered By |
Arguments |
Notes |
start-commit |
Before the commit transaction starts |
Repository Path and username |
Can be used for repository access control. |
pre-commit |
After the commit transaction starts but before the transaction is commited |
Repository Path and transaction name |
Often used to validate a commit such as checking for a non-empty log message. |
post-commit |
After the commit transaction completes |
Repository Path and the revision number of the commit |
Can be used to send emails or backup repository. |
pre-revprop-change |
Before a revision property is changed |
Repository Path, Revision, Username, name of the property |
Revision Property’s new value is passed into standard input. Can be used to check permission. |
post-revprop-change |
After a revision property is changed |
Repository Path, Revision, Username, name of the property |
Can be used to email or backup these changes. |
My goal was to provide a nice strongly typed interface and a few useful service methods for accessing Subversion. Thus handling a Subversion hook is as easy as implementing an abstract base class and calling methods on a Subversion wrapper interface.
Setup
To setup CaptainHook, simply unzip the exe file and its related assemblies into the hooks folder on your Subversion server. The distribution includes a plugins directory with a single plugin already there. The plugins directory is where CaptainHook looks for other hook handlers. Be sure to update the config file with settings that match your environment.
The next step is to rename the .tmpl file for the hook events you wish CaptainHook to handle. CaptainHook comes with some sample batch files you can use instead, one for each hook. Just copy the ones you want to use from the SampleBatchFiles directory into the hooks directory.
Flow
Now when a commit occurs, Subversion will call the post-commit.bat file which in turn calls CaptainHook with the post-commit flag. This flag indicates to CaptainHook which plugins to load. CaptainHook then looks in the plugins directory for any assemblies with types that implement the PostCommitHook abstract class. It then instantiates instances of these types and calls the Initialize method and then the HandleHook method.
Note that for the time being, CaptainHook is an executable so it has to incur the cost of searching the plugin assemblies every time which may seem like overkill (and it is). However my focus was on the model for CaptainHook. At some point it will evolve to use remoting as a Server Activated Singleton or it may become a Windows Service which fit the model better. Either way, there are ways in which we can incur this cost only once. But for now, this will do and performs well enough.
Assemblies and Classes
Captain hook contains serveral assemblies.
| Assembly |
Purpose |
| CaptainHook |
This is the main console exe and is the starting point for the application. |
| CaptainHook.Interfaces |
Contains the interface definitions. This is the only assembly you need to reference when writing a plugin. |
| CaptainHook.SubversionWrapper |
This is potentially useful as a stand-alone library. It includes the SubversionRepository class which allows running commands against Subversion and receiving the output as a string. This is useful for running straight commands against Subversion. This assembly also includes the SubversionTranslator class. This class wraps the SubversionRepository class and provides an object oriented means to calling the Subversion commands. |
| Velocit.Hook.Plugins |
Contains the PostCommitEmailHook plugin that started this whole ordeal as well as the RequireLogMessageHook which is a pre-commit hook that demonstrates how to reject a commit if no log message is specified. |
Let me know if you find this useful. It is definitely a work in progress as not every command is implemented in the SubversionTranslator class. If it turns out that several people find this useful and want to contribute to the code, I am willing to put the code on SourceForge.
Download CaptainHook from my company’s tools site.
The zip archive contains both the source code and the binaries. I also compiled an x64 version of the exe for you 64bit kids.
My company’s official 1 year birthday was yesterday, and we didn’t even notice. I guess it is a good thing that we are so busy that we fail to notice our own birthday passing us by.
We’ve had a rocky start which involves some interesting stories that I will tell someday when we are far enough removed from them. But things are starting to hum along much better now.
My goodness. Today is Open Source News Day for me.
I just read the sad news that Kevin Downs, the leader of the NDoc project, announced that NDoc 2 Is Officially Dead. NDoc is a wonderfully useful tool for generating nicely formatted CHM and HTML documentation from XML comments within your source code.
What is really troubling about the news is the attitude by some members of the community that was a contributing factor to Kevin calling it quits.
As some of you are aware, there are some in the community who believe that a .Net 2.0 compatible release was theirs by-right and that I should be moving faster – despite the fact that I am but one man working in his spare time...
This came to head in the last week; I have been subjected to an automated mail-bomb attack on both my public mail addresses and the ndoc2 mailing list address. These mails have been extremely offensive and resulted in my ISP temporarily suspending my account because of the traffic volume. This incident has been reported to the local authorities, although I am highly doubtful they will be able to do anything about it.
This has was the ‘last-straw’ and has convinced me that I should withdraw from the community; I’m not prepared to have myself and my family threatened by some lunatic!
Kevin
I cannot understand the mentality of someone who demands for developers to hurry up and release a new version of a free software product. If you are in such dire need of the new version, why not hurry up and contribute?
Having said that, I feel particularly bad that I never contributed to NDoc, yet enjoyed so much use out of it. As the manager of an open source project myself, I understand the inherent difficulty in generating interest and contributions from the community at large.
In general, I try to contribute to Open Source projects when I can. I have contributed a tiny bit of money and some documentation and code to RSS Bandit, I bought a domain name for MbUnit, and of course I contribute to Subtext. But I realized today that I am quite haphazard in how I contribute to useful open source projects. I contribute to some when the mood strikes, but not others.
Challenge
So I offer the following challenge. First, ask yourself these two questions:
- How much money do I save because of Open Source Software?
- Considering how many people spend three bucks for a useless ring tone, is a single dollar too much to contribute to a useful Open Source project?
So let’s ban together and declare today, July 26, 2006, Contribute To Open Source Day. Look at the open source software you use and consider making a tiny contribution to the project if you find it useful. The contribution could be a small monetary contribution such as a buck (or five). Even more valuable is contributing a single bug fix. If you want to contribute to Subtext, consider writing a Unit Test to cover a case that isn’t tested. What better way to learn about writing unit tests?
Gee, using GIMP alone saves me a few hundred that I would otherwise pay for Photoshop. My goal now is to at least contribute something everytime I download a new open source product, whether it be some useful bug reports or feedback, a bug fix, a buck...anything. Just as long as it is something.
I will check back with you next year on July 26 and remind you again to consider contributing. It is worth it.
It is a common notion that there is a lot of ambiguity and disagreement around the term Open Source. While that has been true in the past, I believe that there is much more consensus now because the term has been refined and the taxonomy of software has been improved. Certainly there are still arguments, but I believe for the most part, there is a detente.
Much of the past ambiguity surrounded the philosophical disagreements between those who felt software should be free as in liberty, not only free as in beer. This view essentially holds that derivative works of Free Software must themselves be open and Free. Richard Stallman (who does not want to be associated with the term Open Source) embodies this philosophy, as does the GPL license.
The opposition felt that we should not place such restrictions on software to consider it Open Source.
While this disagreement may have been heated at times, in many ways the core argument around the term Open Source has been resolved by differentiating between Free Software and Open Source. There are now sites dedicated to the Free Software Definition and the Open Source Definition to promote these terms.
While there are certainly disagreements between the two camps on how software should be licensed, in many ways the camps are compatible. When looking specifically at the licenses, it is helpful to think of Open Source as a superset of Free Software. Free Software is necessarily Open Source, but an Open Source is not necessarily Free Software because a Free Software license is in general as restrictive or more restrictive than an Open Source license.
Likewise a Source Available is not necessarily Open Source, but Open Source is necessarily Source Available.
In my hope to be a neologist, I will push the term Source Available (I like it better than my earlier term Code Available) and hope it catches on. If someone coined it before me, at least I hope the claim to fame of making it popular. ;)
I have written on the topic of Open Source software before if you are interested.
Dave Burke makes the interesting claim that Community Server is an open source application. Whether this is true or not of course depends on your definition of the term Open Source. Here is Dave’s definition.
To talk about Community Server and Open Source we should start with a baseline definition of an Open Source application: All of the source code is available. For free.
But is that all there is to Open Source, access to the code? Is mere access to the code the fairy dust that has inspired such a passionate movement in the software community?
Certainly the term Open Source has had a history of ambiguity, so that definition might contain some validity. But I do not think that is the commonly agreed upon minimal criteria for something to be considered Open Source.
Open source isn’t just about whether the source code is available, it is all about the license to the source code.
My favorite definition of open source software is The Open Source Definition (or OSD for short) on the Open Source Initiative website.
The definition starts with the following introduction and then lists serveral criteria for open source software.
Open source doesn’t just mean access to the source code. The distribution terms of open-source software must comply with the following criteria:
The first criteria listed is Free Redistribution which states...
The license shall not restrict any party from selling or giving away the software as a component of an aggregate software distribution containing programs from several different sources. The license shall not require a royalty or other fee for such sale.
Contrast this to the Community Server license agreement 2.0 which states...
3g. Distribution. You may not distribute this product, or any portion thereof, or any derived work thereof, to anyone outside your organization. You are not allowed to combine or distribute the Software with other software that is licensed under terms that seek to require that the Software (or any intellectual propertyin it) be provided in source code form, licensed to others to allow the creation or distribution of derivative works, or distributed without charge.
For many people, the terms of the Community Server license might not be a problem. They are not terribly restrictive. If you plan to use Community Server under the community license your only requirement is to display the Powered By Community Server logo on every page of the site that uses Community Server.
However for many others, these terms are restrictive enough. For example, suppose you don’t like the way development is progressing on Community Server. You cannot fork the code base and start a new project based on the source code. Although a fork may seem like a bad thing, Karl Fogel points out in his book "Producing Open Source Software - How to Run a Successful Free Software Project that the threat of a fork is what keeps the leader(s) of an open source project from being tyrannical. It is this threat of a fork that motivates and requires open source projects to be well run.
Not every open source license is created equal as I pointed out in my guide to Open Source Software Licensing. For example, under the BSD license in which Subtext is licensed, you and I are free to create a commercial derivative version of Subtext and keep your changes to the code closed source and proprietary. That’s right. If you wanted to (and had the ability to), you could package up the Subtext source code in its entirety and start selling it as a packaged product.
Note that you can’t turn around and claim that you have the copyright to the Subtext code. You would only have copyright to your changes to the code. Pretty much the only restriction is that the original license must be retained with with the code, but it does not have to be publicly visible in your site (such as in an about box).
In contrast, with a GPL Licensed project, you could start selling it, but you couldn’t keep your changes closed source without violating the terms of the license.
In the end, I think we need to agree on a term for unique products such as Community Server in which the source code is freely available, but does not fit the definition of an open source product. I suggest the term Source Available.
Please do not misconstrue this as an attack on Community Server or its licensing. I have met both Scott Watermasysk and Rob Howard and they are both very smart and capable leaders of a strong company. Community Server is a great product and deserves the recognition it gets. I am not a zealot and have no beef with closed source products. Certainly my livelihood depends on many such products.
At the same time, I am passionate about Open Source software and it is important to me to help keep the distinctions clear and educate others on what open source software is and the value it provides.
The Subtext team is currently furiously working to prepare the Subtext 1.9 Daedelus release for your blogging enjoyment. Subtext 1.9 will run on ASP.NET 2.0 in Medium trust and contain a few new goodies, though those goodies have been kept to a minimum as the goal was to create a straight port to ASP.NET 2.0. The release date has not been set since my day job and another minor side project has been keeping me busy. My hope is to release somewhere around the first or second week of August.
In the meanwhile, we found the need to respond to some minor bugs in the last release. Thus today we release Subtext 1.5.2 which still runs on ASP.NET 1.1. This is a very minor release, so probably not necessary unless you are affected by one of the bugs listed below.
Probably the most annoying bug was one that caused problems when attempting to create a post using the web-based admin tool. This was due to a misconfiguration on my part of our ReverseDOS configuration file.
It was this misconfiguration that inspired my “stroke of pure genius” suggestion to the creator of ReverseDOS (though methinks he is a bit strong on the compliment). ;)
Here are the release notes.
Version 1.5.2 - Bugfix Release
- BUG: [1492127] Fixed a constraint on the
subtext_Config.BlogGroup in the import
script.
- BUG: [1524172] The User Name Is Being Saved as the Title
- BUG: [1502661] Change ReverseDOS.config - turn on
isRegex="true" flag for
Trusted Addresses.
- BUG: [1514406] Fixed bug in which permissions were not
applied to [Subtext_GetEntry_PreviousNext] stored
procedure.
Download it here.
The number of code contributions to Subtext has been increasing lately which is wonderful! Thanks for sticking with us and we really appreciate the contributions. Keep it up!
Anyone have recommendations for Content Management systems for ASP.NET (ideally 2.0) that can handle a lot of traffic and allow for extremely customized site design? I am curious about which products are out there other than DotNetNuke and Microsoft Content Management Server.
Ideally this is something that you have implemented successfully on a project.
Now this is the ultimate in geekery. K. Scott Allen posts pickup lines in .NET.
Now is this really necessary? Don’t geeks have enough troubles with the ladies without these serious groaners to encourage them?
So naturally, I must respond with my own groaners. Warning, not safe for ... well really anyone who enjoys humor that is actually humorous.
- You can handle my event any time
- What’s your sign? I’m unsigned and I do byte. Rowr.
- I’d like to get
Interlocked with you and see what increments.
- Them hips must be newing up objects real fast because you are putting pressure on my heap.
- The moment I saw you I nearly overflowed my stack.
- Care to reference my private member?
- You Auto-Complete Me.
Update: My favorite comment so far on these groaners comes from Sahil Malik over on Scott’s blog.
Creating .NET pickup lines are a bit like opening a strip bar in antartica. Who are you gonna use ’em on?
My response is that these are not for us geeks. It is for those who wish to land a geek.
My company has a small business account at Washington Mutual. I handle a lot of our banking via the online banking website. For several months now, their online banking website has been an absolute pain to use. After logging in I would be unable to access features that are kind of important such as...oh...I don’t know... the register and bill pay!
What was weird is that I would click on the link and would get some error message about calling technical support or an access denied message. But click on it again once (or 10 times) and it would finally work. Right now, the change password feature doesn?t appear to work.
Over time I would send messages to customer service and never get a satisfying response. Finally, Akumi offered to draft up a letter to the President of WAMU after one particularly frustrating bout with the site in which I was fuming.
Fast forward several days and I get a call from a representative who actually knows something about what is going on with the site and doesn’t blame me or my account. He is helpful and gives me his direct line. As he is walking me through some steps, I notice that some of the URLs end with .aspx. Oh, that is a promising sign!
I mention this to the rep because I never noticed it before. Most of the site runs on classic legacy ASP. The rep informs me that this weekend they have a planned upgrade to ASP.NET. That is definitely a step in the right direction!
Golly gee whiz but you learn something new every day. I was a big fan of Quickdoc viewer as a means to quickly view an HTML rendering of your XML comments directly from within VS.NET.
The reason I say was is that I cannot for the life of me get that sucker to work in VS.NET 2005 on my machine. I am also having problems with GhostDoc. It seems any plugin that uses the new XML file based registration (instead of the registry) doesn’t work for me. Perhaps because I am running as a LUA?
In any case, I just noticed that Resharper has a similar feature, though not nearly as nice as Quickdoc. If you select a method or class and hit CTRL+Q, it will show a window with a more readable version of your XML comments. The following image demonstrates the feature.
I am very happy to find this, but only because I can’t get QuickDoc to work. Quickdoc is far better in how it renders the documentation in pretty much the same way that NDoc does. Maybe I should the Jetbrains folk with a feature request to improve this a tad bit.
When we started VelocIT, one of the early tasks we had was to develop an identity. Identity development is an ongoing process that is really never over. Typically it starts with a flurry of identity building, then it tapers off as real work takes precedent. Every few years though, the process ramps up again as companies reinvent themselves or freshen their “look” for the next decade.
In our case, we had the talented Jon Hebert help us with our logo design. He writes about the process of starting with a vision and ending with a logo. I love the progression from the rough early sketches (which look like doodles I made in sixth grade) and ending with a logo we are all very happy with.
Yesterday afternoon the wife and I took a bike ride along Ballona Creek. Now the word creek might conjure up an image for you of tall brushy trees on grass shores following the languid contours of a water way occasionally broken up by a small rapid or waterfall. Maybe the picture on the right reflects that vision.
If so, you do not live in Los Angeles.
The picture at the left here reflects the concrete reality of Los Angeles water ways. Los Angeles rivers and creeks are heavily angular and typically filled with a murky green substance one hesitates to call “water” in fear of insulting the life sustaining liquid.
Now this may not look like a nice place for a bike ride, but consider for a moment the alternative - Biking down Venice Boulevard: Dodging gas guzzling H2 drivers in vehicles so large they require binoculars to see us lowly bikers. Stopping at a light every five seconds. Breathing in the smog ridden fumes of cars that I know have failed their smog check. Nah, that is not for me.
Personally, my favorite rides are up in the ocean tinged fresh air hills of Santa Monica Mountains (those of us who have lived in Alaska pretty much call everything here in Los Angeles a “hill”, with the exception of Mt. Baldy).
But there is something to be said of a ride that one can take right from the door of his or her own home. A short ride down the street takes us to the Ballona Creek Bike Trail entrance. From there, miles and miles of trail without interruptions from stop lights and motorized vehicles. And for the most part, it doesn’t really smell all that bad and there are some nice views of L.A. here and there.
But what really makes this ride great is the prize at the end. Marina Del Rey and Playa Del Rey. As the water turns from green sludge to a bluer color that reminds you that it is indeed water we are riding next to, the view opens up to the harbor. Sail boats drift by as the trail continues on and takes you right along the beach with people playing volleyball, jumping in the water, and throwing sand all over the place. It is really beautiful there.
We stopped at Tanner’s coffee to sit back for a moment and enjoy frosty beverage (it was the only thing we could find at that moment) and then headed back. Next time I will be sure to take some pictures.
I recently ran into a perplexing problem that I believe is a bug in ASP.NET 2.0.
Subtext dynamically loads UserControls into the page when fulfilling a request. When commenting on a post, we load a user control that contains the comment form fields and some instances of the RequiredFieldValidator validation control.
While testing, I noticed I kept getting javascript errors when trying to post a comment. Here is the error message:
missing ; before statement
Viewing the source, I noticed the error occurs in the javascript generated by the ASP.NET runtime for client side validation. Here is a tiny snippet of the line with the problem.
var ControlWithValidators.ascx_validateThat = document.all ? ...
Notice the problem? There is a dot in the variable name that Javascript does not like since ControlWithValidators is not an object. What the?
After some digging around I found the culprit. I won’t bore you with the nitty gritty details. When dynamically adding controls to a page, it is a good idea to specify an id before adding them to the Controls collection. That way the controls can reload their state on Postback. However the snippet of code I found was giving the controls an ID that contained a period.
To prove this was indeed the culprit, I created a new simple VS.NET 2005 Web Application Project that exhibits the bug. The page dynamically loads a user control that contains a validation control. Here is the Page_Load method of the web page. When you compile this and run the page, you will see the javascript error.
protected void Page_Load(object sender, EventArgs e)
{
Control control = LoadControl("ControlWithValidators.ascx");
control.ID = "ControlWithValidators.ascx";
placeholder.Controls.Add(control);
}
The quick fix was to simply replace the period with an underscore when assigning the id. Hopefully this helps you if you ever run into something so obscure.
If you are interested in duplicating the bug, you can download the validator bug demo solution here. By the way, does anyone know where is the best place to report this kind of thing?
Jim Holmes and James Avery announced that their new book Windows Developer Power Tools is going to be published in November.
Since I haven’t read the book yet, I cannot claim it will absolutely be the must read book of the summer. But I have a good feeling about this one for some reason.
Ok. Ok. I admit, I am biased because I am a contributor. Take a look at the table of contents here. My contributions are in Chapter 11, “Accessing Subversion and CVS with TortoiseSVN/CVS” as well as in Chapter 13 where I wrote about one of my favorite topics, Subtext.
I also reviewed the chapter on MbUnit with Andrew Stopford.
Having seen a couple other draft chapters, I am really excited about this book and not just because I contributed. Open source projects can be notorious for lacking in documentation. I can’t be sure, but I imagine that this book may be the impetus for some of these projects to actually write some documentation. But I am going out on a limb by saying that.
My contribution to this book is much more significant and original than my pseudo-contribution to another book this year.
Here is a quick little nugget for you custom provider implementers. I recently scanned through this article on MSDN that describes how to implement a custom provider and found some areas for improvement.
Reading the section Loading and Initializing Custom Providers I soon encountered a bad smell. No, it was not my upper lip, but rather a code smell. Following the samples when implementing custom providers would lead to a lot of duplicate code.
It seemed to me that much of that code is very generic. Did I just say generics?
Simone (blog in Italian), a Subtext developer, recently refactored all our Providers to inherit from the Microsoft ProviderBase class.
One of the first things he did was to create a generic provider collection:
using System;
using System.Configuration.Provider;
public class GenericProviderCollection<T>
: ProviderCollection
where T : System.Configuration.Provider.ProviderBase
{
public new T this[string name]
{
get { return (T)base[name]; }
}
public override void Add(ProviderBase provider)
{
if (provider == null)
throw new ArgumentNullException("provider");
if (!(provider is T))
throw new ArgumentException
("Invalid provider type", "provider");
base.Add(provider);
}
}
That relatively small bit of code should keep you from having to write a bunch of cookie cutter provider collections. But there is more that can be done. Take a look at the LoadProviders in Listing 6 of that article.
There are two things that bother me about that method listing. First is the unnecessary double check locking, which Richter poo poos in his book CLR via C#. The second is the fact that this method is begging for code re-use. I created a static helper class with the following method to encapsulate this logic (apologies for the weird formatting. I want it to fit width-wise):
/// <summary>
/// Helper method for populating a provider collection
/// from a Provider section handler.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static GenericProviderCollection<T>
LoadProviders<T>(string sectionName, out T provider)
where T : ProviderBase
{
// Get a reference to the provider section
ProviderSectionHandler section =
(ProviderSectionHandler)WebConfigurationManager
.GetSection(sectionName);
// Load registered providers and point _provider
// to the default provider
GenericProviderCollection<T> providers = new
GenericProviderCollection<T>();
ProvidersHelper.InstantiateProviders
(section.Providers, providers, typeof(T));
provider = providers[section.DefaultProvider];
if (provider == null)
throw new ProviderException(
string.Format(
"Unable to load default '{0}' provider",
sectionName));
return providers;
}
This method returns a collection of providers for the specified section name. It also returns the default provider via an out parameter. So now, within my custom provider class, I can let the static constructor instantiate the provider collection and set the default provider in one fell swoop like so:
public abstract class SearchProvider : ProviderBase
{
private static SearchProvider provider = null;
private static GenericProviderCollection<SearchProvider>
providers = ProviderHelper.LoadProviders<SearchProvider>
("SearchProvider", out provider);
}
By employing the power of generics, writing new custom providers with a minimal amount of code is a snap. Hope you find this code helpful.
You probably didn’t know this, but this blog is more than a technical blog. It is also your source for solving all the world’s problems. For example, there is the age old question, “Where did the matching sock go?” Did you notice the extra amount of lint in the dryer? Problem solved.
Today I noticed something interesting in the cupboard. We bought some Arizona Iced Tea Mix. It’s the powder stuff you mix with water to produce a refreshing drink. I noticed that not only is it packaged in a tin, but right there on the tin it lets you know that it is a collectible tin. Really? Wow! I can’t wait to get the whole set!
So that got me thinking, it makes a ton sense to label it as a collectible. Otherwise that tin becomes an ordinary piece of garbage when the little tea packages run out. But now, my friends, it is a wonderful mantle piece ready to be displayed with pride to other collectors. Perhaps I will be the one to puff out my chest and brag how I found the rare Peach Tea Tin with the misprint logo, eliciting a bit of envy among other collectors.
But why stop there. I notice that candy bars produce a lot of litter on the streets. Why not have our Snicker bars come in collectible wrappers? Or that impossible to break plastic shell that electronic devices are packaged in? Maybe if we made every newspaper issue a special collector’s edition, we could really reduce our landfill clutter.
The end result is that we will have moved from a centralized landfill structure (and all us geeks know centralized is bad! Booo!) into a decentralized peer-to-peer (good! yay!) landfill structure. It is the Napsterization of garbage...er collectibles (though perhaps Bittorentization is more accurate, but Napsterization sounds better).
Okay, it is survey time for all you Subtext users! I would like to know which Subtext skin you use for your blog. Please leave a comment with the skin that you use. If you use a custom skin, then just say Custom.
I have heard many say that the skins in Subtext look like they were designed by a developer rather than a designer. Well surprise surprise, probably because they were! I have a sneaking suspicion that many are not in use.
The reason this is important to me is that rather than simply continuing to add more and more skins to Subtext (generating a huge list), I would like to weed out some of the more drab skins. But instead of deleting skins, I was thinking I could simply replace them with newer skins that still fit the spirit of the original skin’s name. For example, I might replace the skin White with a new design that is very light in color and tone. Make sense?
I have been considering using a separate library for generating the RSS and Atom feeds in Subtext. My first thought was to use RSS.NET but I noticed that there seemed to be no recent activity.
I contacted the admin and found out that RSS.NET has been bought by ToolButton Inc and will be released as a product. Very cool!
In the meanwhile, I still need an open source RSS library to package up with Subtext. Fortunately, RSS.NET was developed under the MIT license which, as I mentioned before, is very compatible with our BSD license.
So one option is to simply copy the code into our Subtext code base. My only qualm about this approach is that I would like to keep stand-alone libraries that are not central to the Subtext Domain out of the Subtext codebase as much as possible, preferring to reference them as an external library.
Ideally, I would like to start a new project that is essentially a fork in RSS.NET, perhaps called FeedGenerator.NET (call me the forkmaster). I could probably host it on CodePlex in order to give me an opportunity to try it out and provide feedback. Would anyone find such a library useful other than us blog engine developers? Anyone have a better name?
I probably wouldn’t spend much time on this project except to provide changes and bug fixes as needed by Subtext. It would by no means be intended to compete with Web 2.0 Tools products, since they are probably going to be much more full featured than our humble needs. Besides, under the MIT license, any improvements we make would be available for them to roll into their product (following the terms of the license of course). It is the beauty of the MIT and BSD licenses.
Any thoughts? Suggestion? Etc...?
In my last post, one of the restrictions listed when running in medium trust is that HTTP access is only allowed to the same domain. It is possible in web.config to add a single domain via the originUrl attribute of the <trust> element as described by Cathal.
To add more than one domain requires editing machine.config or creating a custom trust policy which will not be accessible to many users in a hosted environment. This may pose a big problem for those who care about trackbacks since even if you could modify machine.config, there is no way to predetermine every domain you will trackback.
One solution is to beg your hosting environment to relax the WebPermission in medium trust. If trackbacks and pingbacks are important to you, you shouldn’t be above begging. ;)
Another is for someone to create a passthrough trackback system in a fully trusted environment. Essentially this would act on behalf of the medium trusted trackback creator and forward a trackback to the final destination. It would require blogging engines affected by medium trust to trust this single domain. Of course the potential for abuse is high and the rewards are low (unless people out there absolutely love trackbacks).
UPDATE: Mea Culpa! It seems like Log4Net has no problems with medium trust and an external log4net file. I have written an updated post that talks about the problem I did run into and how I solved it.
A while ago I wrote a quick and dirty guide to configuring Log4Net for ASP.NET. Unfortunately, this technique does not work with ASP.NET 2.0 when running in medium trust.. This technique continues to work with medium trust!
While digging into the problem I found this blog post (from an aptly titled blog) by Kevin Jones.
This article from Microsoft discusses the ramifications of running ASP.NET 2.0 in medium trust more thoroughly. Here is a list of constraints placed on medium trust applications.
The main constraints placed on medium trust Web applications are:
OleDbPermission is not available. This means you cannot use the ADO.NET managed OLE DB data provider to access databases. However, you can use the managed SQL Server provider to access SQL Server databases.
EventLogPermission is not available. This means you cannot access the Windows event log.
ReflectionPermission is not available. This means you cannot use reflection.
RegistryPermission is not available. This means you cannot access the registry.
WebPermission is restricted. This means your application can only communicate with an address or range of addresses that you define in the <trust> element.
FileIOPermission is restricted. This means you can only access files in your application’s virtual directory hierarchy. Your application is granted Read, Write, Append, and PathDiscovery permissions for your application’s virtual directory hierarchy.
You are also prevented from calling unmanaged code or from using Enterprise Services.
Fortunately there is a way to specify that a configuration section within web.config should not require ConfigurationPermission. Simply add the requirePermission="false" attribute to the <section> declaration within the <configSections> area like so:
<configSections>
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler
, log4net"
requirePermission="false"/>
</configSections>
Unfortunately this applies to configuration sections within the web.config file. I have not found a way to specify that ASP.NET should not require ConfigurationPermission on an external configuration file. As I stated in my post on Log4Net, I prefer to put my Log4Net configuration settings in a separate configuration file. If anyone knows a way to do this, please let me know!
So in order to get Log4Net to work, I added the declaration above to the web.config file and copied the settings within the Log4Net.config file (pretty much cut and paste everything except the top xml declaration) into the web.config file. I then removed the assembly level XmlConfigurator attribute from AssemblyInfo.cs as it is no longer needed. Instead, I added the following line to the Application_Start method in Global.asax.cs.
protected void Application_Start(Object sender, EventArgs e)
{
log4net.Config.XmlConfigurator.Configure();
}
So in summary, here are the changes I made to get Log4Net to work again in medium trust.
- Added the log4Net section declaration in the
configSections section of web.config and made sure the requirePermission attribute is set to the value false.
- Moved the log4Net settings into web.config.
- Removed the assembly attribute
XmlConfigurator
- Added the call to
XmlConfigurator.Configure() to the Application_Start method in Global.asax.cs.
I have been working on getting the version of Subtext in our Subversion trunk to run in a medium trust environment, but there have been many challenges. Some of the components we use do not appear to run in a medium trust environment such as the FreeTextBox. Fortunately, we have a workaround for that issue which is to change the RichTextEditor node in web.config to use the PlainTextRichTextEditorProvider (which is a mouthful and should probably be renamed to PlainTextEditorProvider).
Recently I poked lighthearted fun at one of the recent Apple commercials called “Network”
There are many who dislike the ads and claim that the ad is blatantly not true. One example is this humorous and well written rebuttal by Seth Stevenson.
Seth points out...
The final straw, for me, is that the spots make unconvincing claims. The one titled “Network” has a funny bit where “that new digital camera from Japan” is represented by a Japanese woman in a minidress. While Hodgman has trouble talking with the woman, Long speaks Japanese and shares giggles with her because “everything just kind of works with a Mac.” Now, I happen to have a digital camera from Japan, and it works just fine with my PC. It did from the moment I connected it.
Good point Seth. Perhaps the ad would have been more accurate had it been a Japanese printer rather than a digital camera. And I mean a printer that really is from Japan. My wife’s dad left us an Epson PM-950C Printer (among other things). This printer is not listed on the Epson U.S. site, but presumably can be found in the Japanese site.
In order to test it out, we plugged it into the USB port on her five year old iBook and printed a web page. Worked like a charm.
Fast forward a couple of weeks and we want to use the printer on my PC. We plugged it in and get the Windows cannot find the driver for the hardware dialog and were unable to print. Effectively, it didn’t just work. I then downloaded a driver that is supposed to be the U.S. equivalent. Still didn’t work.
My wife laughed out lound and said, “See! The ads are right!” I caught myself making excuses explaining to her that there are technical reasons beyond her comprehension and that I just need to toy with it for a moment to get it to work.
But I caught myself. Why am I making excuses for PCs and Microsoft? The experiment was quite simple. I plugged in a Japanese printer into a Macintosh, and we were able to print. I plug in the same printer into our PC (which was recently re-installed) and it didn’t work. Anecdotal evidence, sure, but adds weight to the Apple claim that things just work with the Apple and they don’t just work with Windows.
And responding with, “Well it will with Vista” is not a satisfactory answer. Tomorrow cars will fly, the U.S. will win the World Cup, and we will control our computers by having conversations with them and manipulating holograms. But the fact of the matter is that we all live in today and I will give Apple a bit of credit here.
A while ago I had the idea of posting a picture of the day. Of course I didn’t mean every day, but the title might lead one to believe so. Therefore in order to reduce confusion, here is the next photo in my Picture of the Moment series.
This is the most favorited and interesting picture according to Flickr in my Flickr account. So naturally that tells you that it wasn’t me who took this photo, but my lovely wife while on a trip to Japan. I was reminded of this picture because someone recently added a comment.
Speaking of Flickr, I love how you can comment specific regions of a picture by drawing box on the region. If you click on the image, you can see my feeble attempts at humor by moving your mouse of the photo. My comments are in sharp contrast to the elegance of three Geisha walking through a crowd.
Jeff asks the question, “Isn’t programming supposed to be fun?”? Ha ha ha, naive little bunny. As if the person who invented programming had fun in mind. Silly rabbit.
You’ve heard the cliche. The reason it is called work is because it is work. Programming is fun when it is just for fun.
Cynical jokes aside, I actually did work for fun at my last job. Well technically, I worked for FUN. My former employer was bought by a company that then changed its name to Fun Technologies. It is listed under the symbol FUN on the Toronto Stock Exchange and the Alternative Invesment Market of the London Stock Exchange.
When I visited headquarters in Toronto the CEO gave me a hat with the ticker symbol emlazoned on the front. I think I still have it somewhere. I would wear it and go around telling people that I work for FUN. It was mildly funny for about two minutes. But as is my style, I dragged it out for thirty.
Working as a team against a common database schema can be a real challenge. Some teams prefer to have their local code connect to a centralized database, but this approach can create many headaches. If I make a schema change to a shared database, but am not ready to check in my code, that can break the site for another developer. For a project like Subtext, it is just not feasible to have a central database.
Instead, I prefer to work on a local copy of the database and propagate changes via versioned change scripts. That way, when I check in my code, I can let others know which scripts to run on their local database when they get latest source code. Of course this can be also be a big challenge as the number of scripts starts to grow and developers are stuck bookkeeping which scripts they have run and which they haven’t.
That is why I always recommend to my teams that we script schema and data changes in an idempotent manner whenever possible. That way, it is much easier to simply batch updates together in a single file (per release for example) and a developer simply runs that single script any time an update is made.
As an example, suppose we have a Customer table and we need to add a column for the customer’s favorite color. I would script it like so:
IF NOT EXISTS
(
SELECT * FROM [information_schema].[columns]
WHERE table_name = 'Customer'
AND table_schema = 'dbo'
AND column_name = 'FavoriteColorId'
)
BEGIN
ALTER TABLE [dbo].[Customer]
ADD FavoriteColorId int
END
This script basically checks for the existence of the FavoriteColorId column on the table Customer and if it doesn’t exist, it adds it. You can run this script a million times, and it will only make the schema change once.
You’ll notice that I didn’t query against the system tables, instead choosing to lookup the information in an INFORMATION_SCHEMA view named Columns. This is the Microsoft recommendation as they reserve the right to change the system tables at any time. The information views are part of the SQL-92 standard, so they are not likely to change.
There are 20 schema views in all, listed below with their purpose (aggregated from SQL Books). Note that in all cases, only data accessible to the user executing the query against the information_schema views is returned.
| Name |
Returns |
| CHECK_CONSTRAINTS |
Check Constraints |
| COLUMN_DOMAIN_USAGE |
Every column that has a user-defined data type. |
| COLUMN_PRIVILEGES |
Every column with a privilege granted to or by the current user in the current database. |
| COLUMNS |
Lists every column in the system |
| CONSTRAINT_COLUMN_USAGE |
Every column that has a constraint defined on it. |
| CONSTRAINT_TABLE_USAGE |
Every table that has a constraint defined on it. |
| DOMAIN_CONSTRAINTS |
Every user-defined data type with a rule bound to it. |
| DOMAINS |
Every user-defined data type. |
| KEY_COLUMN_USAGE |
Every column that is constrained as a key |
| PARAMETERS |
Every parameter for every user-defined function or stored procedure in the datbase. For functions this returns one row with return value information. |
| REFERENTIAL_CONSTRAINTS |
Every foreign constraint in the system. |
| ROUTINE_COLUMNS |
Every column returned by table-valued functions. |
| ROUTINES |
Every stored procedure and function in the database. |
| SCHEMATA |
Every database in the system. |
| TABLE_CONSTRAINTS |
Every table constraint. |
| TABLE_PRIVILEGES |
Every table privilege granted to or by the current user. |
| TABLES |
Every table in the system. |
| VIEW_COLUMN_USAGE |
Every column used in a view definition. |
| VIEW_TABLE_USAGE |
Every table used in a view definition. |
| VIEWS |
Every View |
When selecting rows from these views, the table must be prefixed with information_schema as in SELECT * FROM information_schema.tables.
Please note that the information schema views are based on a SQL-92 standard so some of the terms used in these views are different than the terms in Microsoft SQL Server. For example, in the example above, I set table_schema = 'dbo'. The term schema refers to the owner of the database object.
Here is another code example in which I add a constraint to the Customer table.
IF NOT EXISTS(
SELECT *
FROM [information_schema].[referential_constraints]
WHERE constraint_name = 'FK_Customer_Color'
AND constraint_schema = 'dbo'
)
BEGIN
ALTER TABLE dbo.Customer WITH NOCHECK
ADD CONSTRAINT
FK_Customer_Color FOREIGN KEY
(
FavoriteColorId
) REFERENCES dbo.Color
(
Id
)
END
I generally don’t go to all this trouble for stored procedures, user defined functions, and views. In those cases I will use Enterprise manager generate a full drop and create script. When a stored procedure is dropped and re-created, you don’t lose data as you would if you dropped and re-created a table that contained some data.
With this approach in hand, I can run an update script with new schema changes confident that I any changes in the script that I have already applied will not be applied again. The same approach works for lookup data as well. Simply check for the data’s existence before inserting the data. It is a little bit more work up front, but it is worth the trouble and schema changes happen less frequently than code or stored procedure changes.
I got a lot of great feedback from my post on Building Plugins Resilient to Versioning, which proposes an event-based self-subscription model to plugins.
Craig Andera points out that we can get many of the same benefits by having plugins implement an abstract base class instead of an interface. This is definitely a workable solution and is probably isomorphic to this event based approach.
Dimitri Glazkov was the voice of dissent in the comments to the post pointing out that the application loses granular control over plugins in this approach. I was not convinced at the time as I was focused on keeping the surface area of the plugin interface that is exposed to the application very small. When the surface area is small, there is less reason for the interface to change and the less reason to break the interface.
However a simple thought experiment makes me realize that we do need to have the application retain granular control over which plugins can respond to which events. This is the scenario.
Suppose our plugin framework defines three events, MessageSending, MessageSent, MessageReceiving and someone writes a plugin that responds to all three events. Later, someone else writes a plugin that only responds to MessageReceiving. If the blog user wants to chain the functionality of that plugin to the existing plugin, so that both fire when a message is received, then all is well.
But suppose this new plugin’s handling of the MessageReceiving event should replace the handling of the old plugin. How would we do this? We can’t just remove the old plugin because then we lose its handling of the other two events. Dimitri was right all along on this point, we need more granular control.
It makes sense to have some sort of admin interface in which we can check and uncheck individual plugins and whether or not they are allowed to respond to specific events. However, this is not too difficult with the event based approach.
.NET’s event pattern is really an implementation of the Observer pattern, but using delegates rather than interfaces. After all, what is a delegate under the hood but yet another class? When any code attaches a method to an event, it is in effect registering a callback method with the event source. This is the step where we can obtain more granular information about our plugins.
In the Application that hosts the plugin, events that require this granular control (not every event will) could be defined like so.
private event EventHandler messageReceived;
public event EventHandler MessageReceived
{
add
{
RegisterPlugin(value.Method.DeclaringType);
AddEvent(value);
}
remove
{
UnRegisterPlugin(value.Method.DeclaringType);
RemoveEvent(value);
}
}
So when adding and removing the event, we register the plugin with the system and then we add the event to some internal structure. For the purposes of this discussion, I’ll present some simple implementations.
void AddEvent(EventHandler someEvent)
{
//We could choose to add the event
//to a hash table or some other structure
this.messageReceived += someEvent;
}
void RemoveEvent(EventHandler someEvent)
{
this.messageReceived -= someEvent;
}
private void RegisterPlugin(Type type)
{
//using System.Diagnostics;
StackTrace stack = new StackTrace();
StackFrame currentFrame = stack.GetFrame(1);
Console.WriteLine("Registering: " + type.Name
+ " to event " + currentFrame.GetMethod().Name);
}
private void UnRegisterPlugin(Type type)
{
StackTrace stack = new StackTrace();
StackFrame currentFrame = stack.GetFrame(1);
Console.WriteLine("UnRegistering: " + type.Name
+ " to event " + currentFrame.GetMethod().Name);
}
As stated in the comments, the AddEvent method attaches the event handler in the standard way. I could have chosen to put it in a hash table or some other structure. Perhaps in a real implementation I would.
The RegisterPlugin method examines the call stack so that it knows which event to register. In a real implementation this would probably insert or update some record in a database somewhere so the application knows about it. Note that this should happen when the application is starting up or sometime before the user can start using the plugin. Otherwise there is no point to having access control.
public void OnMessageReceived()
{
EventHandler messageEvent = this.messageReceived;
if(messageEvent != null)
{
Delegate[] delegates = messageEvent.GetInvocationList();
foreach(Delegate del in delegates)
{
if (EnabledForEvent(del.Method.DeclaringType,
"MessageReceived"))
{
del.DynamicInvoke(this, EventArgs.Empty);
}
}
}
}
Now, when we invoke the event handler, instead of simply invoking the event, we examine the delegate chain (depending on how we store the event handlers) and dynamically invoke only the event handlers that we allow. How is that for granular control?
In this approach, the implementation for the application host is a bit more complicated, but that complexity is totally hidden from the plugin developer, as it should be.