Archives

Archives / 2009 / July
  • Happy Birthday CodePlex!

    CodePlex just recently turned 3 years old and celebrated a pretty good success (hitting the 10,000 project mark). I'm very happy that my SharePoint Forums project was one of the first public non-Microsoft projects hosted on CodePlex (before it was called CodePlex).

    Here are some impressive stats from the 3 year old:

    • 10,000 projects
    • Consistent growth over the 3 years in visits, page views, projects and source code
    • 3 million visitors per month
    • Almost 10 million page views per month
    • 160,000 registered users
    • 160 million lines of code stored across 10 team foundation servers
    • 16,000 source code check-in per month

    Congrats guys! Great work. Check out more stats here on the CodePlex blog and some other info here on Port 25.

  • Boo! SharePoint. Not really.

    I stumbled onto Peter Campbell's well written article about why SharePoint scares him. His points on what bothers him about SharePoint bother me as he tries to compare some parts of SharePoint to say open source CMS systems (he's not specific as to "what" open source portal he compares SharePoint too but one can assume Drupal, Joomla, DotNetNuke maybe).

    It's not clear if Peter has actually ever used SharePoint (either as a developer, admin, or even an end-user). He says that he's been "evaluating" the product for the past four years but that could be just reading white papers and looking at slide decks for all I know. There are some valid points in his article but I think it paints SharePoint in a dim light without being honest to the points he raises. I don't see Peter bashing SharePoint (too much) but rather his take on it for his purpose. His summary is that it requires a lot of consulting, hardware, and budget but organizational needs can better be served by simpler alternative installations. I agree but there are some important points to be aware of if making this comparison and decision.

    So here's yet another point/counter-point blog entry that will probably label me as a Microsoft flag-waver but so be it.

    Integration with Legacy Systems

    Mainly Peter points out that SharePoint is based on SOAP vs. the more simpler REST approach from an integration perspective. This is true and if you're not familar with SOAP and know what the limitations of the web services SharePoint provides you can get yourself into a pickle. I think integration with any legacy system is complicated no matter what the interface is. You're dealing with legacy data which doesn't conform to any current standard. SharePoint isn't the cause for concern here, what you're trying to get SharePoint to do is. If you're trying to migrate a legacy application into SharePoint to be your new Time Tracker, Inventory System, or Help Desk (complete with all the legacy features you enjoyed in your legacy system) then you're making the wrong choice. SharePoint might be great as a presentation layer for this and you can expose all the data through the BDC for example, but trying to rebuild SAP or something in SharePoint? Bad choice IMHO as a SharePoint app and more suited as a .NET application where you can use REST (e.g. via ASP.NET MVC) and expose that content to SharePoint via presentation. SharePoint is not the dumping ground for migrating legacy applications to.

    Two Applications

    He states that SharePoint is "two major, separately developed applications". Not really. Windows SharePoint Services is essentially a platform (and free). The templates Microsoft provides out-of-the-box are just that. Templates. You can rip all of them out, hide them, and otherwise completely ignore them and build your own set of site templates on top of WSS. That's why I consider it a platform that can be extended. Want to build a DotNetNuke or Drupal clone on top of WSS? It would probably take a lot of work to create the templates, styles, and master pages but can be done. Microsoft Office SharePoint Server is the separate application that a) relies on WSS b) provides additional templates and c) provides additional services (Excel, Records Managment, etc.). I don't agree they were "hastily merged into one app". WSS hasn't changed much since the 2.0 days fundamentally (other than the flip in the ASP.NET architecture) and MOSS was developed over the course of 4 years or so of development and testing. How does that classifiy as "hastily"?

    Misguided Site Structure

    Peter doesn't bash SharePoint here (at least I don't think he does). He simply states what we all have said in the past. With great power comes great responsibliity. If you let your users create whatever taxonomy of mischief they can imagine, don't be too proud of this technological terror that gets created. I posted a blog entry to stop blaming SharePoint for your misfortunes here. I think this point just restates that intent.

    The Revenge of the Blob

    Peter points out that the database stores documents as blobs (rather than linking to files on the disk) which in turn might lead to corruption and has some performance hit. He cites he doesn't want to put his organization's critical work and put it on a box that could easily break. I'm not sure the failure rate of SQL and data content but frankly I think this is as fragile as files on a file system. I don't know how many times I've had cross-linked files or ACL issues or whatever on the file system, compared to SQL corruption. I'll come clean and say that I've experienced SQL corruption first-hand (a log file overran the disk and corrupted the data file which was not backed up as the SQL agent was disabled). However this is the exception to the rule. Frankly, the thought of having a set of files on the file system and the metadata in a database scares me more. The risk of getting this out of sync is far greater than SQL database corruption and a SQL database doesn't retain the file corruption that would happen if the file system went south. I simply restore it to a new SQL instance. File level corruption would be replicated if I did file backups so I would be SOL with my "critical work product".  Also I'm not sure how you handle file versioning if files are stored on the file system. A folder for each file? Some naming strategy for files? If you look at modern source control systems (e.g. Subversion, Git, etc.) and how they version files on the file system they all use blob storage for the files with delta values. No different than what SharePoint is doing, except the storage medium is a file system instead of a database.

    Licensing Complications

    I'm no licensing expert and frankly licensing for any Microsoft product is over complicated. Ask five licensing guys at Microsoft about a product and you'll get seven different answers. What I do know is that licensing for external is pretty basic. If you're exposing your site externally you need the external connector license. This is not per-user, but just a single fee for exposing your site on the internet. I believe there is an additional license if you're exposing more functionality than what WSS has to offer (e.g. MOSS features like InfoPath forms or Excel Services) but again I'm not an expert. Peter is correct that pricing is somewhat prohibitive and I agree it's complicated. It's getting simpler (the licensing for 2003 was far more complicated than it is for 2007) and hopefully it'll get better for 2010.

    Hardware Requirements

    Peter compares the hardware requirements of SharePoint to "most open source portals". I'll agree here. Drupal doesn't seem to mention it's requirements (that I can find) however they recently took all their donated money are poured it into infrastructure amounting to 4 total servers and spending about $10,000. This is for the public facing Drupal site. Not bad for a site but again, I can't tell how many users are hitting the site or what kind of stats that hardware is needed to serve up. Similiarly, Joomla only lists software requirments and both basically say they need a LAMP stack (or WAMP in some cases) but no mention of hardware is provided. So to do a comparison you can try to look at hardware requirments for MySQL, Apache and PHP and try to fit them into similar offerings for SharePoint (SQL, IIS, ASP.NET) but it's difficult.

    SharePoint hardware requirements are pretty straight forward and I've lived through all configurations. If you have 1,000 users who use it on a normal basis (more on this in a minute) you can run the entire rig on a modern machine (high end workstation) without any issue. SQL is a pig and your IIS worker process can easily gobble up a few hundred megs of memory just starting up. So a) run 64 bit, it's the only way b) through at least 4GB of RAM on the box, but 8 would be better. After 1,000 users you'll need to scale up to a small farm. This can be 2 web-front ends, the app, and SQL box. Now we're talking about most middle size farms serving up 10,000 users. When I say "normal basis" I mean mostly readers of documents, people authoring documents on a regular basis (say 1 worker uploading a new document a couple of times a day with a maximum of a few hundred workers). The numbers here are not scientific and I encourage you to get the SharePoint Capacity Planning Tool which will give you a much better set of numbers to work from. In any case, a LAMP stack is more easier on hardware but then I've got various WAMP stacks running on the Internet where PHP can easily bring IIS (and Apache) down to it's knees if you don't have a good CPU and lots of RAM behind it. Same as SharePoint. I'll admit the requirements for SharePoint are higher as there's more going on behind the scenes but this is typical of what SharePoint does vs. what you can do with an open source portal.

    I grow tired of the "commercial vs. open source" discussion every day. It's like comparing apples to gasoline. Each does what it needs to do differently, there are different costs involved, different outputs, different inputs. And trying to nitpick over the value of one over another is futile and value-diminishing. When two products from each space can meet all requirements in a balanced fashion then you can make the comparison and put more value on one over another but I have yet to find those products to compare.

    Overall

    It's just my opinion (as is Peters) but I think Peter compares too many fundamental architectural problems and blames SharePoint as the cause. Also there are vague references to "open source" being better and more fluent. The primary issue I have here is that most any other package doesn't offer as many features as SharePoint has (mainly on the integration with the Office client).

    I'll single out Drupal, Joomla, and Wordpress here as I use them all the time. I know Wordpress is considered "just a blogging tool" but it has a lot of functionality that goes beyond that. The third party market for these products is huge and there's some quality stuff out there. If I want to plug a feature into Drupal, it doesn't take long to find it. Building WordPress plugins isn't too complicated and the APIs are flexible and simple to use. Are these tools as powerful as SharePoint? No. Is SharePoint better at doing say Wikis or Blogs like MediaWiki or WordPress. No. However you have to choose the right tool for the right job and in the corporate environment where 99% of your information workers live and breathe in Word, PowerPoint, Excel, and Outlook there's nothing that does things better and easier than SharePoint. Today it's not Web 2.0 and it's painful to do what might be the simplest of things in an open source equivalent.

    Sure, Drupal is easier to setup and Joomla can be more "Web 2.0" out-of-the-box but from a feature perspective and long-term sustainability view, Enterprises have to rely on something that's going to be around in 7+ years and not drastically change. Open source products, while quicker to setup, don't scale to this level; don't offer this kind of integration; and haven't been around long enough without drastic re-work to yield this kind of longevity.

    Far be it for me to say SharePoint is some kind of holy grail in content managment or intranet portals. It's not. It's a good product that does some things very well and other things not so well. Its growing and evolving. With the latest sneak peek at the 2010 version, like the transition from 2003 -> 2007, things are about to get a whole lot better again.

  • The undocumented “deleteconfigurationobject” parameter

    The best damn coder on the planet!SharePoint is sometimes a riddle, wrapped in a mystery, surrounded by an enigma. And even more so when you get stuck trying to do something, hoping the system will tell you how, only to stumble onto an undocumented command line switch that does what you need.

    Enter the super-secret-that-everyone-knows-about “deleteconfigurationobject” parameter in stsadm.exe.

    Go ahead. Enter this in your console.

       1:  stsadm -help deleteconfigurationobject

    And rather than getting the usage screen (meaning you entered a parameter that the program didn’t understand) you get this:

       1:  stsadm.exe -o deleteconfigurationobject 
       2:             -id <objectId>

    Woah. That wasn’t in the usage screen when you enter stsadm.exe by itself. Nor is it in the documentation for stsadm.exe on TechNet here.

    This is what is known in the software world as an “undocumented feature”. Meaning something you can use, invoke, etc. but it’s not documented so it’s not guaranteed to be there tomorrow.

    Okay, armed with the knowledge this bad boy exists, what would I do with it?

    In my situation, I’m working on custom timer jobs. Andrew Connell recently wrote some great blog entries and MSDN white papers on custom timer jobs and Gary Lapointe recently had a posting about deploying files not handled by the WSP schema for you.

    Basically if you want to run something on a regular basis you can create a custom timer job, register it, and have the OWSTIMER.EXE service take care of it for you. This beats the pants off of having to write a console application then setting it up on the Task Scheduler because you have immediate access to the SharePoint site structure to do updates or what have you. For example, rather than calling out to a weather web service for every user or having to deal with caching information, I created a timer job that did it for me and let my web part simply read from a sanitized SharePoint list on the site for the local weather. This required some hourly updates to the list which I did via a custom timer job. Again, check out AC’s article(s) on the subject and you can’t go wrong.

    However, there are times that things go wrong. Very, very wrong. Like tonight. I had created some timer jobs but foolishly ripped out the default constructor (which is required for serialization/de-serialization). Bad Bil. This caused me no end of grief when I tried to remove the jobs since de-serialization failed, the jobs threw exceptions during feature de-activation and never got deleted.

    What I ended up with was this:

    Job Definition Screen

    The names of the custom jobs are obfuscated here, but the two job names at the bottom of the image (pixelated out) are the same job name but with different instance IDs.

    In the Central Admin screen, you can view the job definition and even disable jobs but there’s no where to delete a job definition. There’s even a CodePlex project here for doing custom job manipulation (very handy if you want to quickly reschedule a job) but alas, there’s still no delete feature.

    Update: I did find a project here on CodePlex that’s specifically made for manipulating timer jobs and does (based on the screenshots) allow deletion. Grab it and install it if you don’t want to do the manual steps below!

    I thought I was going to have write a cruddy console app to find the job and delete it via the API. That’s until I found this forum post on the interweb. It hinted at a command line operation for stsadm.exe called deleteconfigurationobject. Like the forum post says, go into Central Admin to find your jobs and hover over the job name. Then select the shortcut and paste it into Notepad or something. You’ll get something like this:

       1:  http://myserver:25435/_admin/JobEdit.aspx?JobId=bf166029%2D7c99%2D49cc%2D9ade%2D45586487c20c

    Note the Id after the JobId parameter. The dashes are encoded and show as “%2D”. Simple change them to a real dash and strip away the rest to get this:

       1:  bf166029-7c99-49cc-9ade-45586487c20c

    This is the GUID for the job timer that we want to delete.

    Now go to a command prompt and enter the secret deleteconfigurationobject operation using the GUID for your job as the id:

       1:  stsadm -o deleteconfigurationobject -id bf166029-7c99-49cc-9ade-45586487c20c 
       2:   
       3:  Operation completed successfully.

    Voila! Rogue job timer gone away! Poof.

    Daniel Bugday, you made my day! I’m sure there are other configuration objects you might need to delete that have no user interface anywhere in SharePoint that might be used with this command.

    Like I said, caveat emptor. This is an undocumented feature and maybe a simple oversight or potentially not something meant for end-users. Thus Microsoft may choose to remove it in the next version or change it’s behavior or turn it into a newt. Don’t bet the farm on this command (but it’s nice to know it’s there isn’t it?).

  • Extending SharePoint: Checking if a List exists

    A common problem when working with the SharePoint Object Model is getting a handle to a list. Very often we find ourselves writing this:

       1:  try
       2:  {
       3:      using (var web = site.OpenWeb())
       4:      {
       5:          if (web != null)
       6:          {
       7:              try
       8:              {
       9:                  var list = web.Lists[ListTitle];
      10:              }
      11:              catch (ArgumentException)
      12:              {
      13:                  web.Lists.Add(ListTitle, ListDescription, SPListTemplateType.GenericList);
      14:              }
      15:          }
      16:          else
      17:          {
      18:              Console.WriteLine("Unable to open web site.");
      19:          }
      20:      }
      21:  }
      22:  catch (Exception ex)
      23:  {
      24:      Console.WriteLine(ex.Message);
      25:  }

    This adds a list if it doesn't exist on the site. What a pain to have to write this over and over again. Okay, so step 1 might be to write a method on a helper class (usually a static class and static method) that would do it for you. I'm not a huge fan of helper classes as you eventually end up with the MonolithicHelperClass or MotherOfAllHelperClasses that pretty much does everything. That's not cool in my books. 

    Enter extension methods for SharePoint.

    If you're building your web part or solution on .NET 3.0 or higher, you can make use of a feature that was introduced called extension methods. I won't get into the details of extension methods here but check out this post by Scott Guthrie on them and do some Googling if you want to know more. In a nutshell, extension methods let us extend existing classes not by subclassing them but by adding new methods to them. In the SharePoint world, extension methods are really useful due to a lot of the OM "workarounds" we face every day.

    To create an extension method we need to do two things. First, create a static class to hold them. This can be named anything you want. Then include a static method. The first parameter to the static method will be the class you're extending (in our case SPWeb) prefixed with the keyword "this". "this" tells the compiler that we're extending the type specified so whenver we encounter a method with this signature of the type, please run our code for us. Here's our code moved into an extension method:

       1:  public static class SPWebExtensions
       2:  {
       3:      public static bool ListExists(this SPWeb web, string listName)
       4:      {
       5:          try
       6:          {
       7:              var list = web.Lists[listName];
       8:              return true;
       9:          }
      10:          catch (ArgumentException)
      11:          {
      12:              return false;
      13:          }            
      14:      }
      15:  }

    Not bad. So now we can use it like this:

       1:  try
       2:  {
       3:      using (var web = site.OpenWeb())
       4:      {
       5:          if (web != null)
       6:          {
       7:              if(!web.ListExists(ListTitle))
       8:              {
       9:                  web.Lists.Add(ListTitle, ListDescription, SPListTemplateType.GenericList);
      10:              }
      11:          }
      12:          else
      13:          {
      14:              Console.WriteLine("Unable to open web site.");
      15:          }
      16:      }
      17:  }
      18:  catch (Exception ex)
      19:  {
      20:      Console.WriteLine(ex.Message);
      21:  }

    However I'm still not happy with the try/catch statement in our extension method. Sure it works but it's expensive always throwing an exception when a list doesn't exist. Inside our extension method, we have access to all public methods and properties of the instance of the object we're extending so why not iterate through the list, checking to see if the name matches up and returning our boolean value instead?

       1:  public static class SPWebExtensions
       2:  {
       3:      public static bool ListExists(this SPWeb web, string listName)
       4:      {
       5:          var lists = web.Lists;
       6:          foreach (SPList list in lists)
       7:          {
       8:              if(list.Title.Equals(listName))
       9:                  return true;
      10:          }
      11:          return false;
      12:      }
      13:  }

    This is cleaner and doesn't throw an exception if the list isn't found. Some would argue that iterating through the entire list is expensive but really people, if you have that many lists on your site you might want to consider a bit of a content/architectural review first before blaming the code techniques presented here.

    FWIW, there is a nice library of SharePoint extensions available on CodePlex here. They have an extension method that will check for a list however I'm presenting this solution here because you may not want to commit to an entire library for a single need like we have here. Their ListExists method extends the SPWeb object, but also makes use of their own extension to the SPListCollection and some LINQ magic. Feel free to use that solultion as there are a lot of great extensions available there.

    Unfortunately there's not much hope in the future for fixes to the Object Model so we continue to add our own extensions as we see fit and the need arises. Eventually something will exist in the base system that you can retire your own RYO code but for now this is a pretty good compromise IMHO. YMMV.

  • Fill out a survey, win a chance for a SharePoint Conference 2009 pass

    Free? Sure, why not? Yeah, title says all. Mindsharp, Nintex, and Combined Knowledge are sponsoring a Global SharePoint Survey. This is an independent survey that you can fill out to let them know about your experience with adoption and usage of SharePoint from your perspective. The survey is quick (only 15 questions) and most questions are multiple choice. You could probably let your cat or two year old fill it out (I did) but also consider taking a few minutes to put some thought behind it (unlike what I did).

    In the end, you get a chance to win a conference pass to the Microsoft SharePoint Conference 2009 (October 19-22) in Las Vegas. This pass is worth $1,119 USD if purchased with real money. A winner will be drawn randomly after the closing of the survey July 17th then notified by email and/or phone. Note the contest is only for the conference pass. You still need to provide a way to get there and pay for your hotel, mini-bar, and pub crawl expenses (and trust me, when you hang with SharePoint dudes, the bar bill can get pretty hefty). Still, it’s a short slice out of your life for a chance to make it big in the city that never sleep.

    You can fill out the survey here. Enjoy and good luck!