Tuesday, February 24, 2009

XML is not a Programming Language

I've said this many many times before, but I don't think I've ever put it so eloquently.
"The fact is, that XML is not a programming language and it should not be used as one."
- One More Commit: The Future of Java Build Tools
This goes far beyond tools like ANT, and extends into other realms such as XSLT. Have you ever tried to do anything really complicated in XSLT?

Back in 2002 I took a XML course taught by the brilliant Axel T. Schreiner. This guy is seriously smart, and I mean it. He used to teach a two-hour class about complex language theory without any notes, completely from memory. One of our tasks was to write a lexicographical bubble sort on a string using standard XSLT. I think only a few people in the entire class figured it out, and the solutions were almost impossible to understand without explanation. It was a wonderful exercise, teaching us the ins and outs of XSLT, as well as its many limitations. If it taught me one thing, however, it's that I didn't ever want to have to write XSLT Stylesheets for a living.

The verbosity of ANT leads to its own problems. I've used ANT heavily in the past, and it has huge advantages over its predicessors. Unfortunately when you start getting even mildly complex build (or other) procedures the organizational overhead is almost nightmarish. It doesn't have built in hierarchical structures, has a non-intuitive inheritance model, and the control structures leave a lot to be desired. Using it to do anything beyond simple builds is difficult, and is definitely using ANT beyond its intended scope.

The main argument I've heard for using XML as either a programming language or in configuration files is that you can deploy without compiling. I've heard this from management, SCM, and some of my fellow developers. For some reason everyone has the misconception that if you don't have to compile to deploy then it's safe. I've even seen different testing procedures put in place depending on if you're deploying compiled java code vs. XML files. This absolutely blows my mind. The idea that an interpreted language is any safer than a compiled language is absurd; if anything it's more dangerous. I can't tell you how many times I've seen a plain old XML file rushed to prod, where it quickly caused huge problems that should have been picked up with propper testing.

ANT and XSLT are very powerful for the applications that they were created to meet. Using them beyond their intended scope is giving them a bad name.

I love XML. I really do. It's a wonderful document format, and lends itself nicely to configuration files. I even think it's a great way to represent serialized objects in many cases. However, it's not the end-all solution for every problem out there. I believe it particularly fails when it's used as a base for programming languages. I really just wish people would stop trying to fit a square peg into a round hole.

Friday, February 20, 2009

XML Request RESTful POSTing Client

Through much headache I have finally been able to create a simple WebRequest based client with which I can call my simple service via HTTP POST with XML in the HTTP Body! Horrah horrah!!

I found a million examples where you can pass your parameters via UriTemplate, simple URL parameters, JSON, and even putting the URL Parameters in the POST body replicating a Form POST. I'm sure I could have thrown together something where I could have parsed the XML from the input stream and generated the object that way (that's actually what I was doing for the URL Parameters in the POST body) but what's the point if you're pretty sure there's something that will do it for you??

So here you go, an example client that will post a simple XML-serialized object to a WCF Service that uses [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml)] but lets the system do the deserialization for you (you can't get it to not do the serialization for you, so we won't worry about that).

First lets define the contract for the service I'm sending to. It's basically a proxy for sending e-mail. It's a simple POST with Bare XML requests and responses:

[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml)]
StatusResponse SendMessage(SendMessageRequest request);

SendMessageRequest is defined as (not in full, obviously):

[DataContract]
public class SendMessageRequest
{
[DataMember]
public String EMail;
[DataMember]
public String Message;
}

I used an XmlTextWriter to generate the envelope (http body):

StringWriter envelopeGenerator = new StringWriter();
XmlTextWriter xmlEnvilopeGenerator = new XmlTextWriter(envelopeGenerator);

xmlEnvilopeGenerator.WriteStartElement("SendMessageRequest");
xmlEnvilopeGenerator.WriteAttributeString("xmlns", "http://schemas.datacontract.org/2004/07/");
xmlEnvilopeGenerator.WriteAttributeString("i", "xmlns", "http://www.w3.org/2001/XMLSchema-instance");

xmlEnvilopeGenerator.WriteStartElement("EMail");
xmlEnvilopeGenerator.WriteString("email@email.com");
xmlEnvilopeGenerator.WriteEndElement();

xmlEnvilopeGenerator.WriteStartElement("Message");
xmlEnvilopeGenerator.WriteString("Test Message");
xmlEnvilopeGenerator.WriteEndElement();

xmlEnvilopeGenerator.WriteEndElement();

xmlEnvilopeGenerator.Flush();

String envelope = envelopeGenerator.ToString();

Now it's very important that you don't start/stop an XML Document around the root element. This will add the XML declaration, which screws up the parser and you'll end up with an HTTP 400 response from your service. The above code gives you the following envelope:

<SendMessageRequest xmlns="http://schemas.datacontract.org/2004/07/" d1p1:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d1p1="xmlns"><EMail>email@email.com</EMail><Message>Test Message</Message></SendMessageRequest>

Creating that envelope is really the hard part. I tried a million different ways to represent the object in XML, learning along the way that the parser is very fickle. All the namespace attributes have to be correct, and that darn xml declaration screwed me up for quite some time. Lastly you just have to post the string (disclaimer, this is non-production test code, and obviously not robust. it is merely for example purposes and should not be used directly):

byte[] envelopeBytes = Encoding.ASCII.GetBytes(envelope);

Uri uri = new Uri("http://someuri");
WebRequest request = WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "text/xml";
request.ContentLength = envelopeBytes.Length;

using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(envelopeBytes, 0, envelopeBytes.Length);
requestStream.Close();

using (WebResponse response = request.GetResponse())
{
Console.WriteLine(new StreamReader(response.GetResponseStream()).ReadToEnd());
}
}

This returns a successful response:
<StatusResponse xmlns="http://schemas.datacontract.org/2004/07/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ErrorMessage/><ResponseCode>1</ResponseCode><ResponseCodeValue>Success</ResponseCodeValue></StatusResponse>

Now you might ask, why write this client in the first place when you can simply create a web service proxy to interact with the service. Well, I've got customers who aren't .NET shops so I need to know exactly what messages they need to send and what responses they should expect.

I hope you found this useful.

Happy POSTing!

Thursday, February 19, 2009

Your Servers Belong in the Cloud?

Thanks to Ryan Schneider over at Mastering the Cloud for sharing with me these amazing marketing videos from the Data Center Knowledge cloud services company. They're hilarious, b (or maybe c) movie style.

Your Servers Will Blow Up


Your Servers Are Not Bulletproof


Your Servers Will Burn


The best part is due to the success of this viral marketing campaign (I can only assume) the entire Data Center Knowledge site is down. So much for 100% up-time and easy scalability! Sweet sweet irony.

Thursday, February 12, 2009

RESTful Web Services with WCF

This is just another pass-through, but I found this resource invaluable when developing PhindMe's API and testing it in a simple fassion. It describes WebGet and WebInvoke briefly, but also provides examples and screenshots. These examples also provide the default URL format which you'll need if you want to do quick testing via a WebConnection or browser. Nothing profound here: just good solid reference material.

http://blog.donnfelker.com/post/How-To-Implement-a-simple-REST-Services-in-WCF-35-Part-1.aspx

Result Sets with Output Parameters

I recently wrote a stored procedure that optionally returns a result set AND uses output parameters: something I'd never done before. Logically I thought the best approach would be to grab the values from the output parameters first, then try to iterate over the optional result set. Everything seemed peachy, except all my output parameter values were dbnull. What gives?

Luckily my first result for "output parameter reader" provided me with the answer:
http://www.velocityreviews.com/forums/t94779-reading-both-result-set-and-output-parameter-of-sp-in-net-framework.html

Aparently the values for the output parameters are passed to the client only after all of the result sets have been processed and the reader has been closed. Works like a charm now.

Thank you Bruce Barker!

WCF WebInvoke & WebGet

This is the best article I could find explaining the WebInvoke and WebGet attributes. These can be most useful when dealing with legacy clients, or minimally non-WCF clients.

http://jeffbarnes.net/portal/blogs/jeff_barnes/archive/2007/11/28/wcf-3-5-webinvoke-attribute.aspx

One little tip, you'll most likely have to set BodyStyle=WrappedRequest if your function contract defines multiple parameters.

Choosing your WCF Binding

I found this great article describing how to choose the correct WCF binding. It doesn't include WebHttpBinding, but it would be another decision point on the way to Basic. Enjoy.

http://weblogs.asp.net/spano/archive/2007/10/02/choosing-the-right-wcf-binding.aspx