World of Warcraft and RESTful web services
REST web services are a pretty popular topic right now and a lot of people are seeing how easy it is to take advantage of them as opposed to other technologies such as SOAP. REST web services are the heart of the web: using HTTP to send messages back and forth without any additional messaging layers like SOAP envelopes. Instead you use the verbs present in the URI and the already existing HTTP methods (GET, POST, PUT, and DELETE).
Most of the web services you can find on the web can be considered nearly-RESTful or REST-like (or GETful as Scott Davis likes to call them). Why is this? Its because they have latched on to the GET method to provide data, but they haven’t even considered touching the other methods. This is a perfectly fine and valid approach; a lot of the time you are just wanting to get data for some purpose (like a Mashup with Google Maps or something) instead of updating data.
Did you know that REST-like web services are all around you? Would it be surprising to know that video-game giant Blizzard has provided such a service (whether they know it or not)? Recently Blizzard created the Armory tool for their mega-hit game World of Warcraft. It allows you to search for character data across their realms and see data such as items, professions, skill levels, reputation, and so on for every character in the game. It is a powerful tool if you ask me.
For example, look at the Armory profile for Bratta, my main character. You can see all sorts of statistics like the fact that I need to get off my butt and get some better gear. The site itself is a very pretty Web 2.0 site using a lot of cool technologies. The interesting part, however, is that the page itself is just an XML file that uses an XSL to transform the data into the HTML we see on the site.
What does this mean? We make a request with some verbs in the URI (r for the realm, n for the character name in the case of character-sheet.xml) and you get back XML containing the relevant data. It’s just a web service!
In fact, the XML you get (before the magical pixies sprinkle the XSLT all over it) looks something like this:
<page>
<characterInfo>
<character name="Bratta" race="Gnome" class="Warlock" level="70"> />
<characterTab>
<professions>
<skill name="Enchanting" value="176" max="225" />
<skill name="Tailoring" value="365" max="375" />
</professions>
</characterTab>
</characterInfo>
</page>
Yes, that is dramatically over-simplified, but you get the picture. We just have raw data from the web service, which means if we are clever enough we can use this to our benefit and capture this data in our own applications.
So let’s pretend we are interested in obtaining information on the primary professions of a character. We could easily build up a Groovy script to harvest that information.
Let’s define our URL. The URL needs to be encoded properly, so spaces need to be translated to +.
def characterUrl = "http://www.armory.worldofwarcraft.com/character-sheet.xml?r=Dark+Iron&n=Bratta"
Now let’s open a connection to the website. Normally we could skip this step and use XmlParser().parse(url) but in this case we are forced to change the User-Agent, because the Armory is doing a bit of browser detection.
// Use the User-Agent from a recent version of Firefox
def userAgent = “Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4″
URLConnection uc = new URL(characterUrl).openConnection()
uc.setRequestProperty(”User-Agent”, userAgent)
Now that we have a connection, let’s parse the XML. In Groovy this becomes one line of code:
def characterXml = new XmlParser().parseText(uc.inputStream.text)
We can now walk the DOM and get our skills. Normally you’d have to worry about child nodes, node types, and other such craziness, but with the magic of groovy, you can walk the DOM as if each element were properties of a parent object:
def skills = characterXml.characterInfo.characterTab.professions.skill
That returns us a list of our skill tags inside the profession element (see the XML example above). Now we can iterate over the skills and get the data using GPath (Groovy’s version of XPath):
skills.each { skill ->
def skillName = skill.’@name’ // The ‘@’ denotes an attribute of the element
def skillLevel = skill.’@value’
def skillMax = skill.’@max’
println “Bratta has profession $skillName at skill level $skillLevel/$skillMax”
}
And that’s all there is to it! Ideally you would want to add the profession to some sort of a bean object and add each bean object onto a list and return the list, but you get the idea.
From here you can see how easy it is to get this information. There are many ways you could go from here. You could query the guild-info.xml to get the names of all the members of your guild, then get the professions for each of those members and store that info in your own XML document or a database. Then you could build a web interface for your guild to query that data to find out who has a particular profession. The possibilities are limitless!