Tuesday, April 7, 2009

My Resume

I’m not officially seeking for a new position but if you interested, you can follow this link to find my Resume.

I haven't been posting for a while, but - coming really soon my next post about Microsoft WCF vs. Java SCA and what this mean in the BizTalk perspective.

Wednesday, December 17, 2008

Managing a BizTalk 2006 MSI deployment packages

One of the features of BizTalk 2006 deployment module is the ability to export and import a BizTalk Application as MSI installation package.

In this way we can treat each application as an atomic deployment package. This means that if we need to update our application resources with a new version, we can create a new MSI version 2.0 with all new versions of the resources and then deploy them all in one managed package.

The creation of the MSI package can be done from within the Administration Console MMC or through BTSTask.exe script.

 

or with BTSTask.exe:

ExportApp -ApplicationName:MyApp -Package:"C:\Temp\MyApp.Msi"

To see BTSTask.exe usage:

C:\Program Files\Microsoft BizTalk Server 2006>BTSTask.exe ExportApp -?

All BizTalk Exported MSI files are always versioned ‘1.0.0.0’

Both BizTalk Administration console and BTSTask.exe sharing the same API to create this MSI file. The methods and classes to administrate BizTalk reside under Microsoft.BizTalk.ApplicationDeployment.Application namespace of assembly Microsoft.BizTalk.ApplicationDeployment.Engine.

The method that actually creates the MSI package is

CreateMsiPackage(string msiPath, ICollection<Resource> resources, ICollection<string> applicationReferences, IDictionary<string, object> requestProperties)

Now, unfortunately by using this method, BizTalk always creates the MSI file version as 1.0.0.0 – this means that we can’t easily manage our MSI installation packages because even if we're creating MSI of version 2.0, BizTalk will export it as 1.0  

This restriction is for some reason hard-coded and you just can't change it.

in the following figure you can see the hard-coded msi version:

 

How can I change the MSI version and successfully manage my installation packages

One of the ways I’ve seen people handling this restriction is by simply name the generated  MSI installation Package with some Naming-Conventions like <ApplicationName>_<VerNum>.MSI

I consider a file name as a poor way to ensure version identity, a better idea is to somehow "Patch" the MSI metadata itself to hold the correct version. in this way, I can assure that no one will accidently rename my MSI file (just think on how easy that can happen).

What is BizTalk MSI installation package

Before I'll show you how to patch the file, it's first must be understood that an MSI file is actually a Database file. Inside the MSI there's some system table that contains a lot of data like parameters of the MSI, the name of the CAB files, the Binary stream data content of the CAB files, custom actions etc.

The MSI Installer engine executes SQL queries against the MSI tables and by that installing and extracting the actual files. the functions required to work with MSI protocol can be reference through msi.dll unmanaged DLL under windows system directory.

You can find the MSI database documentation on MSDN here: 

Browse into BizTalk MSI

The best way to know what MSI in general (and BizTalk MSI file in particular) means is by using "Microsoft Orca".

As you can see in the figure above, the version of the MSI is saved in 'ProductVersion' cell under 'Property' table.

So, the solution to BizTalk hard-coded 1.0 version problem is to change this value inside 'Property' table. but how can we get inside the MSI tables and change this value? – one way is by using a tool like Microsoft Orca, but in this way, the developer (or administrator) must patch the file by hand and it’s a path to future mistakes. I consider it to be a good solution only if we can some-how automate the process afterwards, so some like always some C# code can do the trick.

Patching BizTalk MSI file

BizTalk ships with a helper class to manage MSI and CAB files. The assembly name is Microsoft.BizTalk.MSIManager.dll

This class is actually a managed code wrapper around the MSI.dll functions and mach more easy to code then PInvoke. BizTalk infrastructures API's for deployment uses this class to create and import files form BizTalkMgmtDb into an MSI installer file and vise versa.

So, now we understand that we can't force BizTalk to export the MSI with another version then 1.0.0.0 but we can still use BizTalk helper classes to get an easy access the MSI tables and to change the information holds inside the exported MSI.

Here's the code snippet:

        public void UpdateMSIProps(string msiPath, string version)

        {

            Database db = Installer.OpenDatabase(msiPath, DatabaseOpenMode.Transact);

            string query = @"UPDATE Property SET Property.Value = '" + version + "' WHERE Property.Property = 'ProductVersion'";

            Record rec = null;

            Microsoft.BizTalk.ApplicationDeployment.MSIManager.WindowsInstaller.View vw =

            db.OpenView(query);

            vw.Execute();

            vw.Close();

           

            db.Commit();

            db.Close();

            db.Dispose();

        }

I also guess you’ll probably want to automate following steps as a script:

  • BTSTask.exe -AddResource
  • BTSTask.exe -ExportApp
  • Patch MSI
  • Move MSI to the destination MSI's folder.

Conclusion

Although BizTalk can export Application from BizTalkMgmtDb into a MSI installer package, it’s also forces this package to be of version 1.0.0.0

we simply can't do much with that because it's hard-coded with BizTalk deployment API but we does can take the newly generated MSI and 'Patch' the correct table with our desired version.

In this way we can handle our MSI versioning in the correct way.

Thanks’ to Moti Feldman for helping me with this issue.

Wednesday, September 24, 2008

BizTalk 2006 MMC snap-in limitation

No doubt BizTalk 2006 MMC snap-in is a great move forward since the days of BizTalk 2004 Explorer Add-in. although, the GUI itself suffers sometimes from a problem or another. I think the mostly common problems are missing features or incomplete implementation. one example is when you try to start application. you can right click on the BizTalk application name and choose start. in this case, BizTalk administration console consider the activity to be under one 'transaction' so if one orchestration in one application fails in the start process - all other orchestrations, send-ports etc. in the whole application will automatically rollback to the previous state.

The reason is that under the surface the GUI is using Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer class (please note that it's actually also make use of Microsoft.BizTalk.ApplicationDeployment.Group class - but I'll ignore that for the moment).  when you creates instance of this class and make some changes to send-port etc. the change will not reflect to BizTalkMgmtDb until a call to the SaveChanges() method.

If I could select all the applications in the BizTalk Group and choose them to start (a great feature that for some unknown reason is forbidden and you can start only exactly one application at a time )  the pseudo code in the MMC is something like this:

Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer e = new Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer();
            e.ConnectionString = @"Data Source=" + dbServerName + ";Initial Catalog=BizTalkMgmtDb;Integrated Security=True";
 
            try
            {
                foreach (Microsoft.BizTalk.ExplorerOM.Application app in e.Applications)
                {
                    app.Start(Microsoft.BizTalk.ExplorerOM.ApplicationStartOption.StartAll);
                }
 
                e.SaveChanges();
            }
            catch (Exception ex)
            {
                throw ex;
            }

As you can see, all successfully started applications state has not been saved because that one application failed.


I think it's need to be like this:


            foreach (Microsoft.BizTalk.ExplorerOM.Application app in e.Applications)
            {
                try
                {
                    app.Start(Microsoft.BizTalk.ExplorerOM.ApplicationStartOption.StartAll);
                }
                catch (Exception ex)
                {
                    bool whatToDo = AskTheUserWhatToDo();
 
                    if (whatToDo)
                        throw ex;
                }
            }
 
            e.SaveChanges();

As an insight, the semi-unfriendly BizTalk 2006 Administration console GUI is not a direct effect of BizTalk deployment API's architecture - it's more like a half implementation in the MMC snap-in.

Tuesday, August 26, 2008

Unexpected disappearance of messages after executing BizTalk pipeline component

Here's another 'mysteries' behavior in BizTalk pipeline components:

If you're promoting a context property that only was 'GAC'd but haven't successfully deployed into BizTalkMgmtDb (by adding the BizTalkAssembly as a resource in BizTalk management console), at runtime the pipeline component will search the context schema generated class (see here) in the machine GAC and will promote the value as expected, BUT at the end of the Execute (IPipelineContext pContext, IBaseMessage pInMsg) method, BizTalk will 'freeze' like no message was submitted into the message-box by the pipeline. also there's no error messages in the event viewer. this is happens even if the promoted property is not used as the subscriber parameter.

It's not surprising that for a well working context property you must first deploy the property schema. the strange thing here is one, the way BizTalk pipeline components promoting values which is without any validation inside BizTalkMgmtDb schema tables. second, the unexpected disappearance of the message without any clue in the event viewer.

I guess this is one of the problems with the two management repositories of BizTalk artifacts: GAC and BizTalkMgmtDb database. for example: when you're looking for a IDocumentSpec object using GetDocumentSpecByName method - BizTalk will fetch the schema by executing a query against the database. but when working with a context property, in the pipeline perspective, even if it uses the previously generated schema class to get the name and namespace, the purpose is to add the value into the context property list with IBaseMessageContext.Promote method, so the pipeline is not looking inside the database to actually validate the context property existence and the pipeline assumes the developer is knowing what he is doing (yeah, right...). the problem is that afterwards, the pipeline is submits the message into the message box and then the routing engine is looking for a subscriber by executing T-SQL stored-procedure against the database, so you must have the context property inside the database tables.

Maybe in the next version we'll not have to manage artifacts in two places - till then, just be aware to deploy all the property schemas before running any pipeline component that uses them.

Wednesday, July 30, 2008

Expert Days Conference 2008

Expert Days Conference 2008 arranged by E4D-Solutions and Microsoft Israel. In this year, there's more then 20 .NET Framework Experts in a week of extreme Courses (All the sessions are in the Hebrew language).

I'm giving two sessions:

  • The far side of the moon:  How to use BizTalk and avoid known issues

this session will help advanced BizTalk developers and project managers to avoid 'mysterious' problems and will show how to develop a clean workarounds to BizTalk known issues into a successful BizTalk solution.

  • Advanced WCF integrated with BizTalk Adapter Pack

in this session, a deep dive into the WCF LOB adapter SDK (ASDK) for advanced .NET integration developers.

So, for all of you Israelis readers - hope to see you there.

Sunday, June 22, 2008

Processing soap envelope within BizTalk Server

BizTalk parsing engine supports envelope schemas. This means that if the incoming message is marked as envelope schema, the parsing engine will parse the message and will dispatch the body content of the envelope as the message body part.

The most common scenario is when BizTalk receives SOAP Envelop. It's little known that Microsoft supplies soap envelope XSD schema as part of BizTalk system schemas. The schema name is BTS.soap_envelope_1__1 and is deployed inside Microsoft.BizTalk.GlobalPropertySchemas.dll under BizTalk.System BTS application.

When schema is marked as Envelope (is_envelope="yes"), the engine is looking for the body_xpath annotation. This value is the actual data section and will be assigned as the message part. The value of body_xpath attribute is saved in dbo.bt_DocumentSpec table of BizTalkMgmtDb database. In the disassembling process the engine is extracting the value by using IDocumentSpec.GetBodyPath() under Microsoft.BizTalk.Component.Interop namespace.

Then the engine performing denormalize process by extracting message data from the body element. At the end of the process, BTS.MessageType context property is set to the message type of actual content of the soap:Body section.

How do I start an Orchestration with soap:envelope message type

Normally, Orchestration subscription includes value for BTS.MessageType context property, this mean you can't start orchestration with the soap envelope message type because the XmlDisassembler Component will discard the soap:Envelope content and the messaging engine will only get the soap:Body content – so at the end of the process, BTS.MessageType context property will be set to the actual message type of content under soap:Body section and the orchestration will never start with the type soap:Envelope.

In case you need to process the entire soap:Envelope inside orchestration:

  • Use PassThruReceive pipeline.
  • Define message of type BTS.soap_envelope_1__1 message inside the orchestration (Message Type > schemas > from referenced assembly > Microsoft.BizTalk.GlobalPropertySchemas).
  • Use Xml Document as the incoming message.
  • Assign the incoming Xml Document message to the BTS.soap_envelope_1__1 message.

Which custom pipeline component I need to develop for extracting the soap:Body section

If you need to receive message that is wrapped by a soap envelope and you're using the XmlDisassembler pipeline component - there's no need to develop further components for extracting the body element because the engine will automatically dispatch the soap:Body as the message part.

What about soap:Header and soap:Fault section

The automatic dispatch is only for the soap:Body section and the other section are discarded. so if you also need to process the soap:Header section you have to write a custom pipeline component for this task.

Saturday, June 21, 2008

Dependency in BizTalk Assembly

When you deploy BizTalk assembly you must ensure that any referenced assembly is also deployed. Actually, BizTalk is enforcing that by not letting you applying adding resource action without deploying all its references.

When removing resource BizTalk doesn't uninstalling the actual assembly from the GAC, so, any application would work regardless to missing registration of the assembly in BizTalk database.

On the other hand, BizTalk is forcing dependencies when trying to delete (or modify) any resource that has other depended artifact in BizTalk. This means that if one application uses pipeline component or map from referenced BizTalk Application, BizTalk explorer object model (Microsoft.BizTalk.ExplorerOM) will throw an exception when trying to remove the assembly containing the pipeline component or map as long you'll not reset the pipeline to the default passthrough pipeline components (or some pipeline from other deployed assembly) and remove the mapping from the port.

The very same behavior is happens when it come to BizTalk resources. Any attempt to remove assembly that referenced by other assembly inside any BizTalk application will throw exception by BizTalk ApplicationDeployment API's. (Microsoft.BizTalk.ApplicationDeployment)

How does BizTalk detects references assemblies

BizTalk is actually uses System.Reflection to detect references. Each System.Reflection.Assembly object has References member of type List that contains the fully-qualified name. Inside BizTalk any resource has Resource.Luid property, when deleting (or modifying) assembly ApplicationDeployment API's loads each resource as an System.Reflection.Assembly object and checks the References member for this Luid. If BizTalk finds equal Luid an exception will be thrown.

Because of this reason, updating infrastructure artifacts is quite headache. The Process must be in the following order:

  • Removing all referenced Applications.
  • Remove infrastructures old version.
  • Deploy infrastructures new version.
  • Deploy all high level referenced Application.

Where can I search for BizTalk assembly for checking its referenced Assemblies

There are three places which BizTalk assembly can be found for checking its referenced assemblies:

  • Local server where the assembly location specified by the sourceLocation property – this approach
  • Remote server where the sourceLocation property specifies assembly on the local machine - this is the most bad option because you must be sure the assembly is the very same as the one on the remote computer. Unfortunately this is the actual way BizTalk Administration Console is using – see below.
  • Local server on the GAC – this is the best option because it checks the actual running assembly.

How can I detect references in BizTalk assemblies

BizTalk 2006 MMC offers GUI for detecting references in BizTalk assemblies. Despite this, the MMC is running in a client mode, on a remote computer so, the GUI is not checking the remote machine GAC. As I wrote, this checking is Reflection based so; the BizTalk MMC must load the assembly into memory before being able to search for Luid equality – which is not possible from remote computer.

The way BizTalk MMC handles this issue is by using the property annotations list that each BizTalk resource contains:

Each Resource has Resource.Properties property of IDictionary<string,Object> type. the entry in this dictionary which points to the resource path is 'SourceLocation' so, BizTalk Administration console uses this entity for the parameters needed by System.Reflection.Assembly.Load method. By this, if the local machine contains the assembly on the same path as in the remote computer – BizTalk Administration console can load it into memory and search for references.

  

In the following, BizTalk administration console MMC was find the assembly in the path specified under the sourceLocation, so check for dependencies was preformed.

 

If the Administration Console can't find the assembly in the location specified in sourceLocation – the System.Reflection.Assembly.Load method failed and no further check is preformed.

In this insight, it would be much more advisable to run the check on the local server GAC. Unfortunately, BizTalk 2006 administration console is MMC snap-in and was designed to run on a remote computer so it does not support this approach.

The first step for extracting BizTalk Assembly from the GAC is by finding the path to the assmebly. this can be done by using the  GetAssemblyPath(string assemblyDisplayName) static method of the GAC class under Microsoft.BizTalk.Gac namespace. you can find this namespace in Microsoft.BizTalk.Deployment.dll

The second step is to load the assembly. this can be done by using Microsoft.BizTalk.Deployment.Assembly namespace. use AssemblyBase property of the BtsAssemblyManager class.

Here's the code snippet:

public List<string> CheckDependencies(string btsAssemblyLuid, string btsSqlServerName)
{
   List<string> dependeciesArr = new List<string>();
 
   string[] btsAssemblyProps = btsAssemblyLuid.Split(',');
   string btsAssemblyName = btsAssemblyProps[0];
   string btsAssemblyVersion = btsAssemblyProps[1].Remove(1, "Version=".Length).Trim();
   string btsAssemblyCulture = btsAssemblyProps[2].Remove(1, "Culture=".Length).Trim();
   string btsAssemblyKey = btsAssemblyProps[3].Remove(1, "PublicKeyToken=".Length).Trim();
 
  string btsAssemblyPath = Microsoft.BizTalk.Gac.Gac.GetAssemblyPath(btsAssemblyName);
  Microsoft.BizTalk.Deployment.Assembly.BtsAssemblyManager mgr = new Microsoft.BizTalk.Deployment.Assembly.BtsAssemblyManager(btsAssemblyPath, null);
 
  Microsoft.BizTalk.MetaDataOM.IAssemblyBase assembly = mgr.AssemblyBase;
 
   Microsoft.BizTalk.ApplicationDeployment.Group group = new Microsoft.BizTalk.ApplicationDeployment.Group();
   group.DBName = "BizTalkMgmtDb";
   group.DBServer = btsSqlServerName;
 
   foreach (Microsoft.BizTalk.ApplicationDeployment.Application app in group.Applications)
   {
       foreach (Microsoft.BizTalk.ApplicationDeployment.Resource res in app.ResourceCollection)
       {
           if (res.ResourceType.Equals("System.BizTalk:BizTalkAssembly") || res.ResourceType.Equals("System.BizTalk:Assembly"))
           {
               string[] props = res.Luid.Split(',');
               string name = props[0];
               string version = props[1].Remove(1, "Version=".Length).Trim();
               string culture = props[2].Remove(1, "Culture=".Length).Trim();
               string key = props[3].Remove(1, "PublicKeyToken=".Length).Trim();
 
               string path = Microsoft.BizTalk.Gac.Gac.GetAssemblyPath(name);
               Microsoft.BizTalk.Deployment.Assembly.BtsAssemblyManager manager = new Microsoft.BizTalk.Deployment.Assembly.BtsAssemblyManager(path, null);
               Array references = manager.AssemblyBase.References as Array;
 
               foreach (string refName in references)
               {
                   if (assembly.DisplayName.Equals(refName))
                   {
                       dependeciesArr.Add(app.Name + " : " + res.Luid);
                   }
               }
           }
       }
   }
 
   return dependeciesArr;
}
 

 
Copyright © 2007 | Diseñado por Blog and Web