(Update: See the latest on the Deployment Framework
here.)
I see that the GotDotNet workspace I referenced in my last post is having occasional trouble, so I'm making the current version of template available for download on this site.
(Update: See the latest on the Deployment Framework
here.)
It has been several months since I initially posted on the topic of using NAnt to coordinate the deployment of BizTalk 2004 solutions, with updates here and here. Since that time, I've been heads-down in a BizTalk project and have had a chance to refine the practice quite a bit.
So, I took the opportunity to introduce quite a few improvements into the sample I had previously released - hopefully turning it into a "template" that can be used on your projects more easily. To that end, I've created a GotDotNet workspace where you can download the current version of the build template, and I invite any feedback/suggestions/participation.
The major changes I've introduced can be categorized as follows:
- Split binding files: On a BizTalk project with multiple developers, I believe it is beneficial to split your binding files into content related to orchestrations vs. content relating to "everything else" (send ports, receive ports, etc.) The new build template sample does just that.
- Improved scripts: Many of the WMI-related scripts I relied on in the past were from BizTalk SDK samples. These were modified to get better/different error handling behavior.
- More generic handling of send ports, receive ports, receive locations, etc. in the script - though it still requires some maintenance.
- Better NAnt citizen: I'm still not a NAnt expert by any stretch, but thanks to Duncan Millard I realized that I had been remiss in applying quotes to a good deal of the text names I was using (i.e. "). In addition, I started using built-in NAnt tasks for at least a few items where I had overlapped functionality, though not in cases where I needed slightly different behavior. Finally, the build file naming convention proposed by the build template is "BizTalkSample.sln.deploy.build" (rather than "BizTalkSample.sln.build") to avoid conflicting with a NAnt file that would actually be used to build the project (e.g. with Cruise Control), as opposed to deploying it.
- Use of "Deploy with NAnt" in an MSI scenario: This is the biggest (and I believe most useful) change...Let me explain further.
As you study the build script, you will notice that it now also tackles such tasks as deploying virtual directories and applying permissions to them, patching binding files to match “local conditions”, etc. The initial goal here is to ensure that in a team development scenario, the amount of “out of band” setup required for any given developer to establish their BizTalk 2004 project environment is minimal – and that the current topology for the project is communicated efficiently (i.e. through the build file, as opposed to email or word-of-mouth.)
After a development team has spent weeks or months with this build script, refining it to represent their exact situation and deployment needs, a question might arise: Why not take this well-tested script and use it for production (or just non-development-machine) deployments as well?
To do this, it is helpful to agree that a two-phase deployment of BizTalk-related projects is both acceptable and useful. The first phase is an MSI-based installation that simply installs all of the BizTalk-related assets in a specific directory, but doesn’t deploy them to the BizTalk server. The second phase occurs when the user goes to the Start-Programs-YourProjectName menu, and chooses “Deploy YourProjectName”. The user also has the option to un-deploy (leaving the assets still on the file system until the MSI is uninstalled) or redeploy (to support the case where a single file is “patched”.) See a picture of the Start menu created by the build template's MSI file.
The reason that two phases are useful is that if the deployment to BizTalk fails, we would like a complete log of the results (which in this sample occurs in the DeployResults directory) for analysis/diagnostics, and we would rather not have the MSI simply roll back. The same holds true of un-deployment.
Dual-purposing the NAnt file you have been maintaining for “real” deployments can be summarized as follows:
-
Use the build template's approach (see BizTalkSample.sln.deploy.build in the download) for the “server.deploy” and “server.undeploy” targets, which make the build file able to work with “co-located” binaries in addition to the standard developer tree.
-
Create a Visual Studio Setup project that deploys your BizTalk assemblies according to the pattern shown in the BizTalkSample.Setup project. Notice that this setup project includes the NAnt build file, the “DeployTools” directory (with our scripts), as well as a subset of NAnt in a subdirectory of our installation (so that we don’t rely on NAnt being present on the target server.) It also includes a few convenience batch files referenced by our Start menu entries for invoking NAnt with specific targets.
The BizTalkSample.Setup project in the build file can be built, installed, and deployed to try this out. (You will want to make sure you haven't already deployed in "developer" mode, because the "Deploy BizTalk Deployment Sample" passes a flag to the NAnt script that will skip the undeploy phase in order to speed up "clean" installations.) Note that as your deployment grows more sophisticated - multiple hosts, servers, etc. - you will find the NAnt script requires additional properties to govern these variations between server deployments and developer-desktop deployments.
The build template sample also discusses the use of NUnit as a post-deployment verification process. See the documentation in the sample for a full discussion -- basically all we are doing is making sure our MSI includes a subset of our unit tests, NUnit, and appropriate test files. This is then wired up to the "Verify Deployment" Start menu option. This can be an extremely effective means of ensuring your operations team has a means of testing the system interactively (not to replace standard automated heartbeat tests you might have…)
Peter Provost proposed a great generic approach to building BizTalk NAnt files a couple of weeks ago - and I shamelessly stole his approach to reverse the "orchestration ordering" property (very slick!) If you do not wish to pursue the MSI option just discussed, and you do not wish to integrate the "out of band" setup actions discussed above (i.e. virtual directory creation, etc.), you might prefer his approach or you might want to push the build template sample provided here in that direction (i.e. more property driven.) However, I believe tackling the kinds of concerns that we do here will generally mean that a real-world project will no longer have a generic-looking NAnt script.
Again, you can download the build template sample here. Enjoy, and I hope you find it useful.
A lot has been said regarding the MSMQT adapter for BizTalk 2004 already, but
below are a few recent observations that may be of help to you.
When people ask me what MSMQT is, my short answer goes something like this:
"MSMQT is the name of the BizTalk 2004 adapter that implements the MSMQ
network protocol directly within BizTalk. It allows BizTalk 2004
to send/receive MSMQ messages directly, and move messages to/from the
MessageBox very quickly - without external (DTC) transaction
coordination. Only private queues are supported for receives."
Using the MSMQT adapter you can:
-
Use Send Ports to send to public or private queues on remote machines - but the
queues must be transactional.
-
Use Send Ports to send to local queues, which will be private
since that is all MSMQT supports.
-
Use Receive Locations to receive from locally defined queues, which will be private
since that is all MSMQT supports. These queues can be either
transactional or non-transactional. You cannot monitor remote queues
(i.e. receive locations can't reference remote queues.)
You cannot using the System.Messaging MSMQ APIs/COM MSMQ APIs/MSMQ C Libraries
or the MSMQ MMC console on a BizTalk 2004 machine that is running the MSMQT
adapter! This is because these all rely on the native MSMQ Service
running.
You can use the aforementioned APIs (though not the MMC console) from remote
machines (that are not running the MSMQT adapter) to put messages in
MSMQT-defined queues.
To test MSMQT queues on the BizTalk 2004 server where they are defined, you
can:
-
Create a Receive Port and a File Receive Location that will monitor a
particular directory...
-
Create a static one-way Send Port that specifies MSMQT & the queue you want
to test as the destination. In the "Filters" portion of the Send Port
configuration, define BTS.ReceivePortName = ReceivePortNameFromStep1
Now, when we you drop a file on your directory, it will be routed to your
queue. The receive location could also use HTTP posts, in which case the
WFETCH tool from the IIS Resource Kit (or a similar tool) will let you easily
get test messages to the queue.
Note: If you have existing applications that used to send to public MSMQ
queues, but will now be sending to MSMQT queues, the most common problem is
that they are referencing those queues via the PathName property, rather than
the FormatName property (with 'DIRECT=OS:machine\private$\qname' syntax.)
This is only a problem for users of the COM/C-Library APIs - the
System.Messaging.MessageQueue class actually allows format names to be used
with the Path property.
See this
Microsoft Support discussion for lots of additional insight into
MSMQT.
There are several QFEs for BizTalk 2004 that you may want to know about, in
case you encounter the situations described below.
Situation: You attempt to create or edit Receive or Send ports
within the BizTalk Explorer in Visual Studio, and CPU consumption hits
100%. Memory consumption climbs until the IDE crashes with an
OutOfMemoryException.
Fix: Ask PSS for the hotfix associated with KB870619 (also
known as hotfix 1185)
Situation: You have a CDATA section in an inbound xml document
within an orchestration. The CDATA section contains flat file data, with
vital trailing (or leading) whitespace. After you execute a transform in
your orchestration, the CDATA designation is stripped (although the flat data
is still there) - and the leading/trailing whitespace is now lost.
Curiously, using the "Test Map" feature (by right-clicking on a map in the
solution explorer) doesn't exhibit this behavior.
Fix: Ask PSS for the hotfix associated with KB841563
Situation: You have a scope shape, and scope timeouts aren't
working as expected. Specifically, for blocking calls in an expression
shape within the scope (like a DCOM call, etc.) where the timeout is being
exceeded, you see 100% CPU consumption in the BizTalk service and the
orchestration never terminates.
Fix: Ask PSS for the hotfix associated with KB811250.
(Note: this problem may have been addressed in the fix rollup released in April
- I'm not sure what the ordering was here.)
(Update: See the latest on the Deployment Framework
here.)
Addressing a few comments/questions that have appeared -
- Bootstrapping your binding file: It was pointed out that a bootstrapping process is required for your initial BizTalk Orchestration binding file. This is quite true - you either need to manually deploy your orchestration(s) and associated assemblies, and use the BizTalk Deployment Wizard to create a binding file, or you need to create a binding file by hand (using a previous file as an example...)
- Automatic maintenance of your binding file: Hermo Terblanche would like the binding file to be maintained automatically (as part of the build process.) One could imagine automatically “refreshing” this file using BTSDeploy at build time. However, there is a question of what the source of truth should be for your bindings - I would argue it should be the binding file, not the current configuration of the server. Others might disagree, at least during development.
- Rule deployment: Chris Delaney is looking for a method of deploying rules. There doesn't appear to be a command line tool for this (that you could call from NAnt), but it looks somewhat trivial to write. See \program files\microsoft biztalk server 2004\sdk\samples\business rules\business rules hello world2\HelloWorld2.cs - specifically the LoadFromFile and DeployRuleSet methods.
(Update: See the latest on the Deployment Framework
here.)
I realized today that in the "deploy.orchestrations" target of the NAnt build
file (discussed
here) there is a bug in the ordering of operations. The original
file imported the binding file prior to deploying the orchestration - which
doesn't work for a "first time" deployment.
In addition, Hermo Terblanche made a good point (in comments) regarding
piplines which are used by Send/Receive ports - i.e. the ports need to be
removed prior to removing/updating the corresponding pipeline assembly.
Therefore, an additional dependency has been added to the "undeploy.piplines"
target - namely, "remove.ports". This target will remove exactly one Send
and one Receive port, the names of which are derived from the binding
file. This section of the build file will have to be customized for your
purposes, just like the names and ordering of orchestrations. Note that
in my sample, the orchestration uses “Specify Now” ports, and
removing the Send/Receive ports is probably not necessary. However, for
“Specify Later” ports (the more typical case) that use custom
pipelines, you will get the error referenced in Hermo's comment if you do not
remove the ports prior to updating the pipeline assembly.
The zip file with a new build file is still located
here. Current NAnt output from the sample will look like
this. Enjoy!
(Update: See the latest on the Deployment Framework
here.)
One of the more complicated aspects for a developer using BizTalk 2004 is the
large number of steps required during the edit/run/debug cycle. Since
your BizTalk artifacts can't be run "in place" (that is, where they were
compiled) but must instead be deployed to a local (or remote) BizTalk server,
life can get quite complicated.
If you have done much BizTalk 2004 development, you know the routine quite
well at this point. If you have orchestrations, schemas, transforms,
pipelines, and (say) C# components all partitioned into separate
assemblies - and you have a number of orchestrations with "Call/Start
Orchestration" shape dependencies that introduce specific start/stop-ordering -
you can spend a lot of time doing the whole
stop/unenlist/undeploy/redeploy/enlist/start/bounce BizTalk routine.
Worse, you might not get it right - which can lead to hours spent debugging
problems (over the course of your project) that don't really exist.
To alleviate this problem, it can be quite helpful to use a tool like
NAnt to coordinate the "update my BizTalk server" process that must
occur after each build. (NAnt is a large topic - suffice to say it is an
Xml-driven software build system.) As long as your NAnt build file (and
BizTalk bindings file) are kept up to date, the whole process can be reduced
to:
-
Comple your solution (which might have multiple orchestrations, schemas,
transforms, pipelines, and external components in separate assemblies)
-
Choose "Deploy to BizTalk" from your Tools menu.
-
Wait 60 seconds or so, enjoying the feeling that you have a consistent &
reliable deployment mechanism.
In addition, you can of course use NAnt to kick off full unattended builds for
nightly builds or continuous integration systems (like
Cruise Control). Since Visual Studio.NET is the
only supported way to build BizTalk projects - despite that tempting
XSharpP.exe file sitting in your BizTalk installation directory - this part of
your NAnt build file must defer to calling devenv.exe in command-line
fashion. (A short "getting started" for NAnt & directions for using
with a VS external tool can be found here.)
So, how about a sample that shows using NAnt to coordinate a reasonably complex
deployment of inter-related BizTalk projects? Available
for download is a zip of a BizTalk solution that contains the following
projects:
-
BizTalkSample.Components (a C# project, with a class called from
orchestration)
-
BizTalkSample.Orchestrations (that contains two orchestrations, one which
calls the other via a Call Orchestration shape)
-
BizTalkSample.Pipelines (a send and receive pipeline, which are not needed
but included to illustrate the deployment aspects)
-
BizTalkSample.Schemas (which includes two schemas)
-
BizTalkSample.Transforms (which contains a single transform used by one of
the orchestrations)
The item I hope you will find most interesting, however, is a
file called BizTalkSample.sln.build. This is a NAnt
build file that has a deployment target which
captures the following dependency tree (only partially blown out here, for
brevity - but this no doubt seems familiar to you if you've been using Biztalk
2004 for awhile…)
Deploy
Deploy Schemas
Undeploy Schemas
Undeploy Orchestrations
Unenlist Orchestrations
Stop Orchestration
Undeploy Transforms
Undeploy Orchestrations
Deploy Components
Deploy Pipelines
Undeploy Pipelines
Undeploy Orchestrations
Deploy Transforms
Undeploy Transforms
Undeploy Orchestrations
Deploy Orchestrations
Bounce BizTalk
Perhaps more illustrative is the output of the deployment itself - a sample of
which can be seen here. Note that the
NAnt script relies heavily on the EnlistOrch.vbs and StopOrch.vbs script files
that ship as samples with BizTalk 2004, as well as the BTSDeploy command line
utility.
The BizTalkSample.sln.build script included in the download should
represent a BizTalk project organization of sufficient complexity to act as a
template for your own projects. You will want to maintain the list (and
correct ordering!) of the names of your orchestrations within the build targets
for "deploy.orchestrations" and "unenlist.orchestrations" (this isn't
deriveable automatically…)
Note that the build script relies on a set of naming conventions that will be
evident once you spend some time with it - namely, that directory names
correspond to assembly names. In addition, the standard "Development" and
"Deployment" configurations in BizTalk projects have been replaced with "Debug"
and "Release", in order to not create inconsistencies with standard .NET
projects (and allow for one "Configuration" property within the build
script.) This replacement was accomplished with a file-level
search/replace.
There is probably room for more sophistication in this build file. It
takes a somewhat pessimistic view of what must happen for each
edit/run/debug/deploy cycle, but I've found that despite the 60 seconds spent
executing the script, your net productivity gain will be quite high given the
time you won't waste trying to figure out what aspect of your deployment isn't
correct. Leave comments with suggestions for improvements, if you like.
In the latest round of documentation that was released for BizTalk 2004 there is an "orchestration operator" defined that was not previously documented: 'succeeded()'
The documentation states that this operator can be used to determine the outcome of a transactional scope or orchestration. When might this operator be needed?
Well, it turns out that the orchestration compiler has some interesting rules about what you can do in an exception handler that might not be entirely intuitive at first (though as you reflect on analogies to C# or other exception-enabled languages, it begins to make sense.)
Suppose that you have defined a Request/Response port as the means of interacting with an orchestration, and you want to ensure that some response is generated regardless of the failure conditions you encounter. Your first attempt might look like this (I know mine did…) Stretch this JPG out to full size to see it clearly (IE will shrink it.)
This will generate a compiler error:
error X2162: must receive before sending a fault message on an implemented port
What is going on here? It sure seems as if we have received a message already - we did it in the Rcv_SomeDoc shape. However, we have the Snd_ResponseDoc shape inside of Scope_WorkThatMightFault, and the orchestration compiler is assuming that we might have already executed that shape prior to the catch block executing (i.e. prior to an exception being raised.) A Request/Response port must only have one response for any given request…and our Snd_FaultDoc shape has the potential to violate this rule. It sure would be nice if X2162 could be more explanatory in this regard…
How do we overcome this? It isn't terribly obvious…We must wrap the Snd_ResponseDoc shape in an (additional) transactional scope, and check in our catch block to ensure that the associated transaction did not succeed before performing Snd_FaultDoc. See this diagram (Acrobat required).
What we are doing here is structuring the flow such that exactly one response will be sent for the original Rcv_SomeDoc shape. The way we do this is to use a Decide shape, with an expression such as "!succeeded(Transaction_SndResponse)" in the rule branch. The Snd_FaultDoc will be in the 'true' side of the branch (i.e. we did not successfully perform Snd_ResponseDoc), while the 'false' side will likely be empty.
This is a pretty subtle bit of enforcement that the orchestration compiler is performing. It is somewhat analogous to a typical language compiler ensuring that all code paths have a return value for non-void functions or methods. And, of course, even though it is not enforced, it is certainly the case that 'catch' and 'finally' blocks in standard languages often have to be aware of what has or hasn't taken place in the associated 'try' block. The orchestration compiler (apparently) just has some well-defined & strict rules it wants orchestrations to adhere to (such as "exactly one response for each request emanating from a Request/Response port".)
There is a somewhat similar case that is described briefly in the BizTalk documentation. Imagine we wish to make reference to a message or variable in our 'catch' block that was initialized within the associated scope. In this case, the orchestration compiler will assume that we might not have gotten around to initializing that variable/message prior to the exception being thrown - and a compiler error will be generated as a result: error X2109: use of unassigned local variable 'Variable_Blah'
In this case, we can wrap the portion of the scope's work that is responsible for initializing the variable/message of interest in an (additional) transactional scope (i.e. "Scope_InitWork"), and we can use a Decide shape with an expression such as "succeeded(Transaction_InitWork)" in the rule branch. This will allow the orchestration to compile…
Wow! One might start to agree with Charles' boss...
(See
here for the current version of the naming conventions!)
One of the primary benefits of the BizTalk Orchestration model is the great
transparency you can get when a software implementation is pictorial.
Regardless of how well a developer comments code, there will always be a
need to maintain a separate set of artifacts (UML diagrams, Visio diagrams with
random shapes of your choosing, prose documents, whiteboard discussions, etc.)
that are used to convey what the code is actually doing especially when working
with a business audience. This is true if for no other reason than that a
discussion of interesting functionality will often take place at a different
level of granularity than raw code can support
Round-trip engineering tools - that attempt to keep code in sync with diagrams
often seem to suffer from a lack of fidelity that renders them ineffective.
With BizTalk Orchestration, the diagram is the implementation (at least
at a particular level) of a piece of functionality. Yes, you can
disappear into components and lose sight of what might happen. Yes, there
is a code representation (xlang/s) underneath the orchestration but it seems to
be completely isomorphic with the diagram.
So the opportunity exists to use an orchestration diagram in several
interesting ways within a project lifecycle:
-
As a way to capture an initial high-level design, using all the orchestration
shapes as intended but not yet bothering with real maps and schemas.
Stubbing out schemas (so you can declare messages and variables to be of proper
types) and maps will allow you to flesh out the orchestration diagram(s) quite
a bit, using the compiler as just a way to check for consistency. All of
the external system interactions, communication patterns, decision points,
parallel vs. joined flows, etc. can be represented at this point in a shell
orchestration.
-
As a way to gain consensus with the development team & business sponsor
about whether the right functionality is indeed going to be built. The
high level design just described is a great tool for this discussion. Put
your orchestration(s) up on a wall with a projector and do a walk-through with
as many of the project stakeholders as makes sense. Or use a tool like
CutePDF
to print the orchestration as a PDF to send around via email. (Of course,
once Microsoft ships the Visio add-on for BizTalk 2004 orchestrations, this
will represent another option for non-VS.NET users. This has the added
benefit of allowing you to exclude what you might consider to be lower-level
detail by setting the Report to Analyst switch on various orchestration shapes
to False.)
-
As a way to estimate work. The various shapes in your initial
orchestration can often represent reasonable granularity for time estimates.
-
And finally, as a way to guide project work...Rather than starting with the
entire orchestration that you created to support steps 1-3, you might find it
easier to create a new orchestration that represents the path(s) you are
tackling at a particular point. You can cut/paste portions of that
original orchestration or simply use it as a reference for what comes next it
serves as your outline.
To help realize some of these benefits, naming conventions within an
orchestration are quite important
While the naming conventions are good practice for variables, Messages,
Multi-Part types, etc. they are even more import for the workflow shapes.
The goal is to ensure that the intent of each shape is clear, and that the text
associated with the shape conveys as much as possible given the space
constraints. In this way, a non-technical audience will be able to use
the orchestration as documentation.
(See
here for the current version of the naming conventions.)
Respond with comments & the document will remain updated per your feedback!
|