# Tuesday, 17 October 2006

After some minor setbacks, I’m finally getting going with ADAM, and amazed at how well it works (once you figure out how it’s supposed to work, which is a non-trivial undertaking).  One thing that makes it harder is that most of the (still scant) documentation around ADAM is geared toward IT Pros, not developers, so there are plenty of places where you track down the documentation for that critical change you need to make to your directory, only to be told “start the command line tool…”.  Frustrating, but workable. 

Once the magic incantations are prized forth, it seems very fast.  Granted we’re not doing anything tricky at this point, but adding users, setting passwords, simple queries, all seem pretty snappy. 

On my previous problems with ADAM and AzMan integration, this is apparently caused by the fact that I had my users in one application partition in ADAM, and my AzMan store in another partition in the same ADAM instance.  The official word is that this is not supported, which probably makes sense.  It means that I’ll probably end up using a separate ADAM instance as the AzMan store, or fall back to the XML file, although that’s harder to distribute.  Only time will tell.

Tuesday, 17 October 2006 10:32:08 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 

Over the weekend I took my kids for a hike up the Deschutes river from where it meets the Columbia, just east of the Dalles.  The weather was pretty nice, although it was overcast most of the day.  At least the rain held off until the middle of the night.  Check out pictures and a brief description at portlandhikers.com

This was the longest hike I’ve ever done with my kids, and as we’ve progressed toward longer hikes over the summer, I’ve learned some valuable lessons about hiking with children and how to make the trip more enjoyable for everyone.  I got a lot of great tips from Extreme Kids: How to Connect With Your Children Through Today's Extreme (and Not So Extreme) Outdoor Sports.  It’s a very well written book, that starts with some general tips about going outdoors with children, and then has some sport-specific information in the second half.  The tips I’ve gotten the most out of so far:

  • dress them for the part.  Hiking-specific gear like hydration packs, boots, and trekking poles make them feel like they are participating in something special, and really help get them out on the trail.
  • talk up the hike.  Take some time to talk up the hike.  Make it sound hard, question their ability to handle such a difficult task (not too seriously) and make it into a challenge.  This has made a huge difference.  My kids both boogied right up Little Belknap Crater after I played up the difficulty of “scaling a volcano”. 
  • keep them fed.  Keeping their blood sugar up is vital.  I’ve started packing not just granola/Clif bars, but some smaller snacks to keep them sugared up.  Generally we avoid giving them sugar, so this one took me a while to warm up to, but on last weekend’s 7.8 miler, it made a big difference.  They were tired, but the never crashed.  The new Jelly Belly “Sports Beans” work great for this.  They are basically jelly beans with electrolytes in them (like Gatorade) that come in 100 calorie packs.  The kids love them, and the feel like they are getting away with something. :-)  We also tried some Clif Shot Bloks, which proved popular.  They come in packs of 6, and were easy to dole out at key milestones.

These tips (and more from the book) have made our time together outside much more enjoyable.  I’m already looking forward to next season (and maybe some snowshoeing over the winter).

Tuesday, 17 October 2006 10:18:17 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, 11 October 2006

I'm here in Redmond at the patterns and practices Summit, and yesterday Don Smith had some very interesting things to say about versioning of web services.  He was polling the audience for opinions and scenarios, and I started to realize that I hadn't thought very hard about the problem.  Typically in cases where I've wanted to version a web services, I've left the existing endpoint in place (supporting the old interface) and added a new endpoint which supports the new interface.  I still think that's a pretty good solution.  What I realized, though, was that there's no way to communicate the information about the new interface programmatically.  It still pretty much requires a phone call. 

Don suggested something like RSS (or actually RSS) to push information about new versions out to clients.  Another suggestion was to have people actively "subscribe" to your service.  That would not only handle the "push" notification, but you'd know how many people were actually using your old vs. new interface, etc. 

I started thinking about something like a WS-VersioningPolicy.  We already have standards like WS-SecurityPolicy, which carry metadata about how you should secure your use of a given service.  Why not use a similar formal, programmatic method for distributing information about versioning policy.  You could convey information like methods that are deprecated, where to find the new versioned endpoint (if that's your strategy), or define up front policies about how you will version your interface without switching to a new endpoint. 

That still doesn't solve the "push" problem.  Clients would have to not only consume the policy file at the time they start using the service, but presumably they'd have to check it from time to time.  That suggests human intervention at some level.  Hmmm. 

This is a hard problem, but one that Don's pretty passionate about solving, so check out his blog for future developments in this space.

Wednesday, 11 October 2006 09:12:47 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, 06 October 2006

I'd been having some trouble (read: beating my head against the wall) trying to get passwords set/changed for users in ADAM early this week.  Unfortunately, many of the available samples are geared toward IT rather than development users, so tend to be in script, etc.  I've been working from the excellent book "The .NET Developer's Guide to Directory Services Programming" by Joe Kaplan and Ryan Dunn.  Sadly, I couldn't get their password changing samples to work, although aside from that the information in the book has been invaluable. 

I finally found what I needed in an obscure section of the documentation after many a googling.  This article gave me the key info that made this work.  I haven't tried all the permutations, but I think the key piece I was missing was the right combination of AuthenticationTypes flags

 

            // Set authentication flags.

            // For non-secure connection, use LDAP port and

            //  ADS_USE_SIGNING |

            //  ADS_USE_SEALING |

            //  ADS_SECURE_AUTHENTICATION

            // For secure connection, use SSL port and

            //  ADS_USE_SSL | ADS_SECURE_AUTHENTICATION

            AuthTypes = AuthenticationTypes.Signing |

                AuthenticationTypes.Sealing |

                AuthenticationTypes.Secure;

 

            // Bind to user object using LDAP port.

            try

            {

                objUser = new DirectoryEntry(

                    strPath, null, null, AuthTypes);

                objUser.RefreshCache();

            }

            catch (Exception e)

            {

                Console.WriteLine("Error:   Bind failed.");

                Console.WriteLine("        {0}.", e.Message);

                return;

            }

This sample worked just fine, and I was finally able to set or change passwords for ADAM users.  Once that's done, authenticating that user is as easy as

            DirectoryEntry de = new DirectoryEntry("LDAP://localhost:50000/RootDSE",

                "test.user",

                "passwordSuppliedByUser",

                AuthenticationTypes.None);

            de.RefreshCache();

If the supplied password is correct, it succeeds, else you get an exception. 

Now I can get on with the interesting parts...

Friday, 06 October 2006 10:42:25 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  | 
# Thursday, 05 October 2006

I’m a relative newcomer to the world of alt country, so everybody but me probably already knew, but Neko Case #@*! ROCKS!  She’s got an amazing voice, and really captures that “torch and twang” aesthetic (which I love).  I’ve listened to Furnace Room Lullaby about 5 times in the last couple days, and I’m wowed each and every time.  I’d put it right up there with k.d. lang’s Absolute Torch and Twang.  This is what I love about Rhapsody.  I’d never hear all this great music otherwise. 

My other big faves right now are Blue Horse by the Be Good Tanyas, and Springtime Can Kill You by Jollie Holland.  Good stuff.

Thursday, 05 October 2006 09:55:18 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, 04 October 2006

I wanted to check out some CardSpace stuff today, so I bit the bullet and installed IE7 RC1.  In the short space of time I played with it, it looks pretty good.  However, as soon as it was installed, my Rhapsody client started crashing on startup.  Bummer.  The Rhapsody client hosts IE to do its “Music Guide” stuff, so I wasn’t really surprised that it didn’t handle the change well.  I’m pretty addicted to Rhapsody at work, since I can listen to just about anything I might want to. (Today it’s been a bunch of alt country stuff, Neko Case, The Sadies, Whiskeytown, Be Good Tanyas…yesterday was largely a Rage Against the Machine/The Prodigy kinda day.)

Apparently, this is a known problem, and Real says tough rocks.  I don’t blame them.  It’s what I’d tell me.  Luckily, some crafty bugger in the Real support forum mentioned that it worked for him if he cleared his IE cache before starting Rhapsody. 

Son-of-a-gun, that worked.  We’ll see how long it lasts…

Home | Music
Wednesday, 04 October 2006 15:02:49 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, 03 October 2006

I’m sure this must work for someone, but I’m having a heck of a time with ADAM.  The latest is that I can create users just fine, assign properties to them, etc., but I cannot seem to set their passwords.  I feel like I’m missing something here, possible a permissions problem, although I can set passwords through the ADSI GUI.  The other possibility is that the user is not in the right state, maybe not correctly enabled, or something.  This is the code that should work

            entry = new DirectoryEntry("LDAP://localhost:50000/CN=test.user,OU=Users,C=Bank", null, null, AuthenticationTypes.Secure);

            entry.RefreshCache();

            entry.Options.PasswordEncoding = PasswordEncodingMethod.PasswordEncodingClear;

            entry.Options.PasswordPort = 50000;

 

            entry.Invoke("ChangePassword", new object[] {"old","new"});

Unfortunately, I have no idea what the problem really is, because all I get back is

System.Reflection.TargetInvocationException was unhandled
  Message="Exception has been thrown by the target of an invocation."
  Source="System.DirectoryServices"
  StackTrace:
       at System.DirectoryServices.DirectoryEntry.Invoke(String methodName, Object[] args)
       at AdAzMan.Program.Main(String[] args) in Program.cs:line 43
       at System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()

Where the inner exception is {"An operations error occurred. (Exception from HRESULT: 0x80072020)"}.  Not very helpful.  That’s pretty much equivalent to “something bad happened”.  Granted, it’s a different error from the ones I get if the bind fails, or if I don’t set the entry’s password options to allow changing the password over an insecure channel, but this one’s got me stymied.

Tuesday, 03 October 2006 10:33:39 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, 28 September 2006

I'm working on a new project these days, and we're going all out with the bleeding edge goodies, including .NET 3.0.  To set the stage, we've got a server that internally calls a WF workflow instance, which is inherently asynchronous, so the interface we expose to clients is a WCF "Duplex Contract" which basically means two sets of one-way messages that are loosely coupled.  Think MSMQ, or other one-way message passing applications. 

[ServiceContract(CallbackContract=typeof(ITestServiceCallback))]

    public interface ITestService

    {

        [OperationContract(IsOneWay=true)]

        void SignOn(SignOnRequest request);

    }

 

    [ServiceContract()]

    public interface ITestServiceCallback

    {

        [OperationContract(IsOneWay = true)]

        void SignOnResponse(SignOnResponse response);

    }

However, we need to call this duplex contract from an ASP.NET application, since that's how we write out clients 80% of the time.  Since HTTP is inherently synchronous, this poses a bit of a challenge.  How do you call a duplex contract, but make it look synchronous to the caller?

The solution I came up with was to use a generated wrapper class that assigns each outgoing message a correlation id (a GUID), than uses that id as a key into a dictionary where it stores a System.Threading.WaitHandle, in this case really an AutoResetEvent.  Then the client waits on the WaitHandle.

When the response comes back (most likely on a different thread) the wrapper matches up the correlation id, gets the wait handle, and signals it, passing the response from the server in another dictionary which uses the WaitHandle as a key.  The resulting wrapper code looks like this:

 

public class Wrapper : ITestServiceCallback

{

    #region private implementation

    private static Dictionary<Guid, WaitHandle> correlationIdToWaitHandle = new Dictionary<Guid,WaitHandle>();

    private static Dictionary<WaitHandle, Response> waitHandleToResponse = new Dictionary<WaitHandle, Response>();

    object correlationLock = new object();

 

    private static void Cleanup(Guid correlationId, WaitHandle h)

    {

        if(correlationIdToWaitHandle.ContainsKey(correlationId))

            correlationIdToWaitHandle.Remove(correlationId);

        if(waitHandleToResponse.ContainsKey(h))

            waitHandleToResponse.Remove(h);

    }

 

    ITestService client = null;

 

    #endregion

 

    public Wrapper()

    {

        client = new TestServiceClient(new InstanceContext(this));

    }

 

    public SignOnResponse SignOn(SignOnRequest request)

    {

        request.CorrelationId = Guid.NewGuid();

        WaitHandle h = new AutoResetEvent(false);

 

        try

        {

            lock (correlationLock)

            {

                correlationIdToWaitHandle.Add(request.CorrelationId, h);

            }

 

            client.SignOn(request);

 

            h.WaitOne();

 

            SignOnResponse response = (SignOnResponse)waitHandleToResponse[h];

 

            return response;

        }

        finally

        {

            Cleanup(request.CorrelationId, h);

        }

    }

 

    #region ITestServiceCallback Members

 

    void ITestServiceCallback.SignOnResponse(SignOnResponse response)

    {

        Guid correlationId = response.CorrelationId;

 

        WaitHandle h = correlationIdToWaitHandle[correlationId];

 

        waitHandleToResponse.Add(h, response);

 

        ((AutoResetEvent)h).Set();

 

        return;

    }

 

    #endregion

}

This worked just fine in my command line test app, but when I moved to an actual Web app, hosted either in IIS or in VisualStudio 2005, I started getting random but very frequent exceptions popping up in my client app, AFTER the synchronous method had completed.  The exception had a very long and pretty incomprehensible call stack

System.ServiceModel.Diagnostics.FatalException was unhandled
Message="Object reference not set to an instance of an object."
Source="System.ServiceModel"
StackTrace:
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Dispatch(MessageRpc& rpc, Boolean isOperationContextSet)
at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result)
at System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Channels.InputQueue`1.AsyncQueueReader.Set(Item item)
at System.ServiceModel.Channels.InputQueue`1.Dispatch()
at System.ServiceModel.Channels.InputQueueChannel`1.Dispatch()
at System.ServiceModel.Channels.ReliableDuplexSessionChannel.ProcessDuplexMessage(WsrmMessageInfo info)
at System.ServiceModel.Channels.ClientReliableDuplexSessionChannel.ProcessMessage(WsrmMessageInfo info)
at System.ServiceModel.Channels.ReliableDuplexSessionChannel.HandleReceiveComplete(IAsyncResult result)
at System.ServiceModel.Channels.ReliableDuplexSessionChannel.OnReceiveCompletedStatic(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Channels.ReliableChannelBinder`1.InputAsyncResult`1.OnInputComplete(IAsyncResult result)
at System.ServiceModel.Channels.ReliableChannelBinder`1.InputAsyncResult`1.OnInputCompleteStatic(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Channels.InputQueue`1.AsyncQueueReader.Set(Item item)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(T item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(T item, ItemDequeuedCallback dequeuedCallback)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(T item)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecurityDuplexSessionChannel.CompleteReceive(IAsyncResult result)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecurityDuplexSessionChannel.OnReceive(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.ReceiveAsyncResult.OnReceive(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Channels.ReliableChannelBinder`1.InputAsyncResult`1.OnInputComplete(IAsyncResult result)
at System.ServiceModel.Channels.ReliableChannelBinder`1.InputAsyncResult`1.OnInputCompleteStatic(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Channels.InputQueue`1.AsyncQueueReader.Set(Item item)
at System.ServiceModel.Channels.InputQueue`1.Dispatch()
at System.ServiceModel.Channels.InputQueue`1.OnDispatchCallback(Object state)
at System.ServiceModel.Channels.IOThreadScheduler.WorkItem.Invoke()
at System.ServiceModel.Channels.IOThreadScheduler.ProcessCallbacks()
at System.ServiceModel.Channels.IOThreadScheduler.CompletionCallback(Object state)
at System.ServiceModel.Channels.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

Obvious? Not so much.

Interestingly, if I removed the lock around the hashtable insertion in the signon method

            lock (correlationLock)

            {

                correlationIdToWaitHandle.Add(request.CorrelationId, h);

            }

everything worked just fine.  Hmmmm. 

The problem, it turned out, was that ASP.NET uses (by default) a little thing called the SynchronizationContext.  As near as I can tell (I haven't researched this thoroughly, to be honest) one of it's jobs it to make sure that any callbacks get run on the UI thread, thereby obviating the need to call Control.Invoke like you do in WinForms.  In my case, that additional lock was giving something fits, and it was trying to clean stuff up on a thread that wasn't around any more, hence to NullReferenceException. 

The solution was provided by Steve Maine (thanks Steve!).  Unbeknownst to me (since I didn't know there was a SynchronizationContext) you can turn it off.  Just add a CallbackBehavior attribute to your client side code, with UseSynchronizationContext=false.  So now my wrapper class looks like this

[CallbackBehavior(UseSynchronizationContext=false)]

public class Wrapper : ITestServiceCallback

and everything works fine. 

I'm open to other ways of making this work, so I'd love to hear from people, but right now everything is at least functioning the way I expect.  Several people have wondered why we don't just make the server-side interface synchronous, but since we are calling WF internally, we'd essentially have to pull the same trick, only on the server side.  I'd rather burn the thread on the client then tie up one-per-client on the server.  Again, I'm open to suggestions.

CodeGen | Indigo | Work
Thursday, 28 September 2006 10:02:46 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  | 
# Monday, 18 September 2006

I'm pretty much a sucker for a good vampire novel.  I'd even go so far as to include the works of Ann Rice in that set, although perhaps in the guilty pleasure category.  I just finished The Historian by Elizabeth Kostova, and it's right up there at the top of the pack.  Maybe almost as good as Already Dead, the previous front runner. 

The Historian is a (dare I say) lavish twist on the Dracula myth, filled with exacting detail.  The author supposedly did 10 years of research while writing this book, and it certainly shows.  Lots of work went into this novel, which demonstrates a detailed understanding of the history of the Ottoman empire, Byzantium, and other aspects of Balkan/Near East history, so it might appeal to history buffs regardless of the vampire content. 

I actually listened to the Audible audio version, which was very long (something like 24 hours) and was read by two different actors who portrayed a set of 3-4 different characters, so it was very engaging to listen to.

There is a great twist towards the end that I totally didn't see coming.  It made the book that much more enjoyable.  Well worth checking out if you're into vampires, history, or both.

Monday, 18 September 2006 10:51:07 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, 11 September 2006
Yesterday my daughter and I hiked from Timberline lodge out to Zig Zag Canyon.  What a nice hike!  Just about the right length, and the weather up there was perfect yesterday.  Check out the pictures.

Definitely a good hike for even medium-sized kids, and the views are fantastic.

Monday, 11 September 2006 14:07:05 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |