Fix port for development server in VS2008 web projects

From time to time you need to define a fixed port for web projects during development, an example may be a need to exercise services through a proxy for testing.  This can be fixed in the SLN file to ensure that for all solution users the port is fixed.

Find the project in the SLN, and VWDPort setting will be defined, set the required port and add VWDDynamicPort = "false" setting below.  once reloaded the solution will always use the chosen port.

Policy injection and logging with the Enterprise Library

There are plenty of articles out there that cover use of the logging block with enterprise library, try here and here. For method invoke logging most end up with attribute markers for logging like the LogCallHandler. Whilst this removes most of the burdensome log code within methods - it still looks ugly IMHO, and requires compilation to change log output. For example the following basic class:

    public class Foo: MarshalByRefObject
    {
        [LogCallHandler(IncludeParameters = true, 
            Categories = new[] { "General" },
            LogAfterCall = true, LogBeforeCall = true, 
            Priority = 4, Severity = TraceEventType.Verbose)]
        public void Bar(string param)
        {
            Console.WriteLine(param);
        }
    }

With configuration:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </configSections>
  <loggingConfiguration name="Logging Application Block" tracingEnabled="true"
    defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
    <listeners>
      <add fileName="trace.log" header="----------------------------------------"
        footer="----------------------------------------" formatter="Text Formatter"
        listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        traceOutputOptions="None" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        name="FlatFile TraceListener" />
    </listeners>
    <formatters>
      <add template="Timestamp: {timestamp}&#xD;&#xA;Type Name: {property(TypeName)}&#xD;&#xA;Method Name: {property(MethodName)}&#xD;&#xA;Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}"
        type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        name="Text Formatter" />
    </formatters>
    <categorySources>
      <add switchValue="All" name="General">
        <listeners>
          <add name="FlatFile TraceListener" />
        </listeners>
      </add>
    </categorySources>
    <specialSources>
      <allEvents switchValue="All" name="All Events" />
      <notProcessed switchValue="All" name="Unprocessed Category" />
      <errors switchValue="All" name="Logging Errors &amp; Warnings" />
    </specialSources>
  </loggingConfiguration>
</configuration>

gives log output:

----------------------------------------
Timestamp: 10/09/2008 12:11:40
Type Name: EntLibLogging.Foo
Method Name: Bar
Extended Properties: param - log test

----------------------------------------
----------------------------------------
Timestamp: 10/09/2008 12:11:40
Type Name: EntLibLogging.Foo
Method Name: Bar
Extended Properties: param - log test

----------------------------------------

When called using:

            Foo f = PolicyInjection.Create<Foo>();
            f.Bar("log test");

Items to note are the change in the text formatter template to use the {property()} syntax to pick up the extended properties as defined in this MSDN article.

With a pretty simple change in Config the LogCallHandler attribute can be removed from the code and logging become more flexible.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </configSections>
  <policyInjection>
    <policies>
      <add name="Policy">
        <matchingRules>
          <add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.MemberNameMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
            name="Member Name Matching Rule">
            <matches>
              <add match="Bar" ignoreCase="false" />
            </matches>
          </add>
        </matchingRules>
        <handlers>
          <add logBehavior="BeforeAndAfter" beforeMessage="Before" afterMessage="After"
            eventId="0" includeParameterValues="true" includeCallStack="false"
            includeCallTime="true" priority="-1" severity="Information"
            type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.LogCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
            name="Logging Handler">
            <categories>
              <add name="General" />
            </categories>
          </add>
        </handlers>
      </add>
    </policies>
  </policyInjection>
  <loggingConfiguration name="Logging Application Block" tracingEnabled="true"
    defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
    <listeners>
      <add fileName="trace.log" header="----------------------------------------"
        footer="----------------------------------------" formatter="Text Formatter"
        listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        traceOutputOptions="None" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        name="FlatFile TraceListener" />
    </listeners>
    <formatters>
      <add template="Timestamp: {timestamp}&#xD;&#xA;Type Name: {property(TypeName)}&#xD;&#xA;Method Name: {property(MethodName)}&#xD;&#xA;Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}"
        type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
        name="Text Formatter" />
    </formatters>
    <categorySources>
      <add switchValue="All" name="General">
        <listeners>
          <add name="FlatFile TraceListener" />
        </listeners>
      </add>
    </categorySources>
    <specialSources>
      <allEvents switchValue="All" name="All Events" />
      <notProcessed switchValue="All" name="Unprocessed Category" />
      <errors switchValue="All" name="Logging Errors &amp; Warnings" />
    </specialSources>
  </loggingConfiguration>
</configuration>

note the introduction of the policy injection with a specific match for the method name "Bar". 

Once you have taken the step of inheriting from MarshallByRef and instantiating using PolicyInjection to allow policy based logging, stepping onto the config based approach gives a very flexible logging capability.

 

SOA Suite 10.1.3 install issue

There is a known bug with the install of the Oracle SOA Suite on Windows Server 2003 whereby the service required to run the Oracle Application Server is not installed (bug note 423366.1). 

To fix the problem you can create the service yourself using sc.exe.

sc create Oracle<Home Name>ProcessManager binPath= "<Home Path>\opmn\bin\opmn.exe -S " DisplayName= Oracleoracleas1ProcessManager

Replace <Home Name> with the name of the Oracle Home (where the application server is installed which can be found in the registry HKLM/SOFTWARE/ORACLE/Key/ORACLE_HOME_NAME), and <Home Path> with application server the install path.  Note that the <Home Name> is case sensitive, also note the space after the binPath and DisplayName options - these are important!

This will create a service named "Oracle<Home Name>ProcessManager" which you can configure to auto start.

Using UDDI v3 from C#

Microsoft support for UDDI seems to have disappeared of late - just try tracking down anything on MSDN and you will see what I mean from all the 404's and mis-leading information you will stumble across.

UDDI is a really useful part of any SOA, and the Oracle Service Registry contained within the Oracle SOA suite (version 10.1.3) supports version 1, 2 and 3 of the specification (by the way installation of the Oracle Service Registry is not the easiest thing you will ever do).  The easiest version of the spec to implement in .NET is the version 3.

Firstly download the WSDl and supporting schema from http://uddi.xml.org.

http://uddi.org/schema/uddi_v3.xsd
http://uddi.org/wsdl/uddi_api_v3_binding.wsdl
http://uddi.org/wsdl/uddi_api_v3_portType.wsdl

download additional supporting schema from http://www.w3.org

http://www.w3.org/2001/xml.xsd
http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd

saving to the same location.

Technorati Tags: ,

Edit xmldsig-core-schema.xsd and comment out the doctype definition - as this declaration makes svcutil barf as described in http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3065329&SiteID=1).

Generate the service proxy using svcutil, making sure to reference all required WSDL and schema.

svcutil /t:code /out:svcuti.cs uddi_api_v3_binding.wsdl uddi_api_v3_portType.wsdl uddi_v3.xsd xml.xsd xmldsig-core-schema.xsd

You will need to amend the svcutil generated config to include the endpoint for the UDDI service (just add the address attribute to the endpoint config). Then you can use the proxy to access the registry.

UDDI_Inquiry_PortTypeClient proxy = new UDDI_Inquiry_PortTypeClient("UDDI_Inquiry_SoapBinding_UDDI_Inquiry_PortType");
 
find_binding fb = new find_binding();
fb.serviceKey = "uddi:systinet.com:uddi:service:v3_inquiry";
 
bindingDetail detail = proxy.find_binding(fb);
foreach (bindingTemplate template in detail.bindingTemplate)
{
   accessPoint ap = template.Item as accessPoint;
   Console.WriteLine(ap.Value);
}

The sample code defines a simple search based on the defined key of a registered service (in this case "uddi:systinet.com:uddi:service:v3_inquiry" which is a key of the UDDI inquiry service as registered in the sample data from the Oracle Service Registry install). The code then enumerates the endpoint address for each registered endpoint listed within the returned binding detail.

For more information on the use of UDDI API read http://www.uddi.org/pubs/uddi_v3.htm

DateTimePicker with nullable DateTime...

More work with nullable DateTimes!

There are oodles of articles out there discussing data binding with the DateTimePicker control attempting to address its limitations for support of nullable types.  Of course you can define your own control, or override the built in to amend the behaviour, but I wanted to see if I could use some of the .NET syntactic sugar to provide a solution that had light impact.

The idea was based on this article http://windowsclient.net/blogs/faqs/archive/2006/05/18/what-is-the-proper-way-to-bind-nullable-datetime-property-to-datetimepicker.aspx, which was obviously posted during the .NET 2 beta as it still uses INullableValue in the implementation.  The basic of this article is to use the Format and Parse events of the Binding to deal with null using the check box.  This is pretty light touch, so I thought I would wrap this in an extension to the DateTimePicker itself.  This is the extension class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
 
namespace manik.Windows.Forms
{
    public static class NullableDateTimePickerExtension
    {
        public static void AddNullableBinding(this DateTimePicker picker, Binding binding)
        {
            picker.DataBindings.Add(binding);
 
            binding.Format += new ConvertEventHandler(picker.OnFormatNullableDateTimePicker);
            binding.Parse += new ConvertEventHandler(picker.OnParseNullableDateTimePicker);
        }
 
        public static void OnParseNullableDateTimePicker(this DateTimePicker picker, object sender, ConvertEventArgs e)
        {
            Binding binding = sender as Binding;
 
            if (binding != null)
            {
                DateTimePicker dtp = (binding.Control as DateTimePicker);
 
                if ((dtp != null) && (!dtp.Checked))
                    e.Value = new Nullable<DateTime>();
            }
        }
 
        public static void OnFormatNullableDateTimePicker(this DateTimePicker picker, object sender, ConvertEventArgs e)
        {
            if (e.Value == null)
            {
                Binding binding = sender as Binding;
 
                if (binding != null)
                {
                    DateTimePicker dtp = (binding.Control as DateTimePicker);
 
                    if (dtp != null)
                    {
                        dtp.Checked = false;
                        e.Value = dtp.Value;
                    }
                }
            }
        }
    }
}

The AddNullableBinding extension will take a Binding object and hook up the Parse and Format events to further extension methods on the DateTimePicker.  Usage is really simple:

Binding testDateBinding = new Binding("Value", m_dataObject, "TestDate", true);
dtTestDate.AddNullableBinding(testDateBinding);

Just create a Binding for the date object, then call the extension method (making sure you have the correct using available of course) AddNullableBinding and that's it.  Light impact...

Nullable DateTime in PropertyGrid...

Now a lot of people hate it, but I think the PropertyGrid is cool - excellent for quick generation of edit UI.  We have shown it to customers as part of prototype build, and they have asked for its inclusion!  I was using the PropertyGrid to define a really simple editor for some data types and was working really well until I made the DateTime properties Nullable.

public virtual DateTime? EndDate
{
    get { return m_endDate; }
    set { m_endDate = value; }
}

The PropertyGrid was not displaying the editor, bit of a bummer! Although not a massive deal, I thought I was going to need to create a UITypeEditor that would cope with Nullable dates.  Fortunately if you are explicit, the built in editor supports a Nullable DateTime. 

[EditorAttribute(typeof(DateTimeEditor), typeof(UITypeEditor))]
public virtual DateTime? EndDate
{
    get { return m_endDate; }
    set { m_endDate = value; }
}

Adding the EditorAttribute specifying the DateTimeEditor to the property forces the use of the built in editor for the Nullable type.

Post build event weirdness...

After checking out a Visual Studio 2008 solution from our Subversion repository to a new machine (and new physical location) I started getting failure on the post build event reporting an exit code of 9009.  As ever it was building fin on another machine (luckily for once this failure wasn't on the continuous integration server).

With an exception like that It would have been easy to go off on one!  This project had some post build processing running in batch file, including RegAsm!  Looking at the simple things first, it had been checked out to a location with long file names such that the batch file was not running correctly.

	$(ProjectDir)\Install\Install.cmd  

Just wrapping the command in quotes sorted it. 

	"$(ProjectDir)\Install\Install.cmd"  

Ridiculously simple I know, but I thought I would write this one up - the way this was reported you could easily burn an hour or so chasing your tail...

Debugging the XmlSerializer

So we all love the XmlSerializer right! Probably a lot more than the DataContractSerializer, but then we have had more time to get used to it's ways.  Most of the time we don't need to care what's going on, but sometimes (and usually when we are forced into doing some custom serialization) it doesn't do exactly what we expected and after the allowable "couple of minutes" of trial and error (or is that just me) we need to step through the serialization code to see what's up.

First thing needed is to add diagnostics switches on XmlSerializer in the applications config file:

   1: <system.diagnostics>
   2:       <switches>
   3:          <add name="XmlSerialization.Compilation" value="1" />
   4:       </switches>
   5: </system.diagnostics>

Then compile and run the application in debug to the point immediately after the XmlSerializer in constructed for the type:

   1: XmlSerializer ser = new XmlSerializer(typeof(MyType));

The dynamic assembly and c# source is created in c:\documents and settings\[user]\local settings\temp under XP or c:\users\[user]\appdata\local under Vista - open the latest created *.cs file in your debug visual studio instance and off you go!

Printing to MODI under Vista

Not sure how, but somehow I lost the ability to print to Microsoft Office Document Image Writer (MODI) delivered with Office 2003 under Vista - I was getting pretty ugly exceptions when trying and as ever needed to get it sorted 'cos I needed it quick!  The quickest way to resolve was to re-install MODI, which wasn't that painful:

  • Close all running applications - if you have any Office application running, the driver will not be installed properly
  • Go to the "Control Panel" and "Programs and Features"
  • Click on"Microsoft Office 2003" and then click on "Change"
  • Check "Add or Remove Features" and click "Next"
  • On the bottom of the page check "Choose advanced customization of applications." and click "Next"
  • Under Microsoft Office click on the + sign in front of "Office Tools"
  • Locate "Microsoft Office Document Imaging", click the icon and select "Not Available"
  • Click on "Update" - this will uninstall the broken MODI driver
  • Repeat steps to get back to the point of expanding "Office Tools"
  • Locate "Microsoft Office Document Imaging", click the icon and select "Run from My Computer"
  • Click on "Update" - this will re-install the driver

Not sure how this got broken - I have installed a bunch of software since I last used it...

Subversion with Windows authentication on Server 2003

Subversion is a powerful source code control repository, but getting it to authenticate using Windows users can be a mite tricky...

In this setup the aim is to have an Apache served repository allow author details to be recorded in the subversion repository (predominantly to allow you to find out who to blame) - so the setup will be simply to allow all users read access to the repository and ensure that domain authenticated users only are allowed to write to the repository.

First step is to install the Subversion repository and get it running with Apache; I find the CollabNet package really well put together (v 1.4.6 is what is used in this setup and can be obtained from http://downloads.open.collab.net/collabnet-subversion.html).  The CollabNet install is very simple just be sure to choose Apache, and make sure the repository is working unauthenticated before moving on.

Once its all running you need to download the mod_auth_sspi Apache plug in, there are various builds available, but the one I have found to be reliable is the sourceforge project available from http://sourceforge.net/projects/mod-auth-sspi.  You need to make sure you use the correct plug in version for the version of Apache, version 1.04 from the mod_auth_sspi-1.0.4-2.0.58.zip drop works with the v1.4.6 CollabNet package.  From the downloaded zip copy the mod_auth_sspi.so file into the httpd\modules in the CollabNet install directory (default is c:\program files\CollabNet Subversion Server\httpd\modules).  Then you need to edit the httpd.conf file in the CollabNet install directory http\conf (default c:\program files\CollabNet Subversion Server\httpd\conf):

Add

	LoadModule sspi_auth_module modules/mod_auth_sspi.so

after the mod_auth.so load:

	LoadModule auth_module modules/mod_auth.so

then add

	LoadModule authz_svn_module modules/mod_authz_svn.so

 

after the mod_dav_svn.so load:

 

	LoadModule dav_svn_module modules/mod_dav_svn.so

Finally add

	
	# authentication	
	AuthName "Subversion Authentication"	
	AuthType SSPI	SSPIAuth On	
	SSPIAuthoritative On	
	SSPIDomain DOMAIN.COMPANY.COM	
	SSPIOfferBasic On	
	SSPIOmitDomain On 	
	SSPIUsernameCase upper	
	<LimitExcept GET PROPFIND OPTIONS REPORT>		
		Require valid-user	
	</LimitExcept>

within the <Location> tag at the end of the file following the location defaults

	DAV svn	SVNParentPath C:/svn_repository

The settings are in the main self explanatory, AuthType SSPI is fairly obvious as are SSPIAuth and SSPIAuthoritative (turning it on).  The SSPIDomain define the domain to authenticate against, and must be the full domain name.  The SSPIOfferBasic, SSPIOmitDomain and SSPIUsernameCase settings ensure the user is correctly prompted for authentication when requesting an operation other than the basic read ones listed in LimitExcept. 

There are ways to finely control access rights at all levels of the repository - but frankly I find the simplest configuration of recording the user who made the change rather than locking down easiest to manage in the long run.

After making the config changes restart the Apache server in the services.msc control panel plug in.  If it starts then you have done everything and just need to make sure that authentication details are recorded when you make repository changes (if you are using TortoiseSVN don't try this test using the repository browser to create folders it just doesn't request the authentication!).  If the service doesn't start then you have to start trial and error!  My recommendation would be to add the config items piece by piece in the order listed above checking the Apache logs as you go.  The authentication block is usually the tricky bit, again commenting out the salient bits (like SSPIDomain and LimitExcept) until the service starts.

Once you are at this point you can start additional configuration as required to more finely control access to the repository - however now is when I normally leave it...