Tuesday, December 31, 2013

Configure BizTalk Receive Location with net.tcp

I just spent ages trying to change a receive location from basic Http to use net.tcp. When I tried to access the web service in IE I kept getting the message "The Messaging Engine failed to register the adapter for 'WCF-CustomIsolated' for the receive location...".

As usual it asks whether the receive location was started (it was) and if the isolated adapter runs under an account that has access to the BizTalk databases (which it does).
I had already followed the step to add the non-HTTP protocols and configured the web site to support net.tcp. I also configured the virtual directory to support http and net.tcp protocols.

To recap when creating the Receive Location I used the WCF-CustomIsolated adapter. I set the Binding to netTcpBinding and set the security mode to None to match the Anonymous access setting in IIS.
The problem was with the address Uri. I was using the address of net.tcp://localhost:808/path/service.svc which is shown in the examples. Changing the address Uri to /path/service.svc solved the problem.

Now when openeing the service in IIS I was able to see the usual service screen (using the http address) and view the wsdl. BTW, I wasn't able to get this to work with the WCF-NetTcp adapter because that too insists on the net.tcp:// address format.

But the next problem came when trying to create a client application to send messages to the net.tcp endpoint. I created a console application in Visual Studio but could not add the Service reference because it would not recognise the wsdl. So I switched the endpoint back to basicHttp and created the service reference in my console application and checked that it worked with basicHttp.

So then I switched the Receive Location back to net.tcp and changed the bindings and client endpoint in my console app to use net.tcp. I thought that was it but when I sent the message it would just timeout.

That's when I looked at the settings on the Receive Location again and realised maxConnections was 0 and the ReadQuota values were all zero.

These are what I set in the app.config of my client
<binding listenbacklog="16" maxconnections="16">

< maxDepth="32" maxStringContentLength="2147483646" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="2147483646"/>

I also added the security section to match that of the server.
<security mode="None">
Finally that did the trick and I was able to submit messages with net.tcp.

Wednesday, April 10, 2013

Submit XML message to BizTalk WCF Receive Web Service

I have been exposing orchestrations and schemas as WCF Web Services and I've been struggling recently in submitting messages to those web services. 
The usual approach I use is to create a test harness (a console app or win forms) and create a service reference to it. Then I go through the steps of creating the request message. My particular issue was trying to create an envelope with an embedded message.
I already had a sample XML message that worked and I found this excellent post which explains how to post an XML message to a WCF Web Service in BizTalk.  No service reference is required and there are very few lines of code. I'm going to use this method a lot more particularly where I have big messages because setting the value of each element can be time consuming.

Follow the steps in the post because they worked for me.  I'm reposting because I wanted to include the code for submitting to a WCF with basicHttp binding. Note the all important MessageVersion which must be set to Soap11.

public class SendBizTalk

{
[ServiceContract()]
private interface IBizTalkSubmission
{
         [OperationContract(Action = "*", ReplyAction = "*")]
         void Submit(Message msg);
}

static void Main(string[] args)
{
     XmlTextReader xmlrdr = new XmlTextReader(@"C:\Projects\Test\FedESB.Test1\Envelope2.xml");
    Message msg = Message.CreateMessage(MessageVersion.Soap11, "*", xmlrdr);

    string uriLocationEsbOnRamp = "http://localhost/FedESBWcfService2/Receive.svc";

     BasicHttpBinding b = new BasicHttpBinding();
     b.Security.Mode = BasicHttpSecurityMode.None;
     EndpointAddress epa = new EndpointAddress(uriLocationEsbOnRamp);
     IBizTalkSubmission proxy = ChannelFactory<IBizTalkSubmission>.CreateChannel(b, epa);
     proxy.Submit(msg);
}
}

Friday, March 22, 2013

BizTalk Deployment Framework

I've been using the BizTalk Deployment Framework in particular for highly available environments where there are 2 Receive hosts, 2 Send hosts and 2 Process hosts.  The 2 receive hosts are clustered which works well when you have orchestrations exposed as WCF web services because you can install the virtual directories on both servers and not have a single point of failure. 

The Deployment Framework allows you to deploy your BizTalk assemblies to the GAC and optionally to install them to the BizTalk management database.  So in this 6 server arrangement you select to deploy to the BizTalk management database on one of the servers and deploy just the assemblies to the GAC on the other 5.

Now the Deployment Framework provides the ability to install your virtual directories as part of the deployment but here is where you hit your first problem. It only does so when you select "deploy to management database" as true which means you only get them deployed onto one server.  And your deployment instructions need to be clear about which server you install the virtual directories on. 

I've deployed a lot of web services so I have experience in creating virtual directories and application pools.  The APPCMD is my best friend and can be called using

%systemroot%\system32\inetsrv\APPCMD

So I started to create a Windows CMD file called CreateVDir.cmd.  And it occurred to me I can use the %COMPUTERNAME% to specify which server I want to execute commands on.

IF NOT "%COMPUTERNAME"=="%RECEIVEHOST1%" GOTO END
IF NOT "%COMPUTERNAME"=="%RECEIVEHOST2%" GOTO END

In this case I am passing RECEIVEHOST1 and RECEIVEHOST2 as parameters to the CreateVDir.cmd along with the Virtual Directory name, the physical path and the user name and password for the Application Pool account.  And the great thing is I can have these as environment settings in the SSO and pass them into CreateVDir.cmd as $(VDIR_UserName) $(ReceiveHost1) etc.

So I just need to create a Target in BTDFPROJ as
<Target AfterTargets="" Name="CustomDeployTarget">
<Exec command="..\CreateVdir.cmd <vdirname> <physpath>  $(VDIR_UserName)  $(VDIR_UserPass) $(ReceiveHost1)  $(ReceiveHost2)"/>
</Target><

I also need to create a CustomUndeployTarget which calls my DeleteVdir.cmd file and then I have all the control I need. I like this approach. I keep my BTDFPROJ file simple and I have CMD files that I can create and test independently of the deployment process.

Good old DOS commands!

Monday, March 18, 2013

Create IIS Virtual Directory using APPCMD

The command line script below allows you to create a Virtual Directory from an existing physical directory and set up an application pool.  In this case the application pool will be running under a domain service account. 

To use an identity of Network Service then replace
/processModel.identityType:SpecificUser  ...
with
/processModel.identityType:NetworkUser


@echo OFF

IF "%1"=="" GOTO Syntax
IF "%2"=="" GOTO Syntax
IF "%3"=="" GOTO Syntax
IF "%4"=="" GOTO Syntax

set VDIRNAME=%1
set PHYPATH=%2
set USERNAME=%3
set PASSWORD=%4

REM Create Application Pool

%systemroot%\system32\inetsrv\APPCMD add apppool /name:%VDIRNAME%AppPool

%systemroot%\system32\inetsrv\APPCMD set apppool "%VDIRNAME%AppPool" /managedRuntimeVersion:v4.0

%systemroot%\system32\inetsrv\APPCMD set apppool "%VDIRNAME%AppPool" /managedpipelineMode:Classic

%systemroot%\system32\inetsrv\APPCMD set apppool "%VDIRNAME%AppPool" /processModel.identityType:SpecificUser /processModel.userName:%USERNAME% /processModel.password:%PASSWORD%

REM Add Virtual Directory

%systemroot%\system32\inetsrv\APPCMD add app /site.name:"Default Web Site" /path:/%VDIRNAME% /physicalpath:"%PHYPATH%"

%systemroot%\system32\inetsrv\APPCMD set app "Default Web Site/%VDIRNAME%" /applicationpool:%VDIRNAME%AppPool

:SYNTAX
ECHO.
ECHO VDir Name and Physical Path Required
ECHO.
ECHO CreateVDir.CMD VDirName C:\PhysPath Domain\UserName Password
ECHO example CreateVDir TEST c:\inetpub\wwwroot\test buildx\btsuser Password1





Execute Permission denied on object 'bts_ProcessHeartbeat_....'

It's always worth reposting a useful tip. This one was written by Paul Edwards in 2010 and I came across the problem recently. It occurs when you are using a Host Instance for an Isolated Host.  The full text of the error is

The following stored procedure call failed: " { call [dbo].[bts_ProcessHeartbeat_BizTalkServerIsolatedHost]( ?, ?, ?)}". SQL Server returned error string: "EXECUTE permission denied on object 'bts_ProcessHeartbeat_BizTalkServerIsolatedHost', database 'BizTalkMsgBoxDb', schema 'dbo'.".


The background is when you create a new Host then not only is the Host added to the BizTalk Management database but it also creates a stored procedure called 'bts_ProcessHeartbeat_'.
A new Database Role is also created called _USERS which includes the AD Group Name you specified when creating the Host.  When you create a Host Instance the Login account you use needs to be a member of this group in order to be granted Execute permissions.

But here's the issue: when using an Isolated Host (for recieve handlers of SOAP and HTTP), the stored procedure is called by the Application Pool account that your web site or virtual directory is running under. So you either need to make this account the same as the Host Instance login account or add it to the group you used when creating the Host. 

Saturday, February 9, 2013

BizTalk Hosts, Host Intances and Groups

People new to BizTalk struggle with the concept of Hosts and Host Instances. I've tried to find a simple article that I can direct people to but the articles I've read IMHO don't seem to provide a simple explanation.  So let me have a go.

When you create a new Host in BizTalk all you are doing is creating an entry in a table in the BizTalk Management database and nothing more.  You can attach an Orchestration to this Host, but it won't run until you create a Host Instance.  When you create a Host Instance you actually create a Windows Service and this is what does the work.  So you can start and stop Host Instances but Hosts have no such concept.

So what is the point of a Host? The answer is that its the way BizTalk offers us scalability. Let me provide the analogy of load balancing web servers. As you know you create 2 web servers that have different IP addresses.  And then someone sets up a load balancer for you and gives you the load balanced IP address (or the equivalent DNS entry).  When I point to the web server I use the load balanced IP address.  Same thing with BizTalk Hosts.  The Host entry is my load balanced "address".  Now it should make sense as to why I bind my Orchestrations to the Host name and NOT the Host Instance.  Its so I can get load balancing.  Now obviously with just 1 BizTalk Server this doesn't make a lot of sense so the next thing to understand is BizTalk Groups.

You can have several BizTalk Servers that belong to the same BizTalk Group.  So is it analogous to a load balanced cluster? No, not exactly, its more of a mechanism for distributing the processing across multiple servers.  They key point to understand is that there is only 1 copy of the BizTalk databases sitting on the SQL Server.  They are created when I configure my first BizTalk Server. 

When I add the second BizTalk Server and configure it I select the option to "Join an existing Group".  When configuration is complete and I open the BizTalk Admin console on my second server I see the Hosts that are created on Server 1.  Remember a Host is just a database entry and members of a BizTalk Group share the same database. 

Now here comes the magic bit. I can create a Host Instance on each of these two BizTalk Servers that will run my orchestrations.  So each now has a Windows Service running and BizTalk does the load balancing for me. 

Actually that is NOT how you should start out along the scalability route. There are some good articles on best practices for scaling BizTalk Server. The first configuration is 3 BizTalk Servers in a Group, one configured as a Process Host, one as Receive Host and one as the Send Host.  That's the first step in scalability.  You can then go on to add more servers to the group and use BizTalk's load balancing capability.  But this is where I'm going to stop because I'm not going to repeat what is in those excellent articles. 

Hope that helps.