CFMobile Example – Accessing remote data from mobile application

So far I have posted CFMobile examples that were mostly standalone applications (except a photo application that uploaded image to server). However many mobile applications may need to interact with server, for example to show data from a remote database, to modify data or for many other purposes.

CFMobile features in ColdFusion Splendor make accessing remote CF server very easy. I will demonstrate this using a simple example – I will build a mobile app that displays employee records fetched from a remote CF server. The client side (cfclient) code calls a CFC on the server side which fetches data and returns result to the calling page. You will see that creating and accessing a server side CFC is as easy as it is in a completely server side CFML code – you don’t need to worry about writing code to make AJAX calls. cfclient does that for you transparently. I should mention here that this feature to call server CFCs from cfclient is not limited to mobile application, you can even use it for any web application.

Here is a screenshot of the application –

2014_03_25_image5

I will start with building the server side application. We need to create a database and a table on the server. I am going to use Embedded Apache Derby database for this application. Using ColdFusion Administrator, you can create both database and datasource for Embedded Apache Derby. Open up the administrator and go to ‘Data & Services’->Data Sources page and add a new data source, called EmployeeDS –

2014_03_25_image1

Click Add button, and you will see details page –

2014_03_25_image2

Specify database folder and select ‘Create Database’ option. I have created the database in cfusion/db/EmployeeDS folder. Derby database driver creates the last folder, so make sure EmployeeDS folder does not already exist in cfusion/db, otherwise Derby throws error.

We now need to create employee table. I have created a simple cfml file, db_scripts.cfm with functions to create database, list and delete  records.

<!--- Functions to create Employee databsase, display employees 
	and delete all employees from the table --->

<!--- Comment call to function createTable after table is successfully created --->
<cfset createTable()>

<!---<cfset deleteAll()>--->

<!---<cfset listEmployees()>--->

<br>Script Executed.

<cffunction name="createTable" >

	<cfquery datasource="employeeDS">
		create table employee (
			id integer not null generated always as identity,
			first_name varchar(20),
			last_name varchar(20),
			address varchar(100))
	</cfquery>
</cffunction>

<cffunction name="deleteAll" >
	<cfquery datasource="EmployeeDS" >
		delete from EMPLOYEE
	</cfquery>
</cffunction>

<cffunction name="listEmployees" >
	<cfquery datasource="employeeDS" name="rs">
		select * from EMPLOYEE order by FIRST_NAME
	</cfquery>

	<table >
		<tr>
			<th>First Name</th>
			<th>Last Name</th>
			<th>Address</th>
		</tr>
		<cfoutput query="rs">
			<tr>
				<td>#first_name#</td>
				<td>#last_name#</td>
				<td>#address#</td>
			</tr>
		</cfoutput>
	</table>
</cffunction>

Run this page and it will create employee table with fields id, first_name, last_name and address.

Next I will create a server side CFC, EmployeeDBManager.cfc with two functions – getEmployees and addEmployee –

<cfcomponent >
	<cfset this.ds = "employeeDS">

	<cffunction name="getEmployees" returntype="Array" access="remote" returnformat="JSON"
		hint="Fetches employee rows from Employee table and returns array of EmployeeTO components">

		<cfset result = ArrayNew(1)>

		<cfquery datasource="#this.ds#" name="empRs">
			select * from EMPLOYEE order by FIRST_NAME
		</cfquery>

		<cfloop query="empRs">
			<cfset var empStruct = {"id":id, "firstName":first_name,
									"lastName": last_name, "address":address}>
			<cfset arrayAppend(result,empStruct)>
		</cfloop>

		<cfreturn result>

	</cffunction>

	<cffunction name="addEmployee" access="remote" returntype="numeric"
		hint="Inserts a new employee record in the able">
		<cfargument name="emp" type="struct" >

		<cfquery datasource="#this.ds#" result="newEmpResult">
			insert into EMPLOYEE (FIRST_NAME,LAST_NAME,ADDRESS) values (
				<cfqueryparam cfsqltype="cf_sql_varchar" value="#emp.firstName#">,
				<cfqueryparam cfsqltype="cf_sql_varchar" value="#emp.lastName#">,
				<cfqueryparam cfsqltype="cf_sql_varchar" value="#emp.address#">
			)
		</cfquery>

		<cfreturn newEmpResult.generatedKey>
	</cffunction>

</cfcomponent>

Note that both the functions are marked remote (because they will be called from a cfclient block).
getEmployee returns Array in JSON format. It returns array of Struct with fields id, fistName, lastName and address.
addEmployee takes Struct argument with the same fields as above, except id. It returns id of the newly added employee.
We are done with the server side code for this application.

Let’s now create the client side application. In index.cfm of the client app, I first create HTML UI and write some JavaScript event handlers.

<DOCTYPE html>

<script src="jquery-2.0.3.min.js" ></script>

<script >
	$(document).ready(function(){

		$(document).on("click","#addBtn", function(){
			var firstName = $("#fnTxt").val();
			var lastName = $("#lnTxt").val();
			var city = $("#cityTxt").val();

			if (firstName.trim().length == 0)
			{
				alert("First name is required");
				return;
			}

			addEmployee(firstName,lastName,city);
		});
	});
</script>

<style >
	th,td {
		text-align:left;
	}
</style>

<h2>CFMobile Demo:</h2>
This application calls a CFC on the server side to get all employee records from a database table on the server.<br>
You can add an employee by filling up following details and clicking Submit button.
This again makes call to a server CFC to add employee to the table.

<h3>Add Employee:</h3>
<form>
	<table >
		<tr>
			<td>First Name:</td>
			<td><input type="text" id="fnTxt">
		</tr>
		<tr>
			<td>Last Name:</td>
			<td><input type="text" id="lnTxt">
		</tr>
		<tr>
			<td>City:</td>
			<td><input type="text" id="cityTxt">
		</tr>
		<tr>
			<td colspan="2">
				<button type="button" id="addBtn">Add</button>
				<button type="reset">Reset</button>
			</td>
		</tr>
	</table>
</form>
<hr>

<h3>Employees:</h3>
<table id="empTable" width="100%">
	<tr>
		<th>First Name</th>
		<th>Last Name</th>
		<th>City</th>
	</tr>
</table>

<cfclient>

    <cfscript>
    </cfscript>
</cfclient>

In the cfscript block in cfclient, I first instantiate the server side CFC, EmployeeDBManager, and then call getAllEmployees function.

try
{
	empMgr = new EmpServerApp.EmployeeDBManager();
}
catch (any e)
{
	alert("Error : " + e.message);
	cfabort ();
}

//get all employees from the server and display in the above HTML table
getAllEmployees();

empMgr is a global variable. You can create page level variable also using variables prefix, i.e variables.empMgr.
Notice that you instantiate server CFC from cfclient block just as you would do it in server side code.

In getAllEmployees, I call getEmployees method of the server CFC. But before calling the server CFC, I set callback handler (function) which will be called when server function returns.

//Fetch employees data from server and display in the HTML table
function getAllEmployees()
{
	try
	{
		//Set callback function on the server CFC, which will be
		//called with the result
		empMgr.setCallbackHandler(function(callbackResult){
			var employees = callbackResult.result;
			var empCount = arrayLen(employees);
			for (var i = 1; i <= empCount; i++)
			{
				addEmpToHTMLTable(employees[i]);
			}
		});

		empMgr.getEmployees();
	}
	catch (any e)
	{
		alert("Error : " + e.message);
		return;
	}

}

If you do not set the callback handler, remote CFC function would be called synchronously, which will block application UI till the CFC function returns. So make sure you set callback handler when invoking remote CFC function from cfclient.

Argument to the callbakc handler is an object with result field. This field actually contains result returned by the remote CFC function. getEmployee function of the CFC returns array (of struct/object), so I iterate over it and call addEmpToHTMLTable function for each employee struct.

<!--- Append employee data in the HTML table --->
<cffunction name="addEmpToHTMLTable" >
	<cfargument name="emp" >

	<cfoutput >
		<cfsavecontent variable="rowHtml" >
			<tr>
				<td>#emp.firstName#</td>
				<td>#emp.lastName#</td>
				<td>#emp.address#</td>
			</tr>
		</cfsavecontent>
	</cfoutput>

	<cfset $("##empTable").append(rowHtml)>
</cffunction>

There is one more function in the cfscript block of cfclient, to add new employee

//Add a new employee.
function addEmployee(firstName, lastName, city)
{
	try
	{
		var newEmp = {"firstName":firstName, "lastName":lastName, "address":city};

		//Set callback function on the server CFC, which will be
		//called with the result
		empMgr.setCallbackHandler(function(callbackResult) {
			newEmp.id = callbackResult.result;
			addEmpToHTMLTable(newEmp);
		});

		empMgr.addEmployee(newEmp);
	}
	catch (any e)
	{
		alert("Error:" + e.message);
		return;
	}
}

addEmployee (in cfclient) takes three arguments –  firstName, lastName and city (address). I create a new struct using these arguments and pass it to addEmployee method of the remote CFC (EmployeeDBManager). Here also I set the callback handler because I don’t want call to a server function blocking the application.
addEmployee function on the server side returns id of the new employee. I get this as result field of the argument to callback handler. Then I call addEmpToHTMLTable (see above) to append new employee to the HTML table.

This completes code for the application. You can open index.cfm of the client application in Chrome/Safari and see how the application works. You can also test it on mobile as a standalone application using PhoneGap Shell app that we have built (see links in my earlier post – Simplify Mobile Application Development Using ColdFusion).

However if you package the client application and install it on mobile device, you will find that application does not work – no data is fetched from the server.
When resolving URL of remote CFC in cfclient, the framework tries to resolve it from the URL of the client page. If client page and server CFC are loaded from the same server, which is what happens when you run the application in desktop browser or in the Shell application, cfclient is able to resolve the server CFC. But it cannot resolve it in a packaged application, because the client page is loaded locally from the device and server side CFC does not exist on the device.

To help resolve server CFCs (used in cfclient), you need to provide base URL of the server when packaging the application. You do that by going to project properties (right click the project in the Navigator view of ColdFusion Thunder and select Properties menu option)->ColdFusion Mobile Project-> Miscellaneous page and specifying ‘Application Base URL’

2014_03_25_image4

 

When you right click on the project and select ‘Generate PhoneGap Build’ menu option, the above URL will be used in the generated code to resolve server side CFCs.

You also need to provide access to the external server from your mobile application. To do that, go to PhoneGap tab (in Mobile Project Properties) and click  New button. Go to Access page and type IP of the external (CF 11) server or * to allow access to any external server.

2014_03_25_image6

Download CFBuilder projects for this application. EmpMobileApp folder in this zip contains client side application and EmpServerApp folder contains server side app. I am not providing Android APK file for this app because my server side CFC will not be accessible from this APK when you install it on you device.

Though I showed you how to access remote database from CFMobile application in this post, the technique is not limited to database access. Server CFC can get data from any source and return to cfclient.

-Ram Kulkarni

Update : Final build of ColdFusion 11 has a bug because of which access to CFC on the server does not work. To fix this issue, copy cfclient_main.js to wwwroot/CFIDE/cfclient folder of the server. Make sure you keep back-up of the old file before you overwrite it.

21 Replies to “CFMobile Example – Accessing remote data from mobile application”

  1. Ram,

    I am cannot get the server side CFC functionality to work using CFB3 and CF11 on the server, everything works great running the mobile project in the browser. However once I compile from Phonegap build and install the build on a device it no longer works or throws any errors.

    Is there something special I need to do to get the server side CFC stuff to work.

    For example, in my code inside of in my “index.cfm” I have.

    I call a function to get results and loop over it to display some sample data.

    On the server I have it in the folder “wwwroot/cfc/myCFC.cfc”

    I set the Application Base URL in the mobile properties to “http://www.myserver.com/”

    Is this correct?

    If I access my cfc via the browser it works fine, as in “http://www.myserver.com/cfc/myCFC.cfc?method=someMethod”.

    Under local server dev via the browser all the code works fine.

    I think I must be missing something in the docs, etc? Do I have my server side CFC in the right place?

    Any help would be appreciated, so far I am diggin the CFB3 mobile stuff.

    Thanks,
    -Isaac

    1. Issac, I don’t see any problem in the way you are accessing cfc. I think the problem is in packaging in CFB 3. Something is borken in the way application is packaged between public beta and and the final release. I am suspecting the issue could be in the config.xml generated for PhoneGap Build, but not sure at this point. We will investigate this issue and get back.

      1. Thank Ram.

        I am using a full copy of CFB3 I purchased last week and have CF11 hosting and am using a mac.

        Just so I am clear, I do not have to put the CFC files anywhere special on the server and they can be in any folder under root as long as I have the same path used to instantiate the components inside of cfclient? As in…

        myObj = new cfc.myCFC();

        I wasn’t sure because cfbuilder mentions a context of “cfusion” in the Application Base URL page and I cannot find any documentation on it.

        Have others reported this issue?

        Would it be possible to use my own config.xml to fix this or is this always generated?

        I previously had the thunder beta installed, should I clear that out and maybe reinstall CFB3?

        Thanks,
        -Isaac

        1. Yes, in the case you mentioned, myObj = new cfc.myCFC(); is correct. There is no specific folder where you have to save CFCs. They could be in any folder under wwwroot.

          And you don’t need to specify ‘cfusion’ in the application base URL if you have not configured application context in the server (typically JEE deployment). In the stand-alone server configuration, you don’t need to specify content root.

          You can use your own config.xml. In the ColdFusion Mobile Project properties, go to PhoneGap tab and check option ‘Load the configuration from XML’. Then specify the path to your own config.xml. If you just want to make minor changes to the config.xml that CFB 3 creates, then you can first export it from CFB (in project properties->PgoneGap tab, click ‘Export Config’ button), make changes and then make CFB use that file.

          I would recommend that you uninstall Thunder beta and reinstall CFB 3. And if you are using existing workspace then launch CFB 3 with ‘-clean’ option. You need to do this only once.

          1. App knows the server URL where CFC is located from “Application Base URL” in ColdFusion Mobile Project properties. config.xml should allow access to the server IP. You should have or if you want to provide access to any external server.

            In the project properties, you can specify access by going to PhoneGap tab, click New button, go to Access group and specify Origin.

    2. Issac, turned out that the problem was not in packaging. It was in the CF 11 server. Copy cfclient_main.js to wwwroot/CFIDE/cfclient folder of the server. Make sure you keep back-up of the old file before you overwrite it.

      With this fix, if you have set Application Base URL and Access Origin correctly, then your application should work fine.

      1. Thanks for your help, does this mean it’s broken as is and server side cfc’s will not work?

        I have shared CF11 hosting through a hosting provider with no administrative access to core CFiDE directory. Is this something they need to do?

        1. Without the patch I posted (cfclient_main.js), calling server side CFCs from cfclient won’t work. Yes, your hosting provider needs to update cfclient_main.js.

  2. Ram,

    Finally got it working, thanks for all your help.

    My hosting company HOSTEK updated the file after a support request and I still could not get it to work. This is because I initially did not replace the cfclient_main.js in the built in CFBuilder 3. Once I updated it there as well it worked great.

    Thanks again.

        1. Jason, so far the fix is only posted on blogs (my and the ColdFusion Team blog). If you are packaging applications from your local installation, then need to apply patch locally, even if your application accesses remote CFC hosted somewhere else. Only if you are using the remotely hosted server for packaging the app then you need to apply the hotfix on the remote server.

          I can’t say when the hotfix would be released by Adobe – it depends on many other factors. But this fix would be part of the first update for ColdFusion 11.

  3. Hello. I just stumbled across this. I’ve been setting up our CF11 environment and trying to learn using CFBuilder3 -> CF11 and mobile development. We’re using CF11 update 4 right now. We really want to start building mobile apps that our users can use to retrieve and update data in our network and this example is a great starter!

    I pretty much copied your downloaded files verbatim and just changed the server side queries for our own internal purposes to test. (and renamed a few things).

    I have two projects, the RemoteAccessMobileApp project (which is your “EmpMobileApp”) and I have RemoteAccessServerSide (which is your “EmpServerApp”).

    I got it working perfectly from a browser.

    I have included the phonegap access origin = * and set the Application Base URL (in the ColdFusion Mobile project properties of the “RemoteAccessMobileApp” project).

    To initiate the cfc I use:
    try{
    userMgr = new RemoteAccessServer.UserData();
    }
    catch (any e){
    alert(“Error: ” + e.message);
    cfabort();
    }

    “RemoteAccessServer” is the name of the folder/project on the web server where my “UserData.cfc” file exists. (which is basically your EmployeeDBManager.cfc).

    Like I said it works great in a browser, but when I run the app from the phone I get:
    Alert: “Error : RemoteAccessServer” is not defined.

    I’ve tried downloading and using your cfclient_main.js and that didn’t help (beside we have update4 which I’d hope has the fix). I noticed someone else comment about updating the cfclient_main.js in the CFBuilder app, but that’s not applicable to me as I use remote CF Servers (not that local one).

    Not sure what I’m doing wrong and any help would be appreciated.

    Scott

    1. Oh, one thing (and I’m not sure this is important or not), I did not include an application.cfm file for each project. You had an application.cfm file in each of your projects that had a single cfapplication tag. That is not needed is it?

  4. Well, I started over and just imported your apps and got them all setup and working from a browser (the only thing I modified was the queries in the CFC to work with my existing datasource for user information)…

    Generated the app and same thing… both the android and the iPhone throw the error that they can’t find “EmpServerApp”.

    I have the base application correct (my web server i’m using for this is http://10.3.30.243/). My phones are connected to the same network that has the web server. If fact, if I use Safari on my iPhone to connect to http://10.3.30.243/EmpMobileApp it works great too.

    I have CF11 Update 4. The cfclient_main.js is newer than the one you have here so I’m hoping that the CFC bug you’re referencing would be fixed by CF11 Update 4?

    I really need to figure out a solid way to do this as querying internal (remote) data is all I want to do. Any thoughts?

  5. I also can get no info from the remote server via a CFC when packaging the application an install the APK file on the device.
    The PhoneGapShellApp on the mobile device works oke.
    Any thoughts about solving this problem?

  6. Nearly everything worked fine for me, but I had problems with displaying the ’employee’ table data. It took a little while to find the reason why.

    After activating “Preserve case for Struct keys for Serialization.” under “Server Settings > Settings” in the ColdFusion Admin for the CF-server everything worked fine for me.

Leave a Reply to Anonymous Cancel reply