Thursday, May 29, 2008

How record companies can save themselves

I got a lovely book for Christmas, "Factory Records - The Complete Graphic Album", which shows off the artwork for most of the albums and singles that came out on the Factory label along with other artwork used in ads etc. It got me thinking about why people are not buying music from record labels much anymore, instead choosing to download it free over the internet. Of course, one reason for this is the price but I think there's more to it than that. People have always taped music from friends but they still bought it as well.

But look at that Factory artwork again, some of it was superb (and some of it wasn't...). But buying a Factory record wasn't just about music, it generally came in a beautiful package as well. In the days of vinyl, there were little messages in the run-out groove (it wasn't just Factory that did this of course). Being 12" wide meant the artwork could be much more complex. So record companies should go back to vinyl? No, though they could certainly think about it. But the point is that when I bought music in those days I felt a connection with the artist or record company because it felt more than "product", it was sometimes a work of art.

Now if I pop down to a music shop most of the racks seem to be populated with CDs that look like they had absolutely no thought put into their production. Plastic case, crap photo of the artist, no design aesthetic. Why not download it for free? What do I gain by paying a tenner?

But it doesn't have to be this way. I just bought the boxed deluxe edition of U2's "The Joshua Tree". OK, I know, self-important and pretentious, but I do think it was a great album so I'll overlook the earnestness. For just over twice the price of a normal CD, I got 2 CDs (the album and a CD of rarities), a DVD (live concert, documentary and videos), a book and some postcards. All in beautiful packaging. I snapped it up without a second thought. Presumably Island are making money out of this, so why can't other recorded music come like this? Give people a reason for buying your music and they may well be persuaded.

Tuesday, May 27, 2008

Adding code to a XOML workflow

I was aware that it was possible to add code to a XOML workflow but I hadn't actually seen any examples of it, until I started looking into the Metastorm WF integration. I'm not sure it's something I'd like to do, it's pretty ugly and I like the idea of producing a workflow definition in a purely declarative manner. That way you're forced to put real code in their own activities which I think helps the overall design of your system. However there is one place where it isn't possible to do everything in XML, when you need to add a property to a XOML workflow. So here's an example of that. If you get stuck using this, you're on your own, I don't know much about it and the documentation seems a bit thin on the ground. Here's hoping that MS add support for XOML properties in the next release...

<x:Code><![CDATA[//<?xml version="1.0" encoding="utf-16"?>
//<XCodeItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="XCodeProperty">
//  <MemberTypeName>System.Boolean</MemberTypeName>
//  <MemberName>Thingy</MemberName>
//  <EmitDependencyProperty>true</EmitDependencyProperty>
//  <IsMetaProperty>false</IsMetaProperty>
//  <IsAttached>false</IsAttached>
//  <IsReadOnly>false</IsReadOnly>
//</XCodeItem>


public static System.Workflow.ComponentModel.DependencyProperty ThingyProperty = DependencyProperty.Register("Thingy", typeof(System.Boolean), typeof(MyWorkflow));

public bool Thingy
{
get
{
return ((bool)(base.GetValue(MyWorkflow.ThingyProperty)));
}
set
{
base.SetValue(MyWorkflow.ThingyProperty, value);
}
}
]]></x:Code>

Monday, May 26, 2008

Metastorm BPM 7.6 and Windows Workflow part 1 - The basics

The new version of Metastorm BPM, 7.6, has several new features (and I may do a full review at some point), but the one that interests me the most is the integration with Windows Workflow. This has been a fairly long time in development, a screenshot appeared on the web about 18 months ago. The first WF integration appeared in 7.5, which allowed BPM functionality to be called from WF workflows. This was not of great interest to me as it always seemed more useful to be able to call WF workflows from Metastorm. This is what has been added in 7.6.

Installation is easy, although a complete re-install is required, rather than an in-place upgrade. An upgrade will update the 7.5 pieces, but will not install the new 7.6 pieces.

The main thing of interest is the new WF designer (called WF Composer) that is used to develop and publish WF workflows to the Metastorm database. It's quite a nice tool, but my main gripe is that it's not Visual Studio. If it's aimed at developers then integration with Visual Studio would be a more preferable solution. Metastorm don't have the resources to produce an alternative to VS and hence any WF designer they produce will always look poor in comparison. Perhaps developers aren't the target audience, but if not then why does it include a C# editor? This is also much less useable than the Visual Studio code editor.

Another problem is that it isn't possible to add your own activities into the Designer. I'd always imagined WF could be used by a non-technical person, but to achieve this would require developers to go off and write activities specific to their organisation, that could then be plugged together by the non-technical people. So to my mind, the Composer isn't suitable for a non-technical person either.

To be fair, the Composer does add some functionality to make life simpler. When creating a workflow, it adds the Process Context activity, which provides access to the data in the BPM folder. It also simplifies adding properties to the workflow, since you can add a property just by selecting a name and a type (much like adding custom variables in Metastorm BPM). This then adds the relevant code to the generated workflow. It also includes some activities that implement familiar Metastorm BPM functions such as Email, Manager, SelectSQL etc. Not all functions are wrapped as activities but the rest can be accessed via the generic EvaluateFunction activity.

Executing workflows from BPM is pretty straightforward. Add the Workflow Support Library to a procedure and then use the Integration Wizard to execute the workflow synchronously or asynchronously. One thing to consider when executing a long-running workflow is how you're going to track its life time. It will have to be executed asynchronously since a synchronous execution will lock up your BPM folder and will also likely time out. You can get hold of the ID of the WF instance when you execute it and the tracking data is stored in the Metastorm database so it should be possible to get hold of the tracking data to display in a Metastorm grid. If your WF workflow must complete before your BPM workflow can continue, you'll probably need to add a timed loopback action to keep checking until your workflow is complete. Alternatively you could send a message back from the WF workflow to inform the Metastorm folder it has finished. There is unfortunately no user interface to view WF instances which could make debugging more difficult.

That all said, I am still interested in getting WF workflows executing in Metastorm BPM. This could be very useful so we can re-use functionality in different workflow environments and even help us to migrate away from Metastorm BPM when we are looking for a cheaper solution. But given that I would like to use Visual Studio to author my workflows how do we get them into Metastorm? I'm going to investigate this and will report back in part 2.

Part 2 - The database tables

Part 3 - Using Visual Studio

Part 4 - Using your own activities

Part 5 - Long running workflows

Part 6 - State machines

Thursday, May 15, 2008

No End in Sight: Iraq's Descent into Chaos

Iraq doesn't get in the news much currently, with natural disasters and global financial problems overtaking it in the headlines. But a quick search of Google News shows plenty of people are still dying there. So 'No End In Sight' is still as relevant now as it was when it came out. And a quick précis of the file would be the US (and the UK of course) did one thing right, getting rid of Saddam, then screwed up everything possible after that. It doesn't bother to delve too deeply into the lies that led up to the war but looks at the aftermath instead. First there was no plan, then the plan that was hatched was insanely stupid and it's no surprise that the carnage carried on for so long. Get rid of the army to leave 500,000 soldiers with no work and with a grudge? Brilliant...

The director, Charles Ferguson, has made an unusual change of career. He started out in software, running the company that created FrontPage (and writing an entertaining book about it). Nice to see him put his millions to good use.

Monday, May 12, 2008

Absolutely

It's odd that I watched pretty much every episode of Absolutely when it was on and yet I'd completely forgotten about its existence until I came across an article about it in an obscure South London listings magazine. Perhaps not surprising since it being on telly coincided with my time at university when I was mostly drunk. Anyway, all 4 series are now available on DVD. Go and buy it now.

Monday, May 05, 2008

Synchronous execution of a child XOML workflow

Previously I came up with a reasonable approach to executing a child XOML workflow asynchronously but what I was really after was executing the workflow synchronously. I eventually realised that the solution to this problem was to fire up another WorkflowRuntime and execute the workflow using that. I now have a WorkflowExecutor class as below.

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.Runtime;
using System.Xml;

namespace WFBuild.Activities
{
  /// <summary>
  /// Executes a workflow synchronously
  /// </summary>
  public class WorkflowExecutor
  {
    private AutoResetEvent waitHandle;
    private Guid wfGuid;
    private Exception ex;

    public void Execute(string fileName)
    {
      WorkflowRuntime workflowRuntime = new WorkflowRuntime();
      workflowRuntime.StartRuntime();
      workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(workflowRuntime_WorkflowCompleted);
      workflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(workflowRuntime_WorkflowTerminated);

      FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
      XmlTextReader reader = new XmlTextReader(stream);
      WorkflowInstance instance;
      try
      {
        instance = workflowRuntime.CreateWorkflow(reader);
      }
      catch (WorkflowValidationFailedException exp)
      {
        StringBuilder errorMsg = new StringBuilder();
        errorMsg.AppendLine("Validation failed:");
        foreach (ValidationError error in exp.Errors)
        {
          errorMsg.AppendLine(error.ToString());
        }
        throw new Exception(errorMsg.ToString());
      }
      wfGuid = instance.InstanceId;
      waitHandle = new AutoResetEvent(false);
      instance.Start();
      waitHandle.WaitOne();

      if (ex != null)
        throw ex;
    }

    private void workflowRuntime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
    {
      if (e.WorkflowInstance.InstanceId == wfGuid)
      {
        // pass the problem back to the main call
        ex = new Exception("Workflow terminated - " + e.Exception.Message);
        waitHandle.Set();
      }
    }

    private void workflowRuntime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
    {
      if (e.WorkflowInstance.InstanceId == wfGuid)
      {
        waitHandle.Set();
      }
    }
  }
}

This code is called from the activity like so

using System.Workflow.ComponentModel;

namespace WFBuild.Activities
{
  public class InvokeXomlWorkflowActivity: Activity
  {
    private string xomlFile;
    public string XomlFile
    {
      get { return xomlFile; }
      set { xomlFile = value; }
    }

    protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
    {
      WorkflowExecutor executor = new WorkflowExecutor();
      executor.Execute(xomlFile);

      return ActivityExecutionStatus.Closed;
    }
  }
}

Now this still isn't perfect as a general purpose workflow invoker. Ideally the invoker activity wouldn't wait for the child workflow to complete before returning from Execute. It should start the workflow, return with an Executing status, wait for the workflow to complete, then close itself. But this will do for me so that improvement is left as an exercise for the reader.

Sunday, May 04, 2008

Removing the iTunes crapware

I have to admit to liking iTunes, but one thing I don't like is all the crap it installs alongside it. Some of this stuff is probably essential if you have an iPod, but if not it seems to be pretty much useless. I could get upset about this, but I find the simplest thing is to just remove the unneeded stuff (until the next update). Here's a list of what I've found installed on my PC and how to remove/disable it.

 

Startup programs

These can be removed using Windows Defender (Tools/Software Explorer/Startup Programs)

iTunesHelper.exe

QTTask.exe

 

Services

These can be disabled using Administrative Tools/Services

Apple Mobile Device

Bonjour Service

iPod service