How to Debug Tridion Workflow

With all the anticipation and excitement of the new 2013 Bundle Workflow coming out, I thought I’d dig into my notes about the regular ol’ single item workflow for those still on pre-2013 systems.  In this post I discuss some lessons learnt about debugging workflow.  Note, this is when I was working with Tridion 2009 SP1, but the same should apply all the way up to Tridion 2011 SP1 HR1.

There are a few methods available for troubleshooting your workflow code.  There is no way to use a real debugger on the Workflow VB script since there is no way to set breakpoints in the Visio Automatic Activity Script editor.  You can instead keep your VB code as light as possible and keep your workflow logic in C#.  

Attach to Process and Set Breakpoints/Debug Normally

To do this, your code must be outside of VBScript editor in a C# Class Library DLL as mentioned above.

One approach to this is to write a stand-alone executable that you call via the workflow VBScript.  This stand-alone exe would need to impersonate a user on the CM server, but then can do anything you want.  And since this is a stand-alone program running from some directory on the server you can deploy it with a PDB file and attach to its process.

Another common practice of writing workflow code is to write a DLL then register it as a COM+ object or drop it into the GAC.  Note that with the GAC approach you can’t easily put the PDB into the GAC with it (at least for the x86 architecture; for x64 there is a folder deeply nested somewhere in Windows where you’ll see your DLL,so you can put your PDB next to it). Then we simply use Process Explorer (http://technet.microsoft.com/en-ca/sysinternals/bb896653.aspx) to find the process using your DLL and attach to it.

Find Process using a DLL via Process Monitor

Print Logging Statements

If you have some logistical challenges with being able to attach to a process, then we need a way to print debug output statements somewhere – the old fashion way of debugging.  This “somewhere” can be two places:

  1. CM Server’s Event Viewer.
  2. The finish message.  However there is a character limit on how big this can be, and your code may never reach the finish statement.

Using Tridion’s Logger to Print Debug Output

This is the sure way of logging your debug output.

If you’ve moved your workflow code away from VBScript into a C# DLL, then:

Logging logger =  TDSE.GetLogging();
logger.LogEvent("my debug output", EnumSeverity.severityError, EnumEventCategory.EVENT_CATEGORY_WORKFLOW);

You can do the same thing right from the workflow VB script:

Dim logger
Set logger = TDSE.GetLogging()
Call logger.LogEvent("my debug output", 1, 22)

The log messages will appear in the CM server’s Event Viewer log.

If you don’t have sufficient access to the CM server, then you can use the method listed below.  However, in order to effectively develop Workflow you need to get that server access and the ability to freely see the CM’s Event Viewer.  So convince your PM or IT people to grant you that access or you’ll be burning a lot of rubber spinning wheels rather than getting traction.

Using the Finish Message to Print Debug Output

This is a really cheeky approach to debugging.  Do it as a very last resort or while waiting for IT requests to get processed instead of twiddling your thumbs.

Start with one line of code which is:

Call FinishActivity("my debug output", "Next Activity’s Title or TcmId")

Add some code before this line.  Any output you want to see should go in “my message” parameter of FinishActivity.

Note: there is a character limit to how large the finish message can be.  This is driven by the database column size.  So if you try and spit out too much debug output you’ll get an error.

So be warned.  This is a way to do some quick one off printouts, but don’t rely on this as the main way to debug your code (Although back in the day I’ve gotten through coding a massive workflow with this approach before I realized TOM provided a logger, duh!)

Again, a much better way to log debug output is to use the Logger that comes with TOM.

 

Some Workflow Errors and Things to Watch Out For

Position in the Workflow

CurrentWorkItem.ActivityInstance.Position

This one was fun to debug…  Actually if you carefully read the TOM API, which I did not, it provides an example of how to find out how many more activities are left in the flow.  There actually is a comment in the sample code that mentions that this works only if there are no loopbacks.  Be warned!  If you have a case where you need to find out what the previous activity was or what the next activity is, you can’t rely on the position that Tridion gives you because if you have a rejection of content that takes you back a few steps, the position still gets incremented.

Object reference not set to an instance of an object

An error occurred while executing the Workflow script.

The Script Engine returned the following information:

SOURCE:
  Line = 41
  Column = 0
  Number = -2147467261
  Source = ContentBloom.Tridion.Workflow
  Description = Object reference not set to an instance of an object.
  HelpContext = 0
caused by: ContentBloom.Tridion.Workflow
and description: Object reference not set to an instance of an object.
Source:
LogScriptError

This isn’t the VB dying; it’s a null reference exception somewhere in your C# code.  Unfortunately the error doesn’t tell us where – well it tell us which function is being called by VB and its line number – so throw in a bunch of logging statements into that C# code and see where it bugs out.

Application uses a value of the wrong type for the current operation.

Unable to finish the Workflow Activity (tcm:12-671-131104)
Application uses a value of the wrong type for the current operation.

The CM server’s event viewer may show a few errors with some being a blob of XML.  If you look closely you’ll see this message.  What it means is that the size of the FinishMessage is too big.  The database column for this field has reached its limit.

Simple Release Management with SDL Tridion 2013 and Bundles

I was fortunate to have a preview version the up and coming 2013 release of SDL Tridion to play with. One new feature is Bundles, which is basically a new type of organizational item, which allows you to group together related items, and do operations on them as a whole. Typically this would be putting it through workflow, or publishing them all together in a single transaction, but one nice thing about them is that you can use them however you want to, just add items into a bundle, and use it however you need to.

The new release of Content Porter will also be bundle-aware, which got me thinking that there should be an easy way to keep track of changed items during development using bundles to avoid the pain of keeping track manually. It turns out to be really easy using the Event System.

Continue reading

If Publishing Fails with “Request Entity Too Large” Error

Here is a quick Tridion tip.  If publishing fails with an error: “Request Entity Too Large”, it means that the size of the package is larger than what the Deployer is allowed to accept.  To resolve the issue, simply increase the HTTPSReceiver MaxSize attribute in the cd_deployer_conf.xml file.  What scenarios could cause this…

Continue reading

Creating dynamic labels within SDL Tridion content

Recently a client required a way of creating and storing ‘dynamic’ values within the content managed in SDL Tridion.  By ‘dynamic’, this could be anything from loan rates, petrol prices or the cost of a pint of beer.  So today ‘pint_beer_cost’ could be $3.50, tomorrow it could be $4.50.  Having to go through all stored content and replace $3.50 with $4.50, every time there is a need to change a value would be quite painful.

Continue reading

Avoid Static Variables in Tridion Templates

I’ve recently helped a client resolve some interesting issues with their Tridion template building blocks. The issue was that when publishing the first time desired results were produced by the template. However, when publishing many pages, or republishing the same page/component presentation, subsequently produced strange output results.

The logic in our TBB parsed the Output item in the package and made various manipulations to it. I won’t get into the details of that. The issue was due to having static private variables declared within the TBB. These variables were various data structures and primitives manipulated by methods within our ITemplate-inherited class. The methods themselves had to also be declared as static.  Refactoring the code away from “static” resolved the issue.

Here is why you cannot use static variables in Tridion templates:  Primarily because they become global shared variables across all publish threads.  Here is what the Tridion TOM.NET Session and Engine class API states regarding this matter:

IMPORTANT NOTE: Session objects are not thread-safe; you must not share Session objects across threads. Furthermore, the thread that creates a Session object should be in a Single Threaded Apartment (STA), since the Session will internally create an Apartment Threaded COM object. See SetApartmentState(ApartmentState). 

The Tridion Session object, which the Engine uses (or, as the docs put it, “aggregates”) subscribes to the Single Threaded Apartment model (STA).

An STA is basically a model where the server controls the threads, and each individual thread must not spin off additional sub-threads that depend on the STA-controlled objects. So in our case: the Tridion CM is the server that spins off and controls threads (e.g. we can publish many items simultaneously), and each template is executed within it’s own thread. So if we develop a template that spins of other threads, the common memory across threads, i.e. static variables, isn’t managed.  So STA threads can overwrite each other.

Here is a snippet from an article explaining STAs in much more detail:

…all access to global variables and functions of the server will need to be serialized properly because more than one object may try to access these from different threads. This rule also applies to class static variables and functions. [http://www.codeproject.com/Articles/9190/Understanding-The-COM-Single-Threaded-Apartment-Pa]

So in a common scenario, when is it appropriate to use “static” within a TBB? Well, how about in procedural methods that take some inputs, create new objects, massage them and return them. In other words, if the method is self-contained. A Utility class is a common such scenario.

In conclusion, don’t use static variables inside your TBBs or Event System code.

DWT strings, integers and repeatable fields

I was reviewing a solution I’ve been working on and noticed that at one point I was pushing out a ‘QuantityOfListItems’ from C# into the package – and couldn’t recall why.

On further investigation I saw that I was using this in the DWT to detect when I was on the last list item so I could compare this with the TemplateRepeatIndex value. Although it all worked fine it felt like overkill to rely on C# for something so trivial and didn’t add any value so I had a scoot around.

I found a CollectionLength built-in function that looked like it might be just what the Dr. ordered… 

So… the reason for the post… It took quite some phaffing to tweak the syntax for this – just due to the fact I’ve had my head in C# code more than I have DWT lately so I thought I’d share it!

All I need to do is check

  • if the current index of the item in the repeatable field is the first then print the opening tags
  • write out the repeated syntax/value for each instance of the repeatable field
  • if the current index of the item in the repeatable field is the last then print the closing tags
First attempt:
<!-- TemplateBeginRepeat name="Component.Fields.repeatField" -->
<!-- TemplateBeginIf cond = "TemplateRepeatIndex == 0 " -->
<ul>
<!-- TemplateEndIf -->
<li>@@Field@@</li>
<!-- TemplateBeginIf cond = "TemplateRepeatIndex == @@CollectionLength("repeatField")@@" -->
</ul>
<!-- TemplateEndIf -->
<!-- TemplateEndRepeat -->

OK, so the obvious error – TemplateRepeatIndex is 0 based whilst the CollectionLength returns a 1 based  result! The other syntax is pretty standard so we’ll concentrate on the test for the last index item.

<!-- TemplateBeginIf cond = "TemplateRepeatIndex == @@CollectionLength("repeatField")-1@@" -->

No Joy. So I have another scoot around and find an interesting StackOverflow post reminding me the value returned is a string. As we need to perform an integer calculation and comparison we need to parse the value to an integer.

<!-- TemplateBeginIf cond = "TemplateRepeatIndex == @@parseInt(${CollectionLength("repeatField")})@@-1" -->

Now I’m using the debug method Frank van Puffelen describes in his StackOverflow posting and all appears well – I can see the CollectionLength and the TemplateRepeatIndex are both 1 and the end tag still isn’t written out.

See the deliberate error (well it wasn’t deliberate and it was one of those annoyingly simple things that I’m sure others will trip up on!).  Although our first comparison with TemplateRepeatIndex and == 0 above is with an integer and it works as we want (even if the comparison is against a non-zero integer) I figured I’d try to parseInt the TemplateRepeatIndex…

<!-- TemplateBeginRepeat name="Component.Fields.repeatField" -->
<!-- TemplateBeginIf cond = "TemplateRepeatIndex == 0 " -->
<ul>
<!-- TemplateEndIf -->
<li>@@Field@@</li>
<!-- TemplateBeginIf cond = "@@parseInt(TemplateRepeatIndex)@@ == @@parseInt(${CollectionLength("repeatField")})@@-1" -->
</ul>
<!-- TemplateEndIf -->
<!-- TemplateEndRepeat -->

Success!

Hopefully this simple pattern can assist you in not having to resort to C# as I’d figured I had to originally.

Quick Tip: Turn off verbose template logging in Tridion Event Log

As a good developer, you write out useful information and debug messages in your .NET TBBs using the built in templating logger. This is great when running the TBBs in the template builder to give a bit of extra info about the processing. However, by default info messages will be logged in the Tridion Event Log of your Publisher server, causing log bloat, and making it harder to see important Error and Warning messages. Its really easy to turn this off, but I am not sure I have seen it documented anywhere. Heres how…

Continue reading

Localization != Personalization. Why CXM Shouldn’t Affect Your SDL Tridion BluePrint… Much.

I’m seeing challenges and confusion with BluePrinting and Targeting in BluePrint workshops and in discussions about profiling and personalization. Don’t let the rise of “Customer Experience Management” (CXM or CEM for TLA fans) adversely affect your SDL Tridion BluePrint. Localization != Personalization (!= is code, literally, for “not equals”).

Continue reading

Adding page-free content to your site with ASP.NET MVC

MVC ASP.NETMost implementations I have worked on have sections of the website which consist of some kind of listing, which links through to pages containing detailed content. The details pages themselves contain a single ‘Full Article’ component presentation, and are often auto-created and published using the event system. As such, the pages do not really serve any purpose other than providing a definition of the url for an article. 

This article shows an alternative approach to get rid of pages for a certain section of your site and use the features of ASP.NET MVC to show content without losing ‘real’ urls

Continue reading