REST API prepared on ColdFusion with FW/1 and DI/1 dressing

Cooking Developing REST API with ColdFusion or Railo is not a big deal now

But what if you don't want to create 'special' CFCs, want to reuse code from other projects and stay in line with rest of your FW/1 application ? Then you want to use FW/1 features to create REST API. I prepared small example which involves user login and returning user data via REST API.

The core of API code is FW/1 routes and CFC methods which are executed when route URL is called. To set up routes, Application.cfc is modified to have variables.framework.routes variable store mapings from REST routes to regular FW/1 application actions.

// Application.cfc
variables.framework = {
routes = [
{ "$GET/users/$" = "/users/list" },
{ "$GET/users/:id/$" = "/users/get/id/:id" },
{ "$POST/users/$" = "/users/create" },
{ "$DELETE/users/:id/$" = "/users/delete/id/:id" }
{ "$POST/users/login/$" = "/users/login" },
]

Note that HTTP method and route is mapped to FW/1 application route. Calling GET method on [siteroot]/users URL (route) is like having the framework to execute list() method in users.cfc controller via /index.cfm/users/list/ call. What is different from regular execution, is that controller method will use variables.fw.renderData method to return data. No view file will be searched for, unlike it happens in regular FW/1 method call.

To get advantages of DI/1 and bean factory we should have two files in our project  

/org/corfield/framework.cfc
/org/corfield/ioc.cfc

and register bean factory in Application.cfc

// Application.cfc
function setupApplication(){
var bf = new org.corfield.ioc('model');
setBeanFactory(bf);
}

The argument for bean factory ('model' in our case) is the path where factory going to look for beans and services. Bean is CFC entity that is created every time we asking factory to get us one. Services are created only once (singletons). Here's folder structure:

/model/beans/user.cfc
/model/services/Security.cfc

User is a bean representing user of the system and Security is a singleton CFC instance which is in charge of user management (retrieves and modifies user details and user permissions.)

Now, the routes are in place, bean factory is setup, we need method to do actual work (in this case, login user)

// controllers/users.cfc
component accessors="true" {
property name="SecurityService";
function init(fw){
variables.fw = fw;
}
function login(rc){
var jsondata = DeserializeJSON(GetHttpRequestData().content);
var user = VARIABLES.SecurityService.login(jsondata.login, jsondata.password);
variables.fw.renderData("json", user);
}

First as we see here, component's accessors property set to true, to have ColdFusion add implicit getters/setters methods for component properties. The SecurityService property is declared, following naming convention - we have /model/services/Security.cfc, which became SecurityService (yes, DI/1 is clever enough to understand that singular "service" means plural "services".) So VARIABLES.SecuirtyService will receive singleton object of Security.cfc. Result of SecurityService.login() method is then passed to FW/1's renderData method which will output object in JSON-encoded format. Valid encoding are also XML and plain text. DeserializeJSON method is used to create ColdFusion objects out of JSON-encoded data that is being sent to API with POST method.

Raw request is looking like this:

POST http://localhost/users/login HTTP/1.1
Host: localhost:80
Content-Length: 33
{login:'john',password:'1234567'}

Raw response generated by our code:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=utf-8
Content-Length: 72
{"LASTNAME":"Smith","FIRSTNAME":"John","ID":44312949}

In following article, I'll describe server setup for proper REST API functioning. For commentaries please check this post in CF User Group Ukraine Facebook group.

Fast start with Railo and Apache web server

After system reinstall, I had to quickly rebuild development environment with Railo and Apache (or nginx web server). Small cheat-sheet on configuring web servers with Railo.

0. No web server front

Railo on top of Tomcat has its own web server built-in, and it listens to port 8888 by default - if accessed with browser directly, Tomcat will answer without need of Apache HTTPD or other web server at all.

1. Classics: using ajp

Use Apache 2.4 binaries from Apache Lounge and install mod_jk.

# File: workers.properties
worker.list=worker1
worker.worker1.type=ajp13
worker.worker1.host=127.0.0.1
worker.worker1.port=8009
# File: mod_jk.conf
LoadModule jk_module modules/mod_jk.so
JkWorkersFile /etc/httpd/conf/workers.properties
<IfModule jk_module>
JkMount /*.cfm ajp13
JkMount /*.cfc ajp13
JkMount /*.do ajp13
JkMount /*.jsp ajp13
JkMount /*.cfchart ajp13
JkMount /*.cfres ajp13
JkMount /*.cfm/* ajp13
JkMount /*.cfml/* ajp13
JkMountCopy all
JkLogFile C:\Apache\logs\mod_jk.log
</IfModule>
# File: httpd.conf
Include mod_jk.conf

 

2. Forwarding to AJP port 

We can get rid of mod_jk, and use mod_rewrite for URL tweaking and sending CFML requests to Railo

# File: httpd.conf
ProxyPreserveHost On
ProxyPassReverse / ajp://localhost:8009/
RewriteEngine On
RewriteRule ^(.+\.cf[cm])$ ajp://localhost:8009$1 [P]

 

2.1 Forward to built-in web server

Instead of forwarding to AJP port, we can forward to built-in web server directly:

# File: httpd.conf
ProxyPassReverse / http://localhost:8888/

 

3. Using Railo with nginx front

If you feel comfortable with nginx for serving static files, here's config to forward CFML requests to Railo. Works perfectly.

# File: nginx.conf
server {
listen 8000;
server_name localhost;
 location / {
root c:/wwwroot;
index index.cfm index.html index.htm;
}
 # Main Railo proxy handler
location ~ \.(cfm|cfml|cfc|jsp|cfr)(.*)$ {
proxy_pass http://127.0.0.1:8888;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}
}

Nexus S on Windows 8 issue

I've got a bit outdated Nexus S phone and I decided to try it as test device for some Phonegap development. I had it updated with CyanogenMod for Nexus S (crespo) to Android 4.4.2 in less than one hour. But when tried to run Phonegap app on it, seem like Windows 8 has problem identifying the device (Ubuntu doesn't FTW!). The driver I've found on Samsung website didn't help, phone was listed as unknown device in Device manager. However, generic Google USB Driver that comes with Android SDK does the trick. Use 'Update driver' for unknown device in Windows' Device manager and point it to

[Android SDK folder]\sdk\extras\google\usb_driver\

to help Windows locate driver.

Nexus S Windows 8

After this, plug in Nexus S to USB, turn on USB Debugging, accept computer's key permanently (it will be prompted on phone). Check device to be properly found by SDK by issuing command:

>adb devices
List of devices attached
3232DCDC6699E0E0E0 device

Now the Nexus S device is ready for application development.

Starting Phonegap development: configuration and tools

Those familiar with Phonegap development know this awesome service: Adobe PhoneGap Build. It allows building for different platforms without having entire Phonegap infrastructure installed on developer's machine. However, some 'old-school' guys like me, prefer having everthing within reach on local machine. Here are some instructions to get up and running with Phonegap development for Android platform.

Prerequisites

System: Ubuntu 13.10 or Windows 7 or 8

IDE: Eclipse 3.8.1 or IDEA 13

Java: Oracle JDK 1.7.0_45

Download latest JDK from Oracle web site, untar it to destination of choice (I used /opt). Install alternatives like described in this article. Make sure it's working and Oracle JDK is default:

# java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

Android phone

Plug into USB and turn on development mode. For version 4.2 you have to tap About Phone -> Build number several times to activate Developer's menu, like described here. I'm also used CyanogenMod of Android 4.4. Turn on USB Debugging from Developer options.

Software

  • Download and Install ANT, just unzip to destination folder and add it to $PATH variable;
  • Download and install Android Development Kit - in fact you need only unzip it to destination folder. Add [ANDROID_SDK_FOLDER]/sdk/tools and /sdk/platform-tools to your $PATH variable;
  • Download and install node.js - just copy in destination folder and add path to [NODE_JS_FOLDER]/bin to your $PATH variable;
  • Install Phonegap package using Node Package Manager
# npm install -g phonegap
  • Then create Phonegap project. This will create HelloWorld package with default activity of HelloWorld in folder hello:
$ phonegap create hello

or in full form:

$ phonegap create hello com.example.hello HelloWorld

Eclipse

To develop and debug in Eclipse with ADT Plugin, or with ADT Bundle, start new project:

New project -> Android -> Android project from existing code

Point Eclipse to pick up phonegap-generated code. Make sure that both project and library files were picked up by Eclipse.

Then Run As -> Android Application

IDEA

Despite being favorite IDE for Android development at the moment, I've only found handful of instructions for running Phonegap project in IDEA. For time being, I'm editing HTML files from /www folder in IDEA (as Web project) and then sending to be installed to device via this command:

phonegap run android

This command is run from application folder, it will compile and install app on active development device.

Monads 101

Concepts of functional programming explained, very interesting for imperative language developer. Main idea I brought from the video is that we can control complexity of software and libraries, by having functions obey rules, allowing combine functions in library in safe way. Having small libraries of safely combinable functions keeps complexity under control.