15 July 2013

Asus Zenbook and Ubuntu


I recently bought a shiny new Asus Zenbook, which which I'm delighted. However it came with Windows 8 and try as I might the sales guy wouldn't offer me anything else. I've recently blogged about my intense hatred for all things Windows 8 related. So I've decided to put Ubuntu on it, for now I'm dual booting. With past experience Ubuntu is so awesome, plus quick and easy to install I might decide to go back and clobber Windows 8 altogether.

The following are a few hints and tips I picked up if you try something similar. The bulk of the problem arises around the new version of BIOS called UEFI. This feels like an Intel thing that's been designed specifically for Windows and the rest of the world now has to catchup. Anyhow, I digress.
http://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface

The first thing I did was backup Windows 8. Just in case I needed to roll back. Then of course create a Ubuntu USB Install Key.

  1. Create a system image on a external hard drive or DVDs
    http://www.techrepublic.com/blog/window-on-windows/restore-windows-8-with-system-image-recovery/7464
  2. Create a Bootable USB Reovery Key
    http://www.techrepublic.com/blog/window-on-windows/create-a-recovery-drive-in-windows-8/7261

  3. Create a Bootable Ubuntu Install Key
    http://www.ubuntu.com/download/desktop/create-a-usb-stick-on-windows

Thanks to Greg Shultz for those great articles.

That's the easy bit. In the good old days of BIOS that was basically it. You could insert your USB Ubuntu key and reboot, possibly tweaking BIOS boot order slightly. Sadly this is where UEFI makes things very difficult.

First I had to hold delete to get into UEFI which can easily be missed as the setup window gets smaller all the time. Then you need to disable fast boot and secure boot.Now plug in your Ubuntu USB key and save and restart. As soon as you're out of UEFI hold down the escape key. This brings up the boot options. Hopefully your USB key will be listed there. If not select "Enter Setup" and try again. Sometimes this takes a few goes.

Once you've got the USB Key to boot, you can install Ubuntu as a dual boot or "Full Windows annihilation" mode. This is all pretty self explanatory.

When you finish you'll be asked to reboot. When I did this I went straight back into Windows. Lame.

To combat this I went back into UEFI, didn't change anything, but did a save and exit (F10). Then again held the scape key and selected my USB key from the boot menu. Instead of install, this time select trial, or run from usb key. This gets you running a version of Ubuntu without installing anything. From there follow these instructions:
https://help.ubuntu.com/community/Boot-Repair

This will re-install the boot loader that windows 8 has interfered with. As I mentioned I did a dual boot, so it re-installed GRUB and allowed me to select my desired OS on boot.

That should be it. Remove your USB key and you should be running Ubuntu.

08 July 2013

Android Spinner With Linked Values

In all the languages I've used I've had need for a drop-down. However they all seem to call it something different. .net calls it a combo box, html calls it a select tag and Android appears to call it a Spinner. Oh well! Normally I've used it as a key value pair. That is to say the string you select, isn't normally the same as the value you pass in the result. For example

<select>
    <option value="42">Volvo</option>
    <option value="24">Ford</option>
</select>

Android makes this a little tricky as it doesn't offer the same functionality, but using a hashmap provides a good way to go keep a map of options and their corresponding value.


private String[]            arrMuppetNames  = {"Kermit","Gonzo","Fuzzy","Animal"};
HashMap<String, Integer>    hashMuppets     = new HashMap<String, Integer>();

Setting up the hashmap is fairly trivial using the put method. In this example I used a set of if statements with hard coded values. Normally you'd get your values from a web service or a db call.

One problem I encountered with this method is when you setup your OnItemSelectedListener it will get fired immediately when  you start your application. Technically this is correct as something has been selected onLoad, but not normally desirable activity. So to combat this I've added in a --please select-- value and ignored this element. This is fairly common practice in HTML and works in Android too.

So this is the function that builds the spinner, note we're creating a new array with the zero value set to --please select--.

private void setSpinnerContent(String arrMuppets[]){
    Spinner spinner            = (Spinner) findViewById(R.id.muppets_spinner);
    String arrNewArray[]    = new String[arrMuppets.length + 1];
    
    //Add please select to spinner
    arrNewArray[0]    = this.getString(R.string.select_please);
    for(int i=0; i < arrMuppets.length; i++){
        arrNewArray[i+1]    = arrMuppets[i];
    }
    
    //Application of the Array to the Spinner
    ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(
            this,   
            android.R.layout.simple_spinner_item, 
            arrNewArray
    );
    spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 
    spinner.setAdapter(spinnerArrayAdapter);
}

Then last of course is the selected item listener

private Spinner.OnItemSelectedListener spinnerListener = new Spinner.OnItemSelectedListener(){
    @Override
    public void onItemSelected(AdapterView<?> arg0, View arg1,int arg2, long arg3) {
        Spinner        my_spinner    = (Spinner) findViewById(R.id.muppets_spinner);
        
        //arg 3 is index, if 0 it means they clicked "--please select--"
        if(arg3 != 0){
            //get selected content
            String    strName        = my_spinner.getSelectedItem().toString();
            //use hasmap to translate that into a corresponding id. 
            int        intCatID    = hashMuppets.get(strName);
            
            Toast.makeText(getApplicationContext(), String.valueOf(intCatID), Toast.LENGTH_SHORT).show();
        }
    }
    @Override
    public void onNothingSelected(AdapterView<?> arg0) {
        //this is what happens if we select nothing.
        return;
    }
};

As you can see, we ignore the --please select-- and then call the hashmap to get the value.

Easy! Here's the full code:
http://pastebin.com/kVxwEHgm

01 July 2013

Amazon AWS - Signature Version 4


If you decide to try and interact with AWS Glacier API or certain other AWS services you will need to interact with their signature version 4 authentication. Unfortunately in ColdFusion this is one of the hardest things I've ever had to do. Not really ColdFusion's fault, and not really Amazon's fault. Their documentation is comprehensive (although a little confusing) it is just incredibly fiddly. Hashing is a process where a single wrong character completely changes everything. So one slip up causes failures and it can be difficult to determine what you've done wrong.

I have previously blogged about AWS Signiature Version 2 and using it with AWS SES. This article also touches on HMAC and a few of the other key concepts:
http://webdeveloperpadawan.blogspot.co.uk/2012/02/coldfusion-and-amazon-aws-ses-simple.html


AWS Glacier - http://aws.amazon.com/glacier/

AWS Glacier is a very low cost storage solution designed for archiving and backing up data. The basic idea is its cheaper than S3 storage but access is limited. So you don't necessarily have immediate access to your backups. Instead access can be requested and files retrieved within a given time period.
In order to keep costs low, Amazon Glacier is optimized for data that is infrequently accessed and for which retrieval times of several hours are suitable.
- AWS Website
In order to make an API request to Glacier you are required to authenticate each request using their V4 Signature process. Data in Glacier is stored in "Vaults", similar to an S3 bucket a vault is a storage container. For the purposes of this demo I've created a vault using the AWS web management interface. I will be using the API to list all available vaults. In time I hope to expand tutorials and code to cover more complex operations. However, once you've got the signature sorted that shouldn't be too hard.

Code Glorious Code

Again I just want to make the point that I'm just addressing the signature here. I hope to expand the CFC to better deal with making full requests. That will come in time though.

Setup
This should be fairly self explanatory. 
variables.dteNow                  = DateAdd("s", GetTimeZoneInfo().UTCTotalOffset, now());
variables.strPublicKey            = "publickey";
variables.strPrivateKey           = "secretkey";

variables.oAwsSig4                = createObject("component","lib.awsSig4").init(strSecretKey = variables.strPrivateKey);

//We need a few custom date formats
variables.strCanonicalDate        = variables.oAwsSig4.getCanonicalDateFormat(dteNow = variables.dteNow);
variables.strShortDate            = variables.oAwsSig4.getShortDateFormat(dteNow = variables.dteNow);


Step 1 - Create A Canonical Request

The canonical request is basically a standard way to describe the request you are making to AWS. Be that a GET, POST to Glacier or whatever.


This is the aws documentation pseudocode that describes what's happening:

CanonicalRequest =
  HTTPRequestMethod + '\n' +
  CanonicalURI + '\n' +
  CanonicalQueryString + '\n' +
  CanonicalHeaders + '\n' +
  SignedHeaders + '\n' +
  HexEncode(Hash(Payload))

Although I think that's slightly wrong. In my example1 (below), the second empty line is unexplained.

Here's the call I make to the method:

//step 1, create canonical request
strCanonical    = variables.oAwsSig4.createCanonical(
    strHTTPRequestMethod       = "GET",
    strCanonicalURI            = "/-/vaults",
    strCanonicalQueryString    = "",
    arrCanonicalHeaders        = ["date:#variables.strCanonicalDate#","host:glacier.us-east-1.amazonaws.com","x-amz-glacier-version:2012-06-01"],
    arrSignedHeaders           = ["date","host","x-amz-glacier-version"],
    strPayLoad                 = ""
);

You'll note the payload is empty, so the hash at the bottom is simply a SHA-256 hash of "".

The finished Canonical request should look like this.
Example1:
GET
/-/vaults

date:2013-06-26T13:07:03
host:glacier.us-east-1.amazonaws.com
x-amz-glacier-version:2012-06-01

date;host;x-amz-glacier-version
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Step 2 - Create A String To Sign

The string to sign is a little harder to explain out of context. It's basically the message we will hash which will authorize the request to AWS. It's a short and simple string with a very concise format.

The pseudocode is fine here and describes it quite well:
StringToSign  =
Algorithm + '\n' +
RequestDate + '\n' +
CredentialScope + '\n' +
HexEncode(Hash(CanonicalRequest))

Here's my function call:
//Step 2 - Create String To Sign    
strStringToSign    = variables.oAwsSig4.createStringToSign(
    strAlgorithm        = "AWS4-HMAC-SHA256",
    strRequestDate        = variables.oAwsSig4.getStringToSignDateFormat(dteNow = variables.dteNow),
    strCredentialScope    = strShortDate & "/us-east-1/glacier/aws4_request",
    strCanonicalRequest    = strCanonical
);


This is the finished string to sign:
AWS4-HMAC-SHA256
20130626T133038Z
20130626/us-east-1/glacier/aws4_request
6d26f46dbf5d48665e06f44a2f9a65368b3b8d9ef45638b1496fbbe6604ed9db

Step 3a - Calculate Signing Key

OK Now it gets complicated. This is where we start running things through the HMAC function and problems quickly occur. If you do it right though, it'll all come together. AWS do this all in one step, but I think its easier as two.

Here's the documentation pseudocode:

kSecret = Your AWS Secret Access Key
kDate = HMAC("AWS4" + kSecret, Date)
kRegion = HMAC(kDate, Region)
kService = HMAC(kRegion, Service)
kSigning = HMAC(kService, "aws4_request")

Here's my function call:
//create singing key
bSigningKey    = variables.oAwsSig4.createSigningKey(
    dateStamp    = strShortDate,
    regionName    = "us-east-1",
    serviceName    = "glacier"
);

and here's the function:
<cffunction name="createSigningKey" access="public" returnType="binary" output="false" hint="THIS WORKS DO NOT FUCK WITH IT.">
    <cfargument name="dateStamp"    type="string"    required="true" />
    <cfargument name="regionName"    type="string"    required="true" />
    <cfargument name="serviceName"    type="string"    required="true" />
    <cfscript>
        var kSecret     = JavaCast("string","AWS4" & variables.strSecretKey).getBytes("UTF8");
        var kDate       = HMAC_SHA256_bin(arguments.dateStamp, kSecret);
        var kRegion     = HMAC_SHA256_bin(arguments.regionName, kDate);
        var kService    = HMAC_SHA256_bin(arguments.serviceName, kRegion);
        var kSigning    = HMAC_SHA256_bin("aws4_request", kService);
        
        return kSigning;
    </cfscript>
</cffunction>

I know I've not included the function in the other steps, but I want to highlight the importance of two things:
  • kSecret is "AWS4" + Secret Key, then cast into bytes. This is a very important step and where I was going wrong for quite a while.
  • variables.strSecretKey is the secret key you get from AWS in your account section. It's obviously secret and shouldn't be disclosed to anyone. In my example it's set in the variables scope of the cfc.
  • The function I use HMAC_SHA256_bin accepts a binary argument as param1. This is different from the example in my previous blog post on signature version 2, which used two strings as arguments.
So you can see with four HMAC steps, the tiniest mistake means a totally different response. Obviously AWS will be doing the same thing on their end, so if the two don't match, your request won't get approved.

Step 3b - Sign it!

OK Now we bring it all together

signature = HexEncode(HMAC(derived-signing-key, string-to-sign))

We take the signing key from step 3a and the string to sign from step 2:

bSignature    = variables.oAwsSig4.HMAC_SHA256_bin(strStringToSign, bSigningKey);

Step 4 - Put it all together

Now we make our request:
<cfhttp method="GET" url="http://glacier.us-east-1.amazonaws.com/-/vaults">
    <cfhttpparam type="header"         name="Date" value="#variables.strCanonicalDate#">
    <cfhttpparam type="header"        name="x-amz-glacier-version" value="2012-06-01" />
    <cfhttpparam type="header"        name="Authorization" value="AWS4-HMAC-SHA256 Credential=#variables.strPublicKey#/#variables.strShortDate#/us-east-1/glacier/aws4_request,SignedHeaders=date;host;x-amz-glacier-version,Signature=#lcase(binaryEncode(bSignature, 'hex'))#" />
</cfhttp>

  • Note we didn't hex encode our bSigniature from before, so I do that in the value. Probably better done elsewhere, but I'll get to it.
  • Also note the public key. Again this comes from AWS management console.
  • Note the three different dates, one is the strCanonicalDate we created at the top, the other is the short date and finally the hard coded glacier version date.
That's it. That should work!  Obviously you need the cfc. Take a look below for that.


Advice

My advice to anyone attempting to do this in ColdFusion or any other language is as such:
  1. Baby Steps - The documentation is presented in steps. Get each step working perfectly before moving onto the next. They all rely on each other, so a mistake early on will just cascade and waste time later.
  2. Unit Tests - I'm a huge fan of unit tests anyway, but in this case they really helped. Setting up some great unit tests using AWS examples will help you define your input and your output and tweak your code until you get the response you're looking for.
  3. Check Responses - If you actually make a request to AWS they will tell you in the response what the problem is. Plus they'll tell you the expected canonical request and the expected string to sign. These can really help iron out any tiny discrepancies
  4. Try it in Java -  CFML Doesn't have a native HMAC function (pre CF10), and converting too and from byte arrays caused endless problems. So I just did a few piecemeal functions in Java and learned from that.

The CFC

Ah of course one final step. The cfc. As I've said I hope to improve it a great deal and perhaps open source it and put it on cflib. I've also got a bunch of unit tests I wrote which may help people improve it.
Right now I'm just pleased to have got it working and don't want to forget it all so it's going on the blog.


Thanks

I ended up not using the code, but some of Ben Nadel's stuff was really useful to understand. He's written a great cfc that I urge anyone using HMAC in CFML to consider:
http://www.bennadel.com/blog/2412-Crypto-cfc-For-Hmac-SHA1-Hmac-Sha256-and-Hmac-MD5-Code-Generation-In-ColdFusion.htm




<cfcomponent output="false">

<!---
<OWNER> = James Solo
<YEAR> = 2013

In the original BSD license, both occurrences of the phrase "COPYRIGHT HOLDERS AND CONTRIBUTORS" in the disclaimer read "REGENTS AND CONTRIBUTORS".

Here is the license template:

Copyright (c) 2013, James Solo
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

--->

    <cffunction name="init" access="public" returnType="awsSig4" output="false">
        <cfargument name="strSecretKey" type="string" required="true" />

        <cfset variables.strSecretKey        = arguments.strSecretKey />
        <cfset variables.strPublicKey        = "mypublickey" />
        <cfreturn this>
    </cffunction>


    <cffunction name="createCanonical" access="public" returnType="string" output="false" hint="Create the canonical request">
        <cfargument name="strHTTPRequestMethod"        type="string"    required="true"                 />
        <cfargument name="strCanonicalURI"            type="string"    required="true"                 />
        <cfargument name="strCanonicalQueryString"    type="string"    required="false"    default=""    />
        <cfargument name="arrCanonicalHeaders"         type="array"    required="true"                    />
        <cfargument name="arrSignedHeaders"            type="array"    required="true"                    />
        <cfargument name="strPayload"                type="string"    required="false"    default=""    />
        
        <cfscript>
            var intCount            = 0;
            var strHeaderString        = "";
            var strCanonicalRequest = 
                arguments.strHTTPRequestMethod        & chr(010) &
                arguments.strCanonicalURI            & chr(010) &
                arguments.strCanonicalQueryString    & chr(010);
            
            //Headers
            for(intCount=1; intCount <= arraylen(arrCanonicalHeaders); intCount++){
                strCanonicalRequest    &= arguments.arrCanonicalHeaders[intCount] & chr(010);
            }
            
            strCanonicalRequest    &= chr(010);
            
            //Signed headers
            for(intCount=1; intCount <= arraylen(arrSignedHeaders); intCount++){
                strHeaderString        = arguments.arrSignedHeaders[intCount];
                strCanonicalRequest    &= strHeaderString;
                
                //put a semi-colon between headers, or a new line at end
                if(intCount EQ arraylen(arrSignedHeaders)){
                    strCanonicalRequest    &= chr(010);
                }else{
                    strCanonicalRequest    &= ";";
                }
            }
            
            strCanonicalRequest    &= lcase(hash(arguments.strPayload, "SHA-256"));
            
            return trim(strCanonicalRequest);
        </cfscript>
    </cffunction>


    <cffunction name="createStringToSign" access="public" returnType="string" output="false" hint="I create the string to sign">
        <cfargument name="strAlgorithm"            type="string" required="true" />
        <cfargument name="strRequestDate"        type="string" required="true" />
        <cfargument name="strCredentialScope"    type="string" required="true" />
        <cfargument name="strCanonicalRequest"    type="string" required="true" />
        
        <cfscript>
            var strStringToSign  =
                    arguments.strAlgorithm            & chr(010) &
                    arguments.strRequestDate        & chr(010) &
                    arguments.strCredentialScope    & chr(010) &
                    lcase(hash(arguments.strCanonicalRequest, "SHA-256"));
            
            return strStringToSign;
        </cfscript>
    </cffunction>


    <cffunction name="createSigningKey" access="public" returnType="binary" output="false" hint="THIS WORKS DO NOT FUCK WITH IT.">
        <cfargument name="dateStamp"    type="string"    required="true" />
        <cfargument name="regionName"    type="string"    required="true" />
        <cfargument name="serviceName"    type="string"    required="true" />
        <cfscript>
            var kSecret        = JavaCast("string","AWS4" & variables.strSecretKey).getBytes("UTF8");
            var kDate        = HMAC_SHA256_bin(arguments.dateStamp, kSecret);
            var kRegion        = HMAC_SHA256_bin(arguments.regionName, kDate);
            var kService    = HMAC_SHA256_bin(arguments.serviceName, kRegion);
            var kSigning    = HMAC_SHA256_bin("aws4_request", kService);
            
            return kSigning;
        </cfscript>
    </cffunction>


    <cffunction name="HMAC_SHA256_bin" access="public" returntype="binary" output="false" hint="THIS WORKS DO NOT FUCK WITH IT."> 
        <cfargument name="signMessage"    type="string" required="true" />
        <cfargument name="signKey"        type="binary" required="true" /> 
        
        <cfset var jMsg = JavaCast("string",arguments.signMessage).getBytes("UTF8") /> 
        <cfset var jKey = arguments.signKey />
        
        <cfset var key = createObject("java","javax.crypto.spec.SecretKeySpec") /> 
        <cfset var mac = createObject("java","javax.crypto.Mac") /> 
        
        <cfset key = key.init(jKey,"HmacSHA256") /> 
        
        <cfset mac = mac.getInstance(key.getAlgorithm()) /> 
        <cfset mac.init(key) /> 
        <cfset mac.update(jMsg) /> 
        
        <cfreturn mac.doFinal() />
    </cffunction>


    <cffunction name="toHex" access="public" returnType="string" output="false" hint="I convert binary to hex">
        <cfargument name="bSignature"        type="binary" required="true" /> 
        <cfreturn lcase(binaryEncode(arguments.bSignature, "hex")) />
    </cffunction>
    

    <cffunction name="getCanonicalDateFormat" access="public" returnType="string" output="false" hint="I return a formatted date time for the canonical part of the process">
        <cfargument name="dteNow"    type="date"    required="true" />
        
        <cfreturn "#dateformat(arguments.dteNow, 'yyyy-mm-dd')#T#TimeFormat(arguments.dteNow, 'HH:mm:ss')#" />
    </cffunction>
    

    <cffunction name="getStringToSignDateFormat" access="public" returnType="string" output="false" hint="I return a formatted date time for the string to sign section">
        <cfargument name="dteNow"    type="date"    required="true" />
        
        <cfreturn "#dateformat(arguments.dteNow, 'yyyymmdd')#T#TimeFormat(arguments.dteNow, 'HHmmss')#Z" />
    </cffunction>
    

    <cffunction name="getShortDateFormat" access="public" returnType="string" output="false" hint="I return a short date time">
        <cfargument name="dteNow"    type="date"    required="true" />
        
        <cfreturn "#dateformat(arguments.dteNow, 'yyyymmdd')#" />
    </cffunction>


</cfcomponent>

10 June 2013

CFML REST webservices with Railo

I've been doing a lot of REST web services with CFML and Railo lately and wanted to write up my experiences in case it helps.

First you need to setup Railo with your webservice cfc:
http://www.getrailo.org/index.cfm/whats-up/railo-40-released/features/rest-services/

Second you need to create and configure your cfc:
<cfcomponent rest="true" restpath="/hello">

The restpath provides the step in your URL from which your service is accessed. A normal REST webservice is pretty straightforward. Ensure your method is set to access="remote" and add in httpMethod and a restPath param to the cffunction.
<cffunction name="getmuppets" access="remote" httpmethod="GET" restpath="/getmuppets">

The restpath identifies the url structure you'll use to access the webservice.
The httpmethod tells the webservice if it should respond to GET/POST/PUT etc methods. These methods are defined and talked about more in REST definitions: http://en.wikipedia.org/wiki/Representational_state_transfer#Vocabulary_re-use_vs._its_arbitrary_extension:_HTTP_and_SOAP

Returning data is fairly standard, but if you want to return JSON then you might want to look at my previous blog post.

I've found that unlike much of CFML REST is case sensitive, so its a good idea to make everything lowercase.

This should be enough to get you going, but pretty soon you'll need to add arguments. So lets have a quick look at REST arguments. Lets assume for now we're just interested in a GET request.

You add your cfargument as your normally would, but you also need a param called restArgSource.

<cfargument name="intage"    type="numeric"    restArgSource="Path">

Your new method would look like this:
    <cffunction name="getmuppets" access="remote" produces="application/json" httpmethod="GET" restpath="/getmuppets/intage/{intage}/strsex/{strsex}/">
        <cfargument name="intage"    type="numeric"    restArgSource="Path">
        <cfargument name="strsex"    type="string"    restArgSource="Path">
        
        .....
    </cffunction>

Pay particular attention to how the restpath needs to be updated to match the new arguments. We're using restArgSource set to path, which means your url needs to provide those arguments. Here's an example URL:
http://localhost/rest/demo/hello/getmuppets/intage/30/strsex/male
Each step in the URL is a piece of the puzzle.

There are a few types that restArgSource accepts:
  • Path
  • Query
  • Matrix
  • Cookie / Header
  •  Form
Query is pretty straightforward, instead of using path (as above) you can just get your arguments from the query scope, i.e. ?strage=30. Form is when an argument comes from the FORM scope and you'd normally only use this for post and put httpMethods.
For a full explanation of these types, this article provides a good explanation:
http://www.adobe.com/devnet/coldfusion/articles/restful-web-services.html

That's it, you should be ready to write your own. Best of luck!







30 May 2013

Railo Rest JSON Web Service


So I've been writing lots of REST web services lately with Railo 4.0 and noticed a strange issue / feature when returning json.

So I write me a nice little function and I think everything will work with a little CFML magic:

    <cffunction name="getmuppets" access="remote" returntype="string" httpmethod="GET" restpath="/getmuppets">
        <cfscript>
            var arrMuppets    = [
                {name = "kermit", age = 20},
                {name = "fuzzy", age = 30},
                {name = "animal", age = 40}
            ];
            
            return serializeJson(arrMuppets)
        </cfscript>
    </cffunction>


All good? No :( The json is fine when you call the function locally, but when you call it as a rest webservice all the double quotes get escaped. Which renders it invalid json and is not much use for anything.
"[{\"age\":20,\"name\":\"kermit\"},{\"age\":30,\"name\":\"fuzzy\"},{\"age\":40,\"name\":\"animal\"}]"


So I eventually figured out you've got to change the headers and content returned by the function.
    <cffunction name="getmuppets" access="remote" produces="application/json" httpmethod="GET" restpath="/getmuppets">
        <cfscript>
            var arrMuppets    = [
                {name = "kermit", age = 20},
                {name = "fuzzy", age = 30},
                {name = "animal", age = 40}
            ];
            
            response    = {
                status        = 200,
                headers        = {},
                content        = serializeJson(arrMuppets)
            };
        
            restSetResponse(response);
        </cfscript>
    </cffunction>

Once you do all that, you get valid json:


[{"age":20,"name":"kermit"},{"age":30,"name":"fuzzy"},{"age":40,"name":"animal"}]

18 May 2013

Node JS

For no particular reason I thought I'd have a stab at Node.js today. I'm a bit curious and keen to see what all the fuss is about. Much (I suspect) like a lot of web devs out there I'm good at JS and jQuery (I even dabbled in prototype and YUI) but would've call myself a JavaScript pro! Today I just wanted to get a Hello World out the door and then later try and find out a bit more and perhaps get more advanced.

Install
So I must have read a dozen different tutorials today and every one of them is different. The official note.js site has an instillation guide https://github.com/joyent/node/wiki/Installation but I found it a bit daunting, so I pieced a few together.

I'm on Ubuntu so I downloaded the node-v0.10.7.tar.gz file from http://nodejs.org/ and unpacked it to a new directory.

Then I opened up a console and simply typed:

sudo make install

This took a while as Ubuntu busied itself, but that was it, pretty simple.
You can verify everything is running by typing

node --version

Get Started

Then I created a directory under /var/www/ and the obligatory Hello World file:

console.log("Hello World - All your base belong to me");

Then, still in the console I moved to my new application directory and executed my file:

node helloworld.js

That was it! Easy as pie so far!

14 May 2013

Android Fragments

In Android a fragment is a "behaviour or a portion of user interface in an Activity". I first met fragments when putting maps in my Android activity. They're also useful for putting various UI components on one activity.
I've adapted this example from here http://developer.android.com/training/basics/fragments/index.html
Here's the simple breakdown of using a list fragment:

1) Create an activity_main.xml in res/layout/ as such:
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:name="com.example.droidtestnew.smallpage"
              android:id="@+id/article_fragment"
              android:layout_weight="2"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

</LinearLayout>
2) Create another activity_main.xml in res/layout-large/ (note the second fragment):
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:name="com.example.droidtestnew.smallpage"
              android:id="@+id/headlines_fragment"
              android:layout_weight="1"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

    <fragment android:name="com.example.droidtestnew.smallpage"
              android:id="@+id/article_fragment"
              android:layout_weight="2"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

</LinearLayout>
3) Create a java class called smallpage.java
package com.example.droidtestnew;

import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.widget.ArrayAdapter;

public class smallpage extends ListFragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        String arrHello[] = {
            "Hello One",
            "Goodbye Two"
        };
        
        // We need to use a different list item layout for devices older than Honeycomb
        int layout = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
                android.R.layout.simple_list_item_activated_1 : android.R.layout.simple_list_item_1;

        // Create an array adapter for the list view, using the Ipsum headlines array
        setListAdapter(new ArrayAdapter<String>(getActivity(), layout, arrHello));
    }

}
4) Change MainActivity.java to extend FragmentActivity:
public class MainActivity extends FragmentActivity {
You're done. That should be all you need. If you launch this on a small screen, you'll see the one fragment list, if you launch it on a tablet, you'll see two lists.

Easy as pie! Obviously this is an incredibly basic example, but it should get you started with fragments.