Welcome to MSDN Blogs Sign in | Join | Help
In the past, I have outlined some of the most famous J-POP artists: EXILE or DREAM. Today, let me introduce the (now defunct) Japanese band SweetS (official web site in Japanese). Formed in 2003, the group experienced minor success before disbanding in 2006.
 
Avex Track, a famous production company in Japan held auditions and selected five girls out of their pool of potential talents. After about a year of training, SweetS was born. Sadly, they failed to meet the success they expected. One of their singles "Love like candy floss" (February 2004) best illustrate their style. The video below has all the caracteristics you would expect from J-POP productions: dance moves choregraphed within 1/16 of an inch, perfect synchronization and ... the never ending story of a girl in love with a boy.
 
 

With ASP.NET AJAX Extensions being baked into the .NET Framework 3.5 and the improvements to WCF to support JSON, it seems tempting to write WCF services and host the in Windows Sharepoint Services 3.0

Unfortunately, if you create a WCF service and drop it under a WSS controlled vroot like _layouts or _vti_bin, your service will fail to activate with the following message in the event log:

WebHost failed to process a request.
Exception: System.ArgumentException: virtualPath
at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result)

If you attach a debugger to w3wp.exe and enable break on first CLR change exceptions, the stack trace at the time of the failure will look like:

virtualPath 

at Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.IsExcludedPath(Stri­ng virtualPath) 
at Microsoft.SharePoint.ApplicationRuntime.SPVirtualPathProvider.FileExists(St­ring virtualPath) 
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceA­vailable(String normalizedVirtualPath) 
at System.ServiceModel.ServiceHostingEnvironment.EnsureServiceAvailableFast(St­ring relativeVirtualPath)
at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.HandleRequest()
at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest() 
at System.ServiceModel.Activation.HttpHandler.ProcessRequest(HttpContext context)

After debugging the issue, it turns out¹ that WSS own VirtualPathProvider (SPVirtualPathProvider, briefly described by this MSDN page) refuses to handle requests for which the virtual path starts with "~". 

Now that we understand the issue, a workaround¹ is easy to implement. There are three steps:

  1. Write our own VirtualPathProvider. It will detect if the request is made to a WCF service and if so, it will remove the leading "~" and hand off the request to WSS virtual path provider,
  2. Arrange for our new virtual path provider to be inserted before WSS provider so we get a crack at all requests before WSS does. This will be done using an ASP.NET HttpModule,
  3. Perform a few changes to web.config to insert our new module in the ASP.NET pipeline.

The following paragraphs describe these steps.

A custom VirtualPathProvider

VirtualPathProviders were introduced in ASP.NET 2.0. They are commly used by web applications which want to serve ASP.NET pages saved in other locations that the file system (a SQL table is the most canonical example).

All VirtualPathProviders are daisy chained by ASP.NET. This means that any custom VirtualPathProvider has a "Previous" VirtualPathProvider it can erly on to perform standard operations. For instance, Sharepoint's SPVirtualPathProvider relies on the default VirtualPathProvider to serve ASP.NET pages stored on the file system¹.

For us, we will essentially use the "Previous" VirtualPathProvider (which happens to be of type SPVirtualProvider) to implement our provider. We will pass most requests untouched. We will only look at requests ending with ".svc" and starting with "~". Such a request will be treated as a WCF service call and we will "patch" the virtual path (by removing the leading "~"). The patched virtual path will then be handed off to the previous virtual patch provider for further processing.

NOTE: This causes *all* requests starting with "~" and ending with ".svc" to be patched. If you are going to use this code for a production, you should also restrict this patching process to virtual directories where services can be installed (that is everything under _layouts or _vti_bin). Failure to do so has security implications.

Here is the code for our custom VirtualPathProvider:

public class WCFVirtualPathProvider : VirtualPathProvider {

     public override string CombineVirtualPaths(string basePath, string relativePath) {
         return Previous.CombineVirtualPaths(basePath, relativePath);
          }

     public override System.Runtime.Remoting.ObjRef CreateObjRef(Type requestedType) {
         return Previous.CreateObjRef(requestedType);
     }

     public override bool DirectoryExists(string virtualDir) {
         return Previous.DirectoryExists(virtualDir);
         }

     public override bool FileExists(string virtualPath) {                                 
        // Patches requests to WCF services: That is a virtual path ending with ".svc"     
        string patchedVirtualPath = virtualPath;                                            
        if (virtualPath.StartsWith("~", StringComparison.Ordinal) &&                       
         
virtualPath.EndsWith(".svc",    StringComparison
.InvariantCultureIgnoreCase))     
        {                                                                                  
          patchedVirtualPath
= virtualPath.Remove(0, 1);                                   
        }                                                                                   
        return Previous.FileExists(patchedVirtualPath);                                    
     }                                                                                     

     public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath,
                System
.Collections.IEnumerable
virtualPathDependencies, DateTime utcStart) {
        return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
     }

     public override string GetCacheKey(string virtualPath) {
        
return Previous.GetCacheKey(virtualPath);
    
}

     public override VirtualDirectory GetDirectory(string virtualDir) {
        return Previous.GetDirectory(virtualDir);
     }

     public override VirtualFile GetFile(string virtualPath) {
        return Previous.GetFile(virtualPath);
     }

     public override string GetFileHash(string virtualPath, System.Collections.IEnumerable virtualPathDependencies) {
              
return Previous.GetFileHash(virtualPath, virtualPathDependencies);
     }

     protected override void Initialize() {
        base.Initialize();
     }
}

Most method delegate work to the "Previous" virtual path. Virtual Paths appearing to be WCF service calls are patched.

Registering our VirtualPathProvider 

According to MSDN, VirtualPathProviders must be registered using HostingEnvironment.RegisterVirtualPathProvider. Moreover, VirtualPathProviders must be registered with ASP.NET before the first line of code is compiled. An HttpModule is the perfect place for us to register our provider. The module is trivial:

public class WCFPatchupModule : IHttpModule {
   
  
static bool virtualPathProviderInitialized = false;
  
static object virtualPathProviderInitializedSyncLock = new object();

   public void Dispose() {
  
}

   public void Init(HttpApplication context) {
     
if (!virtualPathProviderInitialized) {
        
lock (virtualPathProviderInitializedSyncLock) {
            
if (!virtualPathProviderInitialized) {
                   
WCFVirtualPathProvider vpathProvider
= new WCFVirtualPathProvider(); 
                    HostingEnvironment
.
RegisterVirtualPathProvider(vpathProvider);       

                    virtualPathProviderInitialized
= true;
             
}
        
}
     
}
  
}
}

Now, we need to compile the two classes above into a strongly named assembly and isntall it into the GAC.

Configuration changes

Assuming you have compiled the two classes above and signed the assembly, you are now ready to insert our HttpModule in the ASP.NET pipeline. This is easily done by changing the <httpModules> section of the sharepoint site as follows:

<httpModules>
<
clear />
<
add name="SPRequest" type="Microsoft.SharePoint.ApplicationRuntime.SPRequestModule, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
<
add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
<
add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
<
add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<
add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
<
add name="RoleManager" type="System.Web.Security.RoleManagerModule" />

<
add name="WcfVirtualPathProvider" type="WcfVirtualPathProvider.WCFPatchupModule,         
           WcfVirtualPathProvider, Version=1.0.0.0, Culture=neutral,                       
           PublicKeyToken=[... put your assembly public key token here ...]
"
/>             

<!--
<add name="Session" type="System.Web.SessionState.SessionStateModule"/> -->
</
httpModules>

Our HttpModule will run after Sharepoint's "SPRequest" module. Since SPVirtualProvider is registered by the "SPRequest" module at startup¹, we know our VirtualPathProvider will always come into  play *after* Sharepoint's SPVirtualPathProvider and therefore, we will get a chance to patch the request's virtual path before it reaches Sharepoint. Of course, you will need to substitute your assembly name, version and public key token. 

You are now ready to test: deposit an WCF service under _layouts and call it. You should now see your service getting activated and calls should now be sucessful.

Disclaimer - Read Me ! 

¹As with all statements of alleged fact, this statement is an interpretation of events based on my observation and thought and does not establish a statement of the official position of the Windows Sharepoint Services team or Microsoft Corporation. That interpretation may ultimately prove incorrect. Techniques and strategies described in this post should be considred "for entertainment only". This post is offered "as is".

My BizTalk Decompiler only works with Reflector 4. The updated version for Reflector 5 has been available for a while on CodePlex: http://www.codeplex.com/reflectoraddins

All date/time tracked by BAM must be expressed in UTC. BAM depends on this to provide accurate durations, among many other things. This is clearly explained here. It was also the case for BizTalk 2004 even if the documentation gave subtle hints instead of describing it:


es.UpdateActivity("PurchaseOrder", poid, "POShipped", DateTime.UtcNow);

This rule is unfortunately easy to forget. For instance, let's assume you have a message with a date/time field you want to track with BAM. You start the tracking profile editor and map the message field to the adequate activity element. It was so easy that you forgot the above rule: the date/time data in the message must always be expressed in UTC or you'll start to see the tracked date/time behave funny in the BAM Portal as well as durations based on this date/time.

If the field is not expressed in UTC, the right approach is to convert it to UTC before you hand it off to BAM. You can do this in a map (with a scripting functoid), an orchestration or in a custom pre-processor component.

Happy date/time tracking!

When using the BAM Portal Activity Search feature, you may be directed to an error page with the above error message.

This error message means that there are instances with duplicate ID in the activity you were searching. If you look at the "Application" event log on the computer which runs the BAM Web Services, you will find one entry from "BAM Web Service" comparable to the following:

EXCEPTION:
System.Web.Services.Protocols.SoapException: There are instances with duplicate ID 'PO_COMPLETED_100' in activity 'PurchaseOrder'. The duplicates must be removed from the database to fix this problem.

BAM will not operate properly if there are duplicate activity instance IDs. Internally, BAM uses these IDs to perform correlations and expects them to be unique. The database is considered corrupted when this situation occurs and this condition should be fixed as soon as possible.

Why does that happen?

You installed one of the BAM sample or tutorial and caused BizTalk to process the same message more than once. Sample messages have hardcoded IDs, so if you process the same message twice, you'll end up with two activity instances with the same ID. 

This can also happen if BAM was asked to insert data with duplicate activity instance IDs through the BAM APIs such as DirectEventStream or BufferedEventStream.

You may also extract activity IDs from incoming messages using a tracking profile and two messages came with the same ID.

For BAM, it is critical you ensure all activity instasnce IDs are unique.

So why does BAM allow me to insert duplicate IDs?

If BAM cannot operate with duplicate activity instance IDs, why doesn't BAM refuse to insert them in the first place? Well, this is for performance reasons. Internally, BAM splits the data into partitions to make insertions faster. Checking for the uniqueness of a key would require to lock multiple tables in a transaction and scan for this ID into multiple partitions / tables. This would dramatically slow BAM down.

Tiho has a good entry on why it is that way and how to fix it. He also explains how to diagnose the problem.

This is an addin for Reflector that allows you to list all BizTalk artifacts contained in an assembly and extract them.

Installation

Donwload the attached file and extract Reflector.BizTalkDecompiler.dll into the same directory as Reflector (otherwise it won't work). Go to View|Add-Ins in Reflector, and Add Reflector.BizTalkDecompiler.dll.

Usage

Using File|Open..., add the BizTalk assembly you'll like to decompile to Reflector's assemblies list. Right click on the BizTalk assembly and select "BizTalk Server 2006 Artifacts".

You should see a list of all artifacts, with their type, name and namespace they were compiled into. Click the "Decompile..." button to decompile all selected artifacts into source files. You can select / de-select artifacts by using the selection check box on the right.

In the decompile dialog, specify the path to the directory where files should be created and press "Decompile". The decompile dialog is multi-threaded for a more responsive user interface.

Screenshot

Limitations

[Update June 19, 2006 - 10:00] This addin was compiled for the .NET Fraework 2.0 and Reflector 4.2 (and above). It will not work with the .NET Framework 1.1: you will get an error comparable to:

System.BadImageFormatException: The format of the file 'Reflector.BizTalkDecompiler.dll' is invalid.
File name: "Reflector.BizTalkDecompiler.dll"
   at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Boolean isStringized, Evidence assemblySecurity, Boolean throwOnFileNotFound, Assembly locationHint, StackCrawlMark& stackMark)
   at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Boolean stringized, Evidence assemblySecurity, StackCrawlMark& stackMark)
   at System.Reflection.Assembly.LoadFrom(String assemblyFile, Evidence securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm)
   at ᜧ.ᜀ(String A_0)

 I am currently investigating a port to .NET 1.1 but I do not promise I'll do it. The addin was designed for BizTalk Server 2006 which requires the .NET Framework 2.0 so you should have any problem.

Most BizTalk artifacts can be decompiled back to their original source form (Pipelines, Schemas). Some artifacts cannot be decompiled to their original source (Maps). When decompiling a map, the addin will export the XSLT which was generated by the compiler, not the actual .btm file.

[Update June 9, 2006 - 13:00] Recovering the full source of orchestrations is possible but invovles a manual step. After exporting, open the .odx file in the BizTalk Orchestration Designer (in Visual Studio) and save it without making any change. The editor will rebuild missing information which cannot be infered from the compiled assembly. Once this is done, the resulting .odx file should behave as if it was the source of the orchestration.

Feedback welcome!

[Update June 9, 2006 - 12:20] This tool is handy but I'd like to make sure everyone understands that is is offered "AS IS" with no warranty. I did not anticpate so much interest in the tool so I had to append this legal disclaimer.

This being said, if you found a bug, want a new feature or just want to say that you are using BizTalk decompiler and you love it, feel free to use the "Email" link above to drop me a note!

The BizTalk Server 2006 Best Practices Analyzer automatically examines a BizTalk Server 2006 deployment and generate a list of issues pertaining to best practices standards for BizTalk Server deployments. The primary use of this tool is to examine BizTalk Server production and staging environments, though it will still be useful on Dev machines.

You can donwload it at http://www.microsoft.com/downloads/details.aspx?FamilyId=DDA047E3-408E-48BA-83F9-F397226CD6D4&displaylang=en.

If you have installed BAM (BizTalk Server 2004 or BizTalk Server 2006), you may have noticed that a web service called the "BAM Query Service" got installed.

As its name hints, this web service allows the BAM Portal to retrieve BAM instance data without connecting directly to a SQL server.

This web service is intended to be called by the BAM Portal only. Therefore, it is not documented and Microsoft will not support code which makes direct calls to this web service.

A long time ago, when we designed BAM for BizTalk Server 2004, we knew we wanted to get data without connecting to SQL server directly. Unfortunately, we did not have the time to provide a robust, well designed, general purpose set of APIs to query BAM instance data so we settled for a private API which served our immediate needs. It was believed that we would have time in a next release to think about this again and perhaps expose well designed APIs for BAM instance data retrieval. Alas, it was on the list but did not make the cut for BizTalk 2006.

The supported way of retrieving BAM instance data is documented on MSDN (link updated 09/18/2007). Essentially, you need to connect to the SQL server hosting BAM and query the following view (available within the BAM Primary Import Database):

bam_<ViewName>_<ActivityName>_View

Today, Microsoft announced that Virtual Server 2005 R2 can be downloaded free of charges.

BizTalk Server 2006 works great under Virtual Server 2005 R2. When we were developing BizTalk Server 2006, I ran all tests under Virtual Server and never saw any problem.

One caveat: make sure you have plenty of physical memory (I use 2Gb), give your guest operating system at least 1600Mb. Do not remember to install the Virtual Machine additions as soon as possible - this is critical to get Windows Server 2003 (and SP1) run at full speed.

In a recent entry, Jeff noticed my Debugger Visualizer for MessageContext and mentioned that I am from MSFT Japan. Well, this is not true. I am in Redmond, USA and I work for the BizTalk team. It it true that I do like Japan and I have visited this gret country numerous times. Every once in a while, you'll find Japan related posts here.

Jeff, I'm happy you like the visualizer!

You probably know this already. You can find an overview of BizTalk Server 2006 features here.

Now that BizTalk 2006 is released, I can speak a little more freely about the product. You can use the "Contact" link to suggest topics you'd like me to shed some light on. I do not promise I'll be able to answer every single question, but I'll do my best.

Visit it here. Kris, Steve, Michael and Mark share product updates, links to training materials and even employment opportunities!

I subscribed to their RSS feed.

Visual Studio 2005 added support for Debugger Visualizers. With debugger visualizers, developers can define what information (and in what form) is shown in the debugger for a specific type. There are many visualizers for various .NET types floating around. Here is a small list of the most popular ones: ASP.NET Cache, Regular Expression, XmlDocument (and other Xml related types), a powerful looking DataSet visualizer and there is even a WindowsIdentity visualizer.

I am not going to explain in details how to write your own visualizer. You can find step by step instructions on MSDN. Most existing debugger visualizers I mentionned in the previous paragraph also have home pages where you will find details on how to write your own or even source code.

I'd like to share some tricks you might find useful when writing your own visualizers:

  • You can write visualizers for public (obvious!) or internal or private types. The trick for non public types is to use the Assembly Qualified Name of the type you want to visualize. For instance, BizTalk 2006's MessageContext property is actually an internal type (see purple type name below):

[assembly: DebuggerVisualizer(typeof(<type of visualizer> ), typeof(<type of visualizer source>), TargetTypeName = "Microsoft.BizTalk.Message.Interop.MessageContext_ManagedViewOfNative, Microsoft.BizTalk.Pipeline, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", Description = "<description>")]

  • Your visualizer code runs non JIT'ed. So make sure you do not perform too much work or it will be slow. If you do too much, the expression evaluator will time out and interrupt your visualizer with an "Expression Evaluation timed out exception".
  • Use DataGrid instead of DataGridView. I know I used a DataGridView in my visualizer, but the DataGrid is faster, especially when running non JIT'ed.

To illustrate these points, I wrote a BizTalk 2006 MessageContext debugger visualiser. You can download the complete solution BTSVisualizers.zip below. It works with Visual Studio 2005 and BizTalk 2006 only.

Load the visualizer solution in Visual Studio and build the release comfiguration.To install the MessageContext visualizer, shut down all instances of Visual Studio and copy BTSVisualizers.dll to %VS8ROOT%\Common7\Packages\Debugger\Visualizers. (If you installed Visual Studio at the default location, that would be C:\Program Files\Microsoft Visual Studio 8\Common7\Packages\Debugger\Visualizers) That's it!

The next time you debug a pipeline (by attaching to BTSNTSVC.exe), you can hover the mouse on the "Context" variable of the message and see all entries in the message context:

Unfortunately, this visualizer will not display anything useful unless you are debugging a BizTalk artifact by attaching to BTSNTSVC.exe. This is because most debugging tools (like pipeline.exe) do not populate the message context.

In a previous post, I described the home page of the new BAM Portal. Today, let's take a look at the Instance Search feature. The instance search allows you to search BAM data for a particular activity instance:

BAM Portal Activity Search

You can add / remove clauses using the "Add" / "Remove" buttons. You can also build a query and save it locally as an XML file for future use. Opening a query is done by clicking the "Open" button. Let take the Search for a test drive, shall we?

BAM Portal Search with results

After selecting the business data you would like to see in the column chooser, I am looking for all purchase orders with an amount of $500. There is only one purchase order matching in my system. I can see the details of this activity by clickin on the row of interest:

BAM Portal Activity Details

Users can see all business data associated with this activity instance. Milestones appear sorted from the oldest one to the newest one. Any related document or related activity would appear in their respective sections.

Finally, the "Technical Assistance" can be used to open a technical assistance ticket. The subject / description entered below will be posted to the event log of the server, along with references to engine artifacts (messages / orchestrations) related to this activity.

BAM Portal Technical Assistance

Tomorow, I'll show how one can view aggregated data and create alerts when specific condition happen.

More Posts Next page »
 
Page view tracker