Saturday, November 8, 2008

Tracing WCF messages on a Windows Server

If you use WCF for communication, you'll need to do quite a bit of debugging if you ever want to get to the bottom of serialization issues. Microsoft has thankfully provided a responsible tracing application which is installed with the SDK.

The Service Trace Viewer tool reads the default log file produced from the Server. To get this to be produced by your WCF Server simply add the following section to your web.config file.




switchValue="Information, ActivityTracing"
propagateActivity="true">

type="System.Diagnostics.XmlWriterTraceListener"
initializeData= "SdrConfigExample.e2e" />






This will produce a file in the root directory called SdrConfigExample.e2e. Start the Trace Viewer by clicking on the SvcTraceViewer.exe which is probably located in the default installation area C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin. Click File/Open and select the SdrConfigExample.e2e file, you'll probably have to select the "All Files (*.*)" option.
As soon as this is opened you should see all the messages which have been sent.

Tuesday, November 4, 2008

Adding Additional Debugging to a WCF call

I've recently been having a number of problems sending complex data types to a WCF service from an iPhone application I've been writing. I found this posting on the Apple site which increases the amount of debug information sent on each call.

// set debug props
WSMethodInvocationSetProperty(soapReq, kWSDebugIncomingBody, kCFBooleanTrue);
WSMethodInvocationSetProperty(soapReq, kWSDebugIncomingHeaders, kCFBooleanTrue);
WSMethodInvocationSetProperty(soapReq, kWSDebugOutgoingBody, kCFBooleanTrue);
WSMethodInvocationSetProperty(soapReq, kWSDebugOutgoingHeaders, kCFBooleanTrue);

Adding this to the genCreateInvocationRef method in your generated code from WSMakeStub did not require a lot of effort. I'd probably recommend it for each call.

e.g.

- (WSMethodInvocationRef) genCreateInvocationRef
{
WSMethodInvocationRef ref = [self createInvocationRef
/*endpoint*/: @"[URL]"
methodName: @"Generate"
protocol: (NSString*) kWSSOAP2001Protocol
style: (NSString*) kWSSOAPStyleDoc
soapAction: @"[method]"
methodNamespace: @"[namespace]"
];

WSMethodInvocationSetProperty(ref, kWSDebugIncomingBody, kCFBooleanTrue);
WSMethodInvocationSetProperty(ref, kWSDebugIncomingHeaders, kCFBooleanTrue);
WSMethodInvocationSetProperty(ref, kWSDebugOutgoingBody, kCFBooleanTrue);
WSMethodInvocationSetProperty(ref, kWSDebugOutgoingHeaders, kCFBooleanTrue);
return ref;

}

Creating Local DNS entries on the Leopard

I recently had a problem with a WCF service which would not resolve correctly on my Mac. It turned out that although I was accessing the Service using an IP address the WSDL XML file contained a number of references to the host name. To address this I needed to create a local DNS lookup so it could resolve correctly.


In Windows this is a simple matter of editing the Hosts file in the C:\WINDOWS\system32\drivers\etc\hosts file.

For the Mac OSX we use the command:
sudo dscl localhost -create /Local/Default/Hosts/myHost IPAddress [IPAddress], then Enter the administrator password. Where myHost is the name of the host and IPAddess the address to resolve too.
Removing the entry is a matter of entering:
sudo dscl localhost -delete /Local/Default/Hosts/myHost

Sunday, October 26, 2008

Mac to WCF Webservice - step 3

Following a substantial amount of head scratching with regard to connecting my Mac to a WCF WebService I'm finally at the stage of moving this to the iPhone environment. This application will build on the WCF service which was created in my earlier article.

  • Create a new iPhone application in XCode of type View Based Application.

  • Give it a meaningful name, I used iWCFDemo.

  • Add the following code to the iWCFDemoViewController.h file


  • @interface iWCFDemoViewController : UIViewController {
    IBOutlet UITextField *resultsField;
    }
    -(IBAction)pingTest:(id)sender;
    @end


  • Add the following code to the iWCFDemoViewController.h file


  • -(IBAction)pingTest:(id)sender
    {
    NSLog(@"Pressed!!");
    }


    This will allow for one button to invoke the WebService request and a text field to display the results following the call.


  • Double click the MainWindow.xib file to start the Interface Builder.

  • - Drag a Text Field from the Library onto the View Suface.
    - Drag a Round Rect Button onto the View Surface and give it the text "Press Me"
    - Drag a label onto the View Surface and give it the text "Results:"
    The result should look like the following:

  • Select the "File's Owner" from the xib view and drag a connection from the pingTest outlet to the button on the Layout Surface. Connect it to the "Touch Down" event.

  • Drag a connection from the resultsField Outlet to the Text Field on the Layout Surface.

  • The result should look like the following:

  • Save all the file and hit "Build and Go", when the Simulator appears, pressing the "Press Me" Button should produce the "Pressed!!" message in the Debugger Console.
    The results should look like something the following:

  • Now we add the connection to the WebService in by Generating the Proxi using WSMakeStub. Start a Terminal session go to the /Developer/Tools directory. Execute the command "WSMakeStubs -x ObjC -dir /Users/username/ -name JokeGen -url http://192.168.1.106/JokeGen/Joke.svc?wsdl" where the username is a valid location on your machine. This will produce the 4 files you need to access the WebService. Add these generated files to your XCode solution.

  • Modify the JokeGen.m file by adding the methodNamespace: @"http://tempuri.org/" and changing the Style parameter to (NSString*) kWSSOAPStyleDoc

  • Search for the resultsValue method in JokeGen.m and replace the generated with:
    - (id) resultValue
    {
    // return [[super getResultDictionary] objectForKey: @"parameters"];
    return [super getResultDictionary];
    }

    We do this so we can see Null values being passed back regardless of what type is sent via the WCF method.

  • Modify the WSGeneratedObj.m file by changing the generated code as its not correct first time out. Due to a conflict in the return values from the SOAP call we need to change the variable from "soapAction" to "soapActionKey".
    NSString* soapActionKey = @"SOAPAction";
    NSDictionary* headers = [self copyHeaderDictionary:1 extraVals:&soapAction extraKeys:&soapActionKey];

  • Add the import statements to the iWCFDemoViewController.m file, #import "WSGeneratedObj.h" and #import "JokeGen.h". Finally modify the pingTest method to invoke the WebService.

    -(IBAction)pingTest:(id)sender
    {
    NSLog(@"Pressed: ");
    NSString *result = [[JokeGen Ping:@""] objectForKey:@"/Result"];
    NSLog([result description]);
    resultsField.text = [result objectForKey: @"PingResult"];
    }


  • As these WebService calls require a number of additional functions which are not part of the standard application build so we have to add the CoreService. Select Add/Existing Framework from Frameworks folder in the Project tree. Browse to the Frameworks folder and select the CoreServices.framework and click OK

  • "Build and Go" the application and the result should be as follows:

    With the Debug window showing the full XML results for the Service Call:




OK so this is hardly an Earth shattering example, however it does show that the Mac OS and Windows .Net are valid options for developers.

Tuesday, September 23, 2008

Mac to WCF Webservice - step 2

Following the failure of my previous attempt to connect a Cocoa application to my Microsoft WCF WebService (Mac to WCF-It Never Happened), I spent some time investigating the issues. It would seem that all the elements are there within the Cocoa Framework to accomplish what I want, but from reading the various blogs its not a simple matter of using SVCUTIL to pump out a proxy class to the client.

Not to be discouraged, I have found that the Cocoa equivalent of SVCUTIL (the Microsoft Proxy class generator) is called "WSMakeStubs" and its accessible from the command line. Assuming your webservice is up and running (read step 2 for instructions), start the Terminal application and change your directory to /Developer/Tools. Type the following command:
WSMakeStubs -x ObjC -dir [output location] -name [ObjectName] -url [url to the WCF file]?wsdl

eg:
WSMakeStubs -x ObjC -dir /Users/User1 -name JokeGen -url http://192.168.1.106:8080/JokeGen/Joke.svc?wsdl

This created me 4 files;
  • WSGeneratedObj.m

  • WSGeneratedObj.h

  • JokeGen.m

  • JokeGen.h


Which can then be added to the XCode project and referenced in the AppContoller.m by adding the following lines:
#import "WSGeneratedObj.h"
#import "JokeGen.h"


Adding a button to the application and joining to the Outlet was followed by adding the following code to the AppContoller.m file:

-(IBAction)pingTest:(id)sender
{
// Show the User that something is going on
[progress startAnimation:nil];

//Create the URL
WSMethodInvocationRef soapCall;
WSGeneratedObj *sq = [[WSGeneratedObj alloc] init];
soapCall = [sq
createInvocationRef: @"http://192.168.1.106:8080/JokeGen/Joke.svc"
methodName: @"Ping"
protocol: (NSString*) kWSSOAP2001Protocol
style: (NSString*) kWSSOAPStyleRPC
soapAction: @"http://tempuri.org/IJoke/Ping"
methodNamespace: NULL
];

NSDictionary *result;

result = (NSDictionary *)WSMethodInvocationInvoke(soapCall);

if(WSMethodResultIsFault) {
NSLog(@"Error:");
NSLog([result description]);
} else {
NSLog(@"No Error:");
NSLog([result description]);
}
[sq release];

//Update the interface
[progress stopAnimation:nil];
}


Following a Build and Run I received the following error message:

Current language: auto; currently objective-c
2008-09-23 17:27:50.640 AmaZone[1166:813] Error:
2008-09-23 17:27:50.642 AmaZone[1166:813] {
"/FaultCode" = -1;
"/FaultString" = "The message with Action 'SOAPAction' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).";
"/kWSHTTPResponseMessage" = ;
"/kWSResultIsFault" = 1;
}


I assume that this was due to some basic miss-match between the client SOAP request and that expected by the server. Doing a number of tests and investigations it turned out that the file created by WSMakeStubs misses out on some important information.
The method Namespace for the WebService call is initially set to NULL, when it should match the SoapAction "http://tempuri.org/", be sure to include the trailing "/".

Following all these changes you should be able to call and retrieve the WCF WebService from a Mac Application.

Sunday, September 21, 2008

Mac to WCF WebService - it never happened.

This article came about from my investigations into developing an iPhone application. As part of my test project I was looking for the iPhone to make a WebService call to my server for data updates. This would be the approach I've used for many .Net application. Other iPhone application also seem to have the same Service Orientated Architecture (SOA) design, so I figured "How hard could it be"?

First off the development was to be broken down into three parts;
1) Build a standard Cocoa application to run on on the Mac calling a generic webservice. Why do this, simply it's because there are good samples already out on the net.
2) Build a WCF Service serving up some content over basicHTTP protocol. I've chosen WCF rather than a standard WebService as I'm more comfortable in the environment and I figure that most Corporate customers would have lots of .Net coders who need to expose internal data using this method.
3) Link the existing Cocoa application to the WCF WebService.

If all this worked I was going then going to convert the Cocoa application to the iPhone.

--- 1) Cocoa application to read from a webservice -------
Building the Cocoa Application to read from a webservice was not difficult. As an example I chose the example from Aaron Hillegass's excellent book "Cocoa Programming for Mac OSX" (ISBN:0-321-50361-9) pp.345-352, where he connects to the Amazon website to get book lists. He used the built in framework to build the request to the URL and parsed the results into an itemNodes array:


// Get the string and precent-escape for insertion into URL
NSString *input = [searchField stringValue];
NSString *searchString = [input stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
//Create the URL
NSString *urlString = [NSString stringWithFormat:
@"http://ecs.amazonaws.com/onca/xml?"
@"Service=AWSECommerceService&"
@"AWSAccessKeyId=%@&"
@"Operation=ItemSearch&"
@"SearchIndex=Books&"
@"Keywords=%@&"
@"Version=2007-07-16",
AXWS_ID, searchString];
// Make the call
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:30];
// Fetch the XML response
NSData *urlData;
NSURLResponse *response;
NSError *error;
urlData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
//Parse the XML Response
[doc release];
doc = [[NSXMLDocument alloc] initWithData:urlData
options:0
error:&error];
[itemNodes release];
itemNodes = [[doc nodesForXPath:@"ItemSearchResponse/Items/Item"
error:&error] retain];



While the Table view columns have been set with identified attributes of the xPath to the data e.g. "ItemAttributes/Title".

--- 2) Window WCF application to serve content -------
Building a WCF application using visual studio 2008 is a dream, simply create the service using File/New Web Site and select WCF Service from the list of options. I choose the name "JokeGen" for the project and renamed the default 'service.c" and "IService.c" files to "Joke". By default this will create the service using the wsHttpBinding within the web.config file. This provides message encryption for any traffic and from a Window Client this is a great idea. Unfortunately we are dealing here with a Mac, so I'm going to downgrade this to basicHttpBinding, that way we won't have to deal with manually decrypting the traffic. To do this edit the web.config file and change the line:
endpoint address="" binding="wsHttpBinding" contract="IJoke"
to
endpoint address="" binding="basicHttpBinding" contract="IJoke"

One other suggestion I'd make would be to remove the dynamic assignment of IP address while debugging. This is a setting on the web project file, setting the "Use Dynamic ports" to false and set the Port number to 8888. I do this as I like to test directly on my development box and as I'm using a VM I want to keep the URL consistent. We still need however to create the application in IIS for the site to be visible outside of the server.

We create two simple functions; first is "Ping" which just returns a "true" for testing purposes. The second is Generate which takes two nouns as string and returns a joke created from these values.
IJoke.C

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

// NOTE: If you change the interface name "IService" here, you must also update the reference to "IService" in Web.config.
[ServiceContract]
public interface IJoke
{
[OperationContract]
bool Ping();

[OperationContract]
string Generate(string noun1, string noun2);

}


Joke.c

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

public class Joke : IJoke
{
public bool Ping()
{
return true;
}

public string Generate(string noun1, string noun2)
{
return String.Format("What do you call an Irish {0}?\n A: A {1}", noun1, noun2);
}
}


--- Changing the Mac application to the WCF service -------

Now we need to modify our previous application to point to the WCF Service. First off we need to check if the service is visible. The simplest way to do this if from your Mac browse to the Windows server (or VM in my case). First ping your Mac from the Windows server, then ping the Windows box from your Mac. Use IPConfig on the Windows box if your unsure what your external IP address would be or use the DNS name you have created. I received a "Safari can’t open the page “http://192.168.1.106:8080/JokeGen/Joke.svc” [The location of the WebSite in IIS] because it can’t find the server" which was a nice way of saying my Firewall was not co-operating. I also got a Username and Password login prompt so I needed to change the permissions for the virtual directory. Eventually you will see the standard service description page.

So now I went off looking for an example and this is what I found:
consuming_a_wsdl_web_service_from_cocoa__failed_attempt_1

Basically this post indicates that this task is going to be a lot harder than I first expected. I'll post again on the use of the WSMakeStubs application and maybe, just maybe be able to finish my dream of having a simple webservice that can be read from the Mac.

Wednesday, September 3, 2008

iPhone development

And so the adventure begins...

Following a few days off (I'm at the start of a long break from work) I've decided to deep dive into iPhone/iTouch Objective-C development. Seeing how the product has taken the market by storm it's easy to get swept up in the hype and loose sight of the down sides; however from what I've seen, these small hand held devices are going to make it into the Corporate space much faster than anyone expects. Also with wireless technology improvements in speed, availability and even reasonable 3G tariffs, these devices will be used for accessing lots of corporate information.

OK, so why then did the Microsoft offering never make it? How can the iPhone break the hold that Blackberry has on Corporate? These are valid questions that probably need to be answered, but I'll leave that to the history books. From my point of view the iPhone has something that the Microsoft/Blackberry/Symbian offering never had and that's a new way to interact with the software... touch screens. Can the other devices catch up? Sure, but for now iPhone is riding a wave that does not seem to be ready to crash anytime soon.

To be honest starting this project has been on my mind for some time, being in the Microsoft space for about 20 years you'd think I'd stick to what I know, but I've always been a generalist at heart and knowing how something works helps me in many ways. Mostly I find it helps in the management of projects using these technologies. A little bit of knowledge is very powerful, I'm sure we've all sat down with a third party supplier or staff developer and been told "Oh, that's going to take a few weeks", when you know it's only going to really take a few hours. It's not that the developer is wrong, maybe they just don't know or don't understand your requirements. Bridging that gap in understanding is vital to setting customers expectations and (more importantly) setting and keeping to your deadlines.

So I start this work in earnest.. moving from the comfort of C#, Visual Studio and Windows 2003 to the new spaces of OSX is going to be a hard slog. In the following blogs I'll document my experiences, which I hope will assist other developers along the same road.

My plan is simple.. document how I got a simple application from the planning to production environment over the next few weeks. That with luck will involve setting up the environment, formulating the plan, reviewing online resources, getting the app past Apple QA and documenting lots and lots of error messages.

As I once said at the start of what turned out to be the worst project of my career "It's going to be great!".