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!".