Skip to end of metadata
Go to start of metadata

Overview

The VMT (Virtual Mobile Technologies) RAMP platform provides a secure mobile enterprise application platform (MEAP) that drastically reduces the cost and time to market for secure mobile solutions. A mobile solution that is built on the VMT RAMP platform is seamlessly kept relevant as technology changes.

The RAMP platform is the only platform that is geared towards securely integrating into existing enterprise infrastructure from mobile devices ranging from low-end 2003 models up to the latest smart phone. Every application built on top of the RAMP platform contains US NIST validated security for 3DES, RSA, random number generation (RNG), and AES, allowing for secure end-to-end communication from the mobile application to any enterprise services.

Example applications

To get an idea of the power and versatility of the platform, here are some example applications that have been built using it. The example applications can be found in the RAMP SDK apps directory.

Price Snap

Price Snap is an application that allows you to use the camera to read barcodes for physical products and compare their online prices. It also allows you to search for products.

This application's client code can be found in the apps/client/pricesnap directory, and the server code can be found in the apps/server/pricesnapserver directory. It makes use of the Android and iPhone barcode plug-ins.

A Price Snap server is already running at http://s.ramp.virtualmobiletech.com/ramp_showcase. If you'd prefer to run your own server, you will need to obtain an App Name from the eBay Developers Program to allow access to eBay services. You will then need to edit the "ramp-config.properties" file and add an "ebay.appname" property referencing your App Name.

iPhone screenshot

Android screenshot

Flow Draw

Flow Draw turns your smart phone or tablet into a whiteboard and allows you to quickly sketch out a process diagram and email it to a colleague.

This application's client code can be found in the apps/client/flowdraw directory. It makes use of the Android and iPhone email plug-ins.

Here is an iPhone screenshot:

And here is an iPad screenshot:

Graph Demo

This application generates a graph from a set of data.

This application's client code can be found in the apps/client/graphdemo directory.

Client/server architecture

For the RAMP platform, client-side mobile applications are created using:

  • UXML – The User Interface XML, a declarative language for laying out user interface components such as forms and buttons.
  • CSS for styling the UI components
  • RAMPScript (or JavaScript) for specifying the behaviour of an application. For example, validating user input or interacting with web services.

Although the RAMP platform can be used to develop standalone mobile applications, it is generally used to integrate with enterprise services. To do this one needs to write a RAMP server integration.

RAMP server integrations can be written in any JVM language (e.g. Java, Clojure, Scala etc.). The RAMP server can also proxy any requests to remote machines which allows integration using any server technologies (e.g. .NET), more information here. For the RAMP platform, an integration typically needs to implement only a single interface to make a service available to the client. This code resides on the RAMP Server and integrates a RAMP client application with a non-RAMP server or web service.

The role of a RAMP server integration is, generally, to translate requests from the RAMP client for handling by another server and to send the results back to the client (see the figure below).

RAMP platform components

The RAMP platform consists of several collaborating components that allow developers to rapidly deliver secure native mobile applications:

  • RAMP Deployment Platform
  • RAMP Server
  • RAMP Virtual Machines (VMs)
  • RAMP IDE

RAMP deployment platform

The RAMP deployment platform is responsible for creating the RAMP virtual machines for each mobile operating system and for delivering the correct RAMP VM to a requesting device. The deployment platform creates a set of RAMP VMs for all supported devices based on the configured settings during creation (e.g. Security keys, feature server, service server, icon, etc.).

When a device requests to download a specific application, the deployment platform automatically detects the device (e.g. make/model) and serves up the correct RAMP VM for that particular device. The diagram below illustrates this process:

The deployment platform negates the need to have the SDK for each mobile operating system (e.g. J2ME WTK, Android SDK, Blackberry tools, etc.), an application is simply created via the web interface of the deployment platform and the RAMP VM (mobile application) is automatically built for each supported mobile operating system. This means that you can create secure cross-platform mobile applications without having any knowledge of how an application is created for the targeted mobile operating systems.

The deployment platform also removes the requirement for your end users to select their device's make/model during download, the deployment platform simply detects their device and presents the correct RAMP VM for download to them. This is a far better user experience than forcing a user to select their device from a list.

RAMP Server

The RAMP server is responsible for providing online services to the RAMP VM as well as compiling the UXML, CSS and RAMP Script. The RAMP Server consists of two major components; the service server and the feature server.

The feature server component is responsible for compiling the UXML, CSS and RAMP script developed by a RAMP developer and to provide it to the RAMP VM.

When the RAMP VM starts up the first time it connects to the RAMP Server that was configured as the feature server and requests the compiled form of the UXML, CSS and RAMP Script. The RAMP VM then executes the compiled code. The RAMP VM can also be configured to only re-download the compiled code when the RAMP developer makes a change to the UXML, CSS or RAMP script files. The diagram below illustrates(with example devices for each platform) this process:

Once the RAMP VM is installed on a user's mobile device it can invoke services on the RAMP service server in order to gain access to enterprise services and data. The RAMP VM connects to the set of service servers that it was configured with during creation.

The communication between the RAMP VM and the service server is done via a binary protocol so that bandwidth usage is minimized. The RAMP server converts incoming service requests to ServiceRequest objects and the integration plugins are able to respond to the service requests via ServiceResponse objects. The diagram below illustrates this flow: 

The RAMP core is the entry point for all communication between the RAMP VM and the RAMP service server. Incoming service requests are converted from their binary representations to ServiceRequest objects by the RAMP core. Each service request contains the name of the service that the RAMP VM wants to invoke along with all parameters and their values.
The RAMP core expects the integration plugin to provide an implementation of the ServiceRequestManager . The purpose of the ServiceRequestManager is to resolve and return ServiceRequestHandler implementations based on the name of the incoming ServiceRequest .

The purpose of ServiceRequestHandler implementations is to process ServiceRequest objects and to invoke enterprise services or retrieve enterprise data based on the incoming ServiceRequest parameters and values. Once a ServiceRequestHandler implementation has been resolved and returned by the ServiceRequestManager the ServiceRequestHandler implementation is given the opportunity by the RAMP core to process the ServiceRequest . The ServiceRequestHandler responds to the RAMP core with a ServiceResponse after necessary processing has been completed. The RAMP core then serializes the ServiceResponse into a binary format and sends it back to the RAMP VM. The ServiceRequest handlers (as well as the ServiceRequestManager ) can throw exceptions at any time and these will be passed back to the RAMP VM.

RAMP Virtual Machines

The RAMP VMs are native applications that are installed on the various mobile device operating systems. The RAMP VM executes the compiled format of UXML, CSS and RAMP script and adapts it to the operating system that it is running on. This means that the developer only needs to write a single application using UXML, CSS and RAMP script that is then able to run on all the supported mobile operating systems (e.g Blackberry OS, Android, Nokia, Motorola, etc).

RAMP IDE

The RAMP IDE is an eclipse plugin which can be used by a RAMP developer to code UXML, CSS and RAMP Script for RAMP client-side applications. The RAMP IDE provides features that are found in modern IDEs.

Using the RAMP IDE, a RAMP developer can upload the UXML, CSS and RAMP Script to a RAMP Server for distribution to the RAMP VMs.

Getting Started with RAMP

JavaScript support

Note that in Ramp 5.0.0 support for using JavaScript instead of RAMPScript was added. Additional information is available here.

 

 

This section describes how to develop RAMP applications by example. It is not a reference for UXML, CSS, RAMPScript or server-side development. The definitive guides are the reference guides.

We start by demonstrating how to set up your client development environment; applications can be created with the RAMP IDE plugin for Eclipse. While it is possible to develop applications without the RAMP IDE, it is recommended for serious development since it simplifies the management of a complex project as well as giving you features such as source control with CVS or Subversion for free. We start by developing a very simple standalone "Hello, world" client application that does not need a service server.

Next, we develop a limited, but functional, Twitter application that allows a user to post Twitter 'tweets' and to view their friends' tweets (their timeline). This application introduces RAMPScript to validate user input and shows how the RAMP client handles errors. Naturally, the twitter client needs to interact with a web service to post and view tweets. We show how to write RAMP services in Java for deployment on a RAMP integration server. The Twitter service deployed on the RAMP server interacts with the Twitter API and translates the responses from Twitter into a compact format that can be parsed by the RAMP client. Once we have the Twitter service up and running on a web server, we continue our development of the client and write RAMPScript code to communicate with the RAMP server.

RAMP is intended for secure enterprise mobile applications, and can be used to integrate securely with any system that allows integration from another system. RAMP has been used to integrate with ISO 8583 interfaces(e.g. Postilion), ERP systems (e.g. SAP), CRM systems (e.g. Salesforce.com) and many more.

The Twitter example is simply used since the Twitter APIs are publicly accessible and therefore convenient to use as an example. The SDK also contains a basic ISO 8583 example in the apps directory. For more information see Appendix III.

Setting up your Application on the RAMP Deployment Platform

Before you begin, you'll need to register at https://admin.ramp.virtualmobiletech.com/d/app/register.

After registering you will be emailed a link to activate your account. You'll need to change your password. Your password must contain at least 8 characters.

Once you've activated your account, you can begin to create applications on the deployment platform. Select "New application"

You'll need to specify a name and description for the application.

You can leave the other defaults as they are. Two that you might want to change, though, are the application's icon and you might want to set a service server. The service server is the RAMP server on which you'll be running your web services. Unless you've installed the RAMP server components on a server on the internet, you can test using a phone emulator and server on your own machine (address: http://127.0.0.1:8080/ramp).

Note

One of the applications created in this guide uses a web service. The web service is already hosted on the default RAMP server so it is not necessary for you to change the service server unless you want to set up the service on your own machine.

Setting up a RAMP Project in Eclipse

Downloading and Installing Eclipse

If you haven't downloaded Eclipse yet, you can download Eclipse Kepler for Java Developers from https://eclipse.org/downloads/packages/eclipse-ide-java-developers/keplersr2. We strongly recommend that you use this version and package of Eclipse. The other available Eclipse download packages may not work correctly.

If you have already downloaded Eclipse, make sure that you are using either Eclipse Juno or Eclipse Kepler for Java Developers.

Installing the RAMP Plug-in for Eclipse

To develop RAMP applications with Eclipse, you need to install the RAMP plug-in. Start up Eclipse. A "workspace launcher" dialog may appear asking you to select a workspace, choose any directory that you want to use. If a "welcome screen" appears, dismiss it by clicking the close button.

To install the plug-in, go to Help>Install new software....

Click on 'Available software sites', select "Add..." and specify the location as http://download.eclipse.org/modeling/tmf/updates/releases

Select "OK" on the Add Site dialog, and then "OK" on the Preferences dialog. This should return you to the Install dialog.

This ensures that eclipse can locate libraries that the RAMP plug-in requires.

Now select "Add...". Specify the name of the plug-in as 'ramp' and the location as http://resources.virtualmobiletech.com/rampide/2.2.x

After Eclipse has located the RAMP update site, select the RAMP IDE plug-in

Click "Next...". The installation starts by checking what software the plug-in needs and, possibly, fetching it from the internet. This can take several minutes. When the dependencies have been resolved, click "Finish". You will be asked whether you want to restart Eclipse. Say "Yes". Wait for Eclipse to restart and you're ready to create your first project. You will need to accept the RAMP licensing terms as part of the installation.

Creating a RAMP Project

Once the Eclipse workbench has opened, Select File>New>Project

The Eclipse "New Project" wizard allows you to create different project types. One of the types should be "RAMP project" (scroll down if necessary) as in the next figure. If you don't see an option to create a RAMP project, shut down Eclipse and check that you followed the installation instructions correctly.

If that all worked, you're ready to create your first RAMP application!

"Hello, World"

Start up Eclipse (if it isn't already running) and select to create a new RAMP project. You'll see the "RAMP Project" wizard. You need to give the project a name, give it the name "helloworld" or whatever name you selected when you created the application on the deployment platform. Click "Finish".

The plugin creates the "helloworld" project in the Eclipse workspace. If you expand the project, you see a number of directories: bin, css, images, src and uxml.

Creating a UXML Form

We'll start by creating a UXML form component. You do this by selecting File>New>Other.... A dialog appears asking you to select a wizard. Scroll down to “RAMP” and select “UXML Component”.

The "New UXML Component" wizard needs you to specify a component type and an ID. Select "Form" for the type and enter "mainform" as the ID

Click "Finish". The "mainform.xml" file is added to the UXML folder in the project and opened in Eclipse's editor window. Eclipse allows XML files to be viewed in a "design" or "source" format. We'll use the source format, click on the "source" tab to see the XML as text.

The plugin creates a default form with:

  • A form heading
  • a label
  • a text input box
  • a button

You don't need the text input box. Delete it.

The label of the form heading shouldn't be empty; set the value of the label to “Hello”.

The text attribute of the label should be “Hello, world”.

The button's label is “OK”; change it to “Exit” and set the value of the click attribute to “exit()”. Notice that the click attribute is, "exit()" (with parentheses), it is NOT "exit" (without parentheses). The click event "exit()" invokes a function while "exit" would try to change the displayed form to a form with id = "exit".

Your XML should now be

mainform
<Form id="mainform" stylename="" backkey="back()" 
		xmlns="http://virtualmobiletech.com/ramp/uxml" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_5.0.0.xsd">
	<FormHeading label="Hello" stylename=""/>
	<Label text="Hello, world" stylename=""/>
	<Button label="Exit" click="exit()"/>
</Form>

Save the changed file.

Deploy the Application

We now have a very minimalistic and valid RAMP application. You can deploy it to your project space on the RAMP deployment server. Right click on the RAMP project in Eclipse and select "Upload RAMP feature..."

You need to specify your username, password, company ID and application ID. The company ID is the company name that you provided when registering. The application ID is the name of the application that you selected when you created a new application on the deployment platform. Unless you're running your own RAMP server (which is unlikely at this stage), there is no need to change the URL for the web service endpoint.

Downloading and Running the Application

Once the application has been uploaded, you can download it to an emulator or, even, to a real phone. The deployment platform provides you with a link to download the microemu emulator. To download the emulator, log in to the deployment platform and select the application that you want an emulator for. You'll see a download emulator link.

The downloaded file is called "emulator.zip". Unzip the contents to some directory. Run the file called start.bat (or start.sh if you're running Linux).

You should see something similar to this

While it's nice that the application displays what we might expect, the visual effect is not very exciting: it's time to apply some styling and put an image on the display.

Applying Styling

If you look back at the XML for the "mainform" component, you'll see that many of the components have an attribute "stylename". We'll populate those attributes.

We'll start by changing the style of the form heading. In Eclipse, specify that you want to create a "New UXML Style" (New>Other...>RAMP>UXML Style from the "File" menu).

Specify that you want to create a new "FormHeading" style and give it the identifier "formheadingstyle".

A CSS file, "formheadingstyle.css", is opened in the editor window when you click "Finish". Some default style attributes are set. Notice that colours are specified as hexadecimal RGB values.

The color attribute sets the background colour of the form heading; change the colour from 0x0000FF (blue) to 0x00FF00 (green). Change the font-color attribute from 0xFFFFFF (white) to 0x0000FF (blue) and add a new attribute "font-style" and set its value to "bold". Save the file.

The form heading in the "mainform.xml" file should now be associated with this new style. Set the value of the FormHeading stylename attribute to "formheadingstyle".

Uploading the new feature and running it produces a slightly more exciting addition to the universe of "Hello, world" applications

Next we added styling to change the background colour, menu colours and menu font size in the formstyle CSS (we used a gradient fill). We changed the colour of the label text from blue to black in the labelstyle CSS. You can examine the changes in the helloworld example in the SDK distribution (the apps/client/helloworld/css directory).

Since a picture says a thousand words and mobile phone displays are too small for a thousand words, we added an image. We copied a PNG image of the earth (“earth.png”) into the images directory of our project and added a Picture element to our Form. You can view the resultant code in the distribution. The XML of the mainform.xml component is now

mainform
<Form id="mainform" stylename="formstyle" backkey="back()"
		xmlns="http://virtualmobiletech.com/ramp/uxml" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_5.0.0.xsd">
	<FormHeading label="Hello" stylename="formheadingstyle"/>
	<Label text="Hello, world" stylename="labelstyle"/>
	<Picture image="earth"/>
	<Button label="Exit" click="exit()"/>
</Form>

And the final result is...

Now for something more substantial!

"Goodbye, World! Hello, Twitterverse!"

The twitter client

In this section we start creating a more substantial application: a Twitter mobile application. In the previous section we saw the UXML Form component and some examples of CSS. In this section, we'll look at other UXML components and start writing some RAMPScript to add a behavioural component to our applications.

Creating the RAMP Application and Project

As in the previous example, the first thing that you need to do is to create a RAMP application and Eclipse project.

On the deployment platform create a new application and give it a name and description. The twitter web service that handles server-side communication with the Twitter API is running on the default RAMP server. If you are going to make use of this, you can leave the other fields as they are. If you plan on building and hosting your own twitter web service (which is a good exercise if you plan on integrating with web services in future projects), you should click Add service server under the Service servers section and enter the URL where your RAMP server will be deployed. You should then remove the Service server that was there by default.

Now create the RAMP project in Eclipse (called "Twi") as in the previous section: New>Project>RAMP Project. Once it's created, you can (if you don't want to do a lot of typing) copy the contents of the Twi project from the SDK (apps/client/twi) into your Eclipse workspace folder and refresh the workspace if necessary.

Adding a Form to the Project

In this section, we'll create a form that provides a user with the following options:

  • To tweet (post a message)
  • To view their friends' tweets (their timeline)
  • To exit the application

To create a form, select New>Other>UXML Component and select "Form" like in the previous section. Give the form an ID of "mainmenu".

Edit the form by adding three clickable HorizontalLayouts each with a Label and Picture as children:

mainmenu Form
<Form id="mainmenu" stylename="formstyle" backkey="exit()"
		xmlns="http://virtualmobiletech.com/ramp/uxml" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_5.0.0.xsd">
	
	<FormHeading label="Menu" stylename="formheadingstyle"/>
	
	<HorizontalLayout ratio="5:1" click="tweetSelected()" stylename="itemstyle">
	    <Label text="Tweet" stylename="labelstyle"/>
	    <Picture image="iconArrow" stylename="picstyle"/>
	</HorizontalLayout>
	
	<HorizontalLayout ratio="5:1" click="timelineSelected()" stylename="itemstyle">
	    <Label text="View" stylename="labelstyle"/>
	    <Picture image="iconArrow" stylename="picstyle"/>
	</HorizontalLayout>
	
	<HorizontalLayout ratio="5:1" click="exit()" stylename="itemstyle">
	    <Label text="Exit" stylename="labelstyle"/>
	    <Picture image="iconArrow" stylename="picstyle"/>
	</HorizontalLayout>
	
	<Button />
	<Button label="Exit" click="exit()"/>
</Form>

The HorizontalLayouts call tweetSelected(), timelineSelected() and exit() respectively when clicked. The exit() function is a built-in function which exits the app. The others, tweetSelected() and timelineSelected(), are functions which we're going to define and write the code for. First though, we'll look at adding a splash screen.

Adding a Splash Screen

An application has at most one splash screen and, if present, should be the first screen that a user sees. The splash screen is the ideal opportunity to provide branding.

Select New>Other>UXML Component and select "Splash" and give it the ID "splash". Edit the component so that the image attribute has the value “twimage” (a file "twimage.png" must be copied into the images directory of your project) and specify that the "click" attribute has the value "mainmenu".

A splash screen displays its contents for (a default value of) 5 seconds or until the user presses a key. After clearing the screen, the click event is processed; the click event must specify either a RAMPScript function to invoke or a UXML component that must be displayed next. Note: The timeout value can be changed by supplying a "timeout" attribute with a value in milliseconds (e.g. 10000 would increase the display time to 10 seconds).

Splash
<Splash id="splash" color="0xFFFFFF" image="twimage" click="mainmenu" 
		xmlns="http://virtualmobiletech.com/ramp/uxml" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_5.0.0.xsd"/>

Notice that click event has the value "mainmenu" (without parentheses). Since there are no parentheses, "mainmenu" must be the ID of a UXML component, not the name of a RAMPScript function. When the splash screen is clicked, the next form displayed will be "mainmenu".

Adding Error Handling

Debugging an application is never easy but it can be made less difficult if you have code that can, at least, report errors to you. Writing your error handling code early is a good habit to get into.

We create a UXML form ("errorform") for displaying errors.

errorform
<Form id="errorform" stylename="formstyle" backkey="back()"
		xmlns="http://virtualmobiletech.com/ramp/uxml" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_5.0.0.xsd">
	<FormHeading label="Error" stylename="formheadingstyle"/>
	<Label id="errortext" text="" stylename="labelstyle"/>
	<Button label="OK" click="mainmenu"/>
</Form>

Notice that we gave the label an ID: "errortext". We'll use this ID to update the text on the form using RAMPScript.

When an unexpected error occurs, the RAMP VM tries to see whether a RAMPScript function called "error" exists. If such a function exists, it is invoked.

To create the error function, you need to add a RAMPScript source file to the project (New>Other>RAMPScript source file). Give the file the name "errorhandler". The file "errorhandler.rs" is created and added to the "src" folder of your project.

You can now start adding source code. Add the following function to errorform.rs and save the file

errorhandler.rs
function error(errorMessage) {
	errortext.text = errorMessage;
	goto("errorform");
}

Notice how we set the value of the text attribute of the errortext label. This is how RAMPScript interacts with UXML.

You can upload (right click on project -> Upload RAMP feature) the project now, and download the emulator from the deployment platform and run it. After the splash screen has been cleared, you're presented with the "mainmenu" form with its various options. If you click on the "Tweet" option, the screen changes to the error form reporting that the function "tweetSelected()" could not be found. Without our error handling code we would not have got any feedback on the device when something went wrong. You might also have noticed network activity before the error was displayed: any errors that occur on the device are also logged by the RAMP VM at the RAMP Server so that they can be investigated.

Defining RAMP Script functions

Let's fix the previous problem and write the tweetSelected() function. Create a new RAMPScript source file called "mainhandler.rs" and type in the following code.

mainhandler.rs
function tweetSelected()
{
	goto("tweet");
}

For now this function will go to another form called "tweet".

Styling the Forms

The "mainmenu" and "errorform" forms can be styled to make them more visually appealing. This is already done in the distributed application, so you can examine them there to learn more.

The Forms are styled using formstyle.css, the FormHeading with formheadingstyle.css, the Labels with labelstyle.css, the arrow Pictures with picstyle.css and the HorizontalLayouts with itemstyle.css. See the following guides for more information on styling: UXML Styling and Smart UXML Styling.

Authenticating

The first option in the menu is an option to tweet. A user should be authenticated before being allowed access to a specific twitter account. Since 31 August 2010, Twitter requires OAuth authentication from 3rd party applications. For further information on Twitter's OAuth authentication see their documentation.

In this section we create a form that informs a user that he needs to allow the application access to his twitter account, the user's browser is then launched for authentication at twitter. After allowing access to the application the user then enters his authentication PIN supplied by twitter.

We first create a form that explains the process to the user.

setupform
<Form id="setupform" stylename="formstyle" backkey="back()"
		xmlns="http://virtualmobiletech.com/ramp/uxml"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_5.0.0.xsd">
	<FormHeading label="Setup twi" stylename="formheadingstyle" />
	<Label text="You need to allow twi access to your Twitter account before continuing. Pressing OK will launch your browser and direct you to Twitter, where you will be asked to allow access to twi, remember the PIN that will be shown, you will need it in the next step!" stylename="labelstyle"/>
	<Button label="OK" click="setup()"/>
	<Button label="Cancel" click="back()"/>
</Form>

We have two buttons on this form, "OK" and "Cancel". When the "Cancel" button is pressed, the function back() is called; this is a native function where the RAMP VM takes the user back to the previous screen.

Unfortunately, we don't get the setup() function associated with the OK button for free. We created a RAMPScript source file, setuphandler.rs, and wrote the setup function:

setuphandler.rs
function setup()
{
  //Get twitter request token from RAMP Server
  response = webService("requestTokenHandler");
  authUrl = mapGet(response,"authUrl");
  @requestToken = mapGet(response,"requestToken");
  @requestSecret = mapGet(response,"requestSecret");
  openUrl(authUrl);
  pinform.message_label.text = "Your browser should have launched the twitter authentication request. Once you have authenticated you need to enter your twitter PIN below.";
  goto("pinform");
}

Notice the RAMP webService function, this allows the RAMP VM to communicate with the RAMP server. In this case the RAMP VM is requesting a twitter request token from the RAMP server. The RAMP server responds with a Map containing the twitter authentication URL, request token, and request secret. The RAMP VM then launches the device's browser using the native function openUrl.

Notice the syntax for a single line comment; we don't generally clutter the code in this user guide with comments but it's not a bad habit to comment code.

Once the user has retrieved the PIN from Twitter, he can enter it on the pinform.

pinform
<Form id="pinform" stylename="formstyle" backkey="back()"
		xmlns="http://virtualmobiletech.com/ramp/uxml"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_5.0.0.xsd">
	<FormHeading label="Twitter PIN" stylename="formheadingstyle"/>
	<Label text="" id="message_label" stylename="labelstyle"/>
	<TextInput id="pin" text="" stylename="textinputstyle" numeric="true"/>
	<Button label="OK" click="processPin()"/>
	<Button label="Cancel" click="back()"></Button>
</Form>

Notice, the use of the TextInput components. In particular, note how we set the "numeric" attribute to "true" for the PIN field to restrict input to be of numeric type.

Here is the processPin() code.

setuphandler.rs
function processPin()
{
  pin = pinform.pin.text;
	
  response = webService("accessTokenHandler","requestToken",@requestToken,"requestSecret",@requestSecret,"requestPin",pin);
	
  @accessToken = mapGet(response,"accessToken"); 
  @accessSecret = mapGet(response,"accessSecret");
	
  #accessToken = @accessToken;
  #accessSecret = @accessSecret;
	
  goto("mainmenu");
}

Notice how RAMPScript is able to get the values of the text input box using the UXML variables pinform.pin.text. Once the user's PIN is retrieved, the RAMP Server is again contacted to retrieve a Twitter access token and access secret. The access secret and token is stored both in persistence variables (#accessToken = ...) and global variables (@accessToken = ...).

This process is not secure since the user's OAuth access token and access secret is transmitted in the clear. Review the Advanced Guide to see how to secure communications between the RAMP VM and the RAMP server.

Finally, we must check that the user has setup his account before allowing him to tweet or get the timeline by modifying the tweetSelected() and timelineSelected() functions.

mainhandler.rs
function tweetSelected()
{
	checkSetup();
	tweet_input.text = "";
	goto("tweet");
}

function timelineSelected()
{
	checkSetup();
	getTimeline();
}

function checkSetup()
{
  	if(@accessToken == "")
	{
		goto("setupform");
	}
}

The RAMP/Twitter Web Service

The Twitter app, Twi, cannot exist entirely on the client: it is necessary to write server-side code to "talk" to the Twitter API. In this section we show how to develop a RAMP service that can translate requests from the RAMP VM into requests to the Twitter API and the responses from the Twitter API into responses that can be parsed by the RAMP VM. There are two tasks:

  • Get the RAMP server up and running
  • Write the web service and deploy it so that the RAMP server can access it.

Note

The twitter service is running on the default RAMP server. It is not necessary to read the rest of this section immediately if your project uses the default RAMP services server. You can jump ahead and start reading about writing client-side code and return to this section later if you want.

Installing the RAMP server

It is easy to get a minimal RAMP server up and running. For a step by step guide on installing a RAMPServer with Tomcat see Appendix I for Mac OS X, and Appendix II for Windows.

Here is a summary of the steps involved:

  • Ensure that you have a server that can run Java servlets (e.g. Tomcat, Jetty, Glassfish, etc.)
  • Copy the file "ramp.war" from the war directory of the RAMP distribution into the webapps directory of your server.
  • Create a directory "/etc/ramp" on the server machine (in Windows, the folder would be called "C:\etc\ramp", "D:\etc\ramp", etc depending on whether your web server is installed on your C:, D:, etc. hard disk).
  • Change directories to "/etc/ramp"
  • Copy the file "ramp-config.properties" from the "war" directory of the RAMP distribution to "/etc/ramp"
  • Create a directory "integration" (i.e. the full path will be "/etc/ramp/integration" or "C:\etc\ramp\integration")
  • Copy the RAMP integration jar from the "lib" directory of the RAMP distribution to the "integration" directory
  • Create a main-context.xml file in the "integration" directory with the following contents:
main-context.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"

	xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
       ">

   <!--
        You can regard this as boiler-plate.
        The advanced guide covers this in more detail.
     -->
   <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="locations" ref="propertyFiles"/>
   </bean>  
	
   <bean id="serviceRequestManager" class="com.vmt.ramp.integration.impl.ServiceRequestManagerImpl">
      <property name="mapper" ref="serviceRequestMapper" />
   </bean>

   <bean id="serviceRequestMapper" class="com.vmt.ramp.integration.impl.ServiceRequestMapper">
      <property name="handlers" ref="handlers" />
   </bean>

   <bean id="featureBundleProvider" class="com.vmt.ramp.integration.impl.MultiDirectoryFeatureBundleProviderImpl">
      <property name="rootDirectory" value="${scripts.dir}"/>
      <property name="signer" ref="featureSigner"/>
   </bean>

   <bean id="featureSigner" class="com.vmt.ramp.integration.impl.security.SoftwareSigner">
      <property name="rsaPrivateKeyDao" ref="rsaPrivateKeyDao"/>
   </bean>
   
   <bean id="featureVersionFilter" class="com.vmt.ramp.integration.impl.FeatureVersionAfterServiceRequestFilter">
      <property name="multiFBP" ref="featureBundleProvider"/>
   </bean>
   
   <bean id="rsaPrivateKeyDao" class="com.vmt.ramp.integration.impl.security.TestKeysDao" />
   
   
   <util:map id="handlers">
      
   </util:map>

</beans>

The RAMP server can now function as a feature server. For it to function as a service server, one or more handlers will need to be added to the main_context.xml file.

Adding Twitter Specific Properties

  • Edit the "ramp-config.properties" file and add two entries, remembering to replace the values between curly braces with the values retrieved from Twitter
    • consumer.key = {your.consumer.key}
    • consumer.secret = {your.consumer.secret}

Creating a RAMP Server Project

We'll assume that you're using Eclipse as your IDE for the server-side development as well. You can either create the project yourself (New>Project>Java Project) and give it the name "twiservice" or you can copy the Eclipse project from the distribution.

The project has dependencies on the RAMP integration libraries (found in the ramp-integration.jar) and on Twitter4J library (twitter4j-2.1.6.jar). Note that you might be able to use a more recent version of the Twitter4J library. Create a library folder (“lib”) in your project and copy these two JARs to the folder. Add the JARs to the project class path (by right-clicking the project, selecting Properties>Java Build Path and selecting the libraries tab and “Add JARs...”.)

To keep this document to a manageable length, we do not discuss writing unit or integration tests for our web services. The code in this section is illustrative and is definitely not in accordance with best practice.

Writing an Authentication Service

We write a class TwitterService that exposes generateRequestToken and generateAccessToken methods:

TwitterService.java
package twiservice.remote;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.http.AccessToken;
import twitter4j.http.RequestToken;

/**
 * An abstraction of the Twitter4J API that the service request handlers
 * interact with.
 * 
 */
public class TwitterService {
	
	private static final Logger logger = LoggerFactory
			.getLogger(TwitterService.class);
	
	private final String consumerKey;
	private final String consumerSecret;
	

	public TwitterService(String consumerKey, String consumerSecret) {
		super();
		this.consumerKey = consumerKey;
		this.consumerSecret = consumerSecret;
	}

        public RequestToken generateRequestToken() throws TwitterException
	{
		return getTwitterInstance().getOAuthRequestToken();
	}
	
	public AccessToken generateAccessToken(String requestToken,String requestTokenSecret,String pin) throws TwitterException
	{
		return getTwitterInstance().getOAuthAccessToken(requestToken, requestTokenSecret, pin);
	}

	protected Twitter getTwitterInstance()
	{
		if(logger.isDebugEnabled())
		  logger.debug(String.format("Instantiating Twitter 4j instance with consumerKey %s, consumerSecret %s",consumerKey,consumerSecret));
		
		return new TwitterFactory().getOAuthAuthorizedInstance(consumerKey, consumerSecret);
	}
	
	protected Twitter getTwitterInstance(String token,String tokenSecret)
	{
		return new TwitterFactory().getOAuthAuthorizedInstance(consumerKey, consumerSecret, new AccessToken(token, tokenSecret));
	}
}

The methods declare that they throw a TwitterException which is declared in the Twitter4J library; this means that our code is tied to the Twitter4J library and we cannot, e.g., replace it with the jtwitter or java-twitter libraries easily.

In a real application we would declare our own exception classes and wrap exceptions thrown by 3rd party libraries in our own exceptions to reduce the coupling between the services we write and our dependencies. We might also prefer to declare TwitterService to be an interface and then write a class TwitterServiceImpl that implements the interface (to facilitate unit-testing and TDD).

Writing a Service Request Handler

Classes that expose web services to the RAMP VM must implement the ServiceRequestHandler interface. The short piece of code below shows our RequestTokenHandler.

LoginHandler.java
package twiservice.integration;

import twiservice.remote.TwitterService;
import twitter4j.http.RequestToken;

import com.vmt.ramp.core.domain.ServiceRequest;
import com.vmt.ramp.core.domain.ServiceResponse;
import com.vmt.ramp.core.integration.ServiceRequestHandler;

public class RequestTokenHandler implements ServiceRequestHandler {
	
	private TwitterService twiService;

	public RequestTokenHandler(TwitterService twiService) {
		super();
		this.twiService = twiService;
	}

	@Override
	public ServiceResponse processRequest(ServiceRequest req) throws Exception {
		RequestToken requestToken = twiService.generateRequestToken();
		
		ServiceResponse res = new ServiceResponse();
		res.put("requestToken", requestToken.getToken());
		res.put("requestSecret", requestToken.getTokenSecret());
		res.put("authUrl",requestToken.getAuthorizationURL());
		
		return res;
	}

}

The above code is very simple: we get a request and we send back a response. In this case we retrieve a request token from twitter and pack it into a ServiceResponse that is then delivered to the RAMP VM.

Here is the code for the AccessTokenHandler.

LoginHandler.java
package twiservice.integration;

import twiservice.remote.TwitterService;
import twitter4j.http.AccessToken;

import com.vmt.ramp.core.domain.ServiceRequest;
import com.vmt.ramp.core.domain.ServiceResponse;
import com.vmt.ramp.core.integration.ServiceRequestHandler;

public class AccessTokenHandler implements ServiceRequestHandler {
	
	private TwitterService twiService;
	
	public AccessTokenHandler(TwitterService twiService) {
		super();
		this.twiService = twiService;
	}

	@Override
	public ServiceResponse processRequest(ServiceRequest req) throws Exception {
		String requestToken = (String)req.get("requestToken");
		String requestTokenSecret = (String) req.get("requestSecret");
		String requestPin = (String) req.get("requestPin");

		AccessToken accessToken = twiService.generateAccessToken(requestToken, requestTokenSecret, requestPin);
		
		ServiceResponse res = new ServiceResponse();
		res.put("accessToken", accessToken.getToken());
		res.put("accessSecret", accessToken.getTokenSecret());
		
		return res;
	}
}

The handler extracts the request token, request secret and request PIN that was sent to it by the RAMP VM. It then requests an access token from Twitter and packs it into the response to the RAMP VM.

Adding Handlers to the Integration Context File

The RAMP server expects RAMP services to specify their handlers in the main-context.xml file. Create a new main-context.xml file in a "integration_config" directory in your Eclipse project. This should be the same as the main-context.xml file in "/etc/ramp/integration", but this time handlers will be defined.

When the web service is deployed, this file will be copied to "/etc/ramp/integration", replacing the existing one. Here is the main-context.xml file with handlers defined:

main-context.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"

	xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
       ">

   <!--
        You can regard this as boiler-plate.
        The advanced guide covers this in more detail.
     -->
   <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="locations" ref="propertyFiles"/>
   </bean>  
	
   <bean id="serviceRequestManager" class="com.vmt.ramp.integration.impl.ServiceRequestManagerImpl">
      <property name="mapper" ref="serviceRequestMapper" />
   </bean>

   <bean id="serviceRequestMapper" class="com.vmt.ramp.integration.impl.ServiceRequestMapper">
      <property name="handlers" ref="handlers" />
   </bean>

   <bean id="featureBundleProvider" class="com.vmt.ramp.integration.impl.MultiDirectoryFeatureBundleProviderImpl">
      <property name="rootDirectory" value="${scripts.dir}"/>
      <property name="signer" ref="featureSigner"/>
   </bean>

   <bean id="featureSigner" class="com.vmt.ramp.integration.impl.security.SoftwareSigner">
      <property name="rsaPrivateKeyDao" ref="rsaPrivateKeyDao"/>
   </bean>
   
   <bean id="featureVersionFilter" class="com.vmt.ramp.integration.impl.FeatureVersionAfterServiceRequestFilter">
      <property name="multiFBP" ref="featureBundleProvider"/>
   </bean>
   
   <bean id="rsaPrivateKeyDao" class="com.vmt.ramp.integration.impl.security.TestKeysDao" />
   
   
   <!--
        This is the important stuff: Declare your services and the classes that 
        implement the handlers.
     -->
   <util:map id="handlers">
      <entry key="accessTokenHandler" value-ref="accessTokenHandler" />
      <entry key="requestTokenHandler" value-ref="requestTokenHandler" />
   </util:map>

   <bean id="accessTokenHandler" class="twiservice.integration.AccessTokenHandler">
   	<constructor-arg><ref bean="twitterService"/></constructor-arg>
   </bean>
   
   <bean id="requestTokenHandler" class="twiservice.integration.RequestTokenHandler">
   	<constructor-arg><ref bean="twitterService"/></constructor-arg>
   </bean>
   
   <bean id="twitterService" class="twiservice.remote.TwitterService">
   	 <constructor-arg><value>${consumer.key}</value></constructor-arg>
   	 <constructor-arg><value>${consumer.secret}</value></constructor-arg>
   </bean>

</beans>

The integration files can get far more complicated with more dependencies or if your services require JMX monitoring or if you're using aspects, etc.

Most of the above is boiler-plate (for now, at least) and can be copied in your own projects. Notice the ${consumer.key} and ${consumer.secret} values in the twitterService declaration, these will be resolved from your "ramp-config.properties" file.

Packaging and Deploying the Service

To deploy the web service, you'll need to package your service into a JAR and deploy the JAR and its dependencies (the ramp-integration JAR and, in this case, the twitter4J JAR) and main-context.xml into a directory.

You can package the application using whatever tool you like (Ant, Maven, your IDE or even the command line). We'll assume that you're using Ant and the sample distribution includes an Ant build.xml file that packages the library and copies it and its dependencies to the "plugin" directory. The script is short and if you're familiar with Ant, it can be instructive to look at it.

ant build
<?xml version="1.0" encoding="UTF-8"?>
<project name="RampExample" basedir="." default="plugin">
	<path id="plugin.class.path">
		<fileset dir="lib">
			<include name="**/*.jar"/>
		</fileset>
	</path>
	<target name="clean">
		<delete dir="build_plugin"/>
		<delete dir="plugin"/>
	</target>
	<target name="prepare">
		<mkdir dir="build_plugin"/>
		<mkdir dir="build_plugin/classes"/>
		<mkdir dir="plugin"/>
	</target>
	<target name="plugin" depends="clean,prepare">
		<javac srcdir="src" destdir="build_plugin/classes" debug="true">
			<classpath refid="plugin.class.path"/>
		</javac>
		<copy todir="plugin">
			<fileset dir="integration_config/" includes="**/*"/>
		</copy>
		<jar basedir="build_plugin/classes" destfile="plugin/twi_integration.jar"/>
		<copy includeemptydirs="false" todir="plugin">
			<fileset dir="lib">
				<include name="**/*.jar"/>
			</fileset>
		</copy>
		<copy includeemptydirs="false" todir="plugin">
			<fileset dir="integration_config">
				<include name="**/*.jar"/>
			</fileset>
		</copy>
	</target>
</project>

We'll assume that you've installed the RAMP server as specified in the previous section. Now you need to ensure that the RAMP server can locate your web service. This involves two steps:

  • After running the Ant build script, copy the files from the "plugin" directory of your project to the directory "/etc/ramp/integration" that you created.
  • Start (or restart) the web server.

Seeing if it Works

At this point you have the ServiceRequestHandlers deployed on a RAMP server. If you haven't done so already, upload the latest client feature from Eclipse by right clicking the RAMP project and selecting "Upload RAMP feature...".

Run the emulator and select “Tweet". The "Setup twi" form should be displayed. If you click OK, the emulator will display a message stating that it was asked to navigate to a URL. Ignore this for now and click OK. The "Twitter PIN" form should now be displayed. Enter an incorrect PIN (e.g. enter "1"). After a little while, the error form should appear with an error message that says something like "Internal ramp server error: 401:Authentication credentials were missing or incorrect.". This is the error thrown by the Twitter API and wrapped in a Twitter4J exception. While the error message is unattractive, we've seen something important: Errors thrown at the server are transparently transformed into errors at the client. Of course, it would be better if the exception were wrapped in a class of our own that would present the failed authentication information to the user in a less intimidating manner.

Now do a positive test: Perform the above process again and enter the correct PIN. Note that to obtain a correct PIN you will need a Twitter account and you will need to navigate to the URL that the Twitter API returned as the Authorization URL. The message that the emulator displayed earlier was informing us that the emulator was asked to open a browser and navigate to the URL, but this is not supported. You will therefore either have to enter the URL that was displayed into a desktop browser in order to obtain the OAuth PIN, or you will have to test on an emulator or device that has a browser. You can download the application to a device by going to http://dlramp.com/{Your company name}/{Your app name}, by substituting {Your company name} and {Your app name} with the relevant values.

Adding a Service Form

The previous code works but it lacks at least one important aspect: User feedback. The user has no way of knowing whether they are being logged in.

UXML has a component known as a ServiceForm. When a web service is invoked (synchronously), the RAMP client will display the ServiceForm.

Create a ServiceForm (from the New UXML Component menu).

ServiceForm
<ServiceForm stylename="formstyle" 
		xmlns="http://virtualmobiletech.com/ramp/uxml"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_5.0.0.xsd">
	<FormHeading label="Busy" stylename="formheadingstyle"/>
	<Label id="serviceform" text="Contacting web service..." stylename="labelstyle"/>
	<Spinner stylename="spinnerstyle"/>
	<Button label="Cancel" click="cancel()"/>
</ServiceForm>

You also need to style the Spinner. Create a new Spinner style called “spinnerstyle”:

Style
spinnerstyle {
	color: 0x0000FF;
}

If you try logging in now the ServiceForm is rendered when the client establishes a connection to the web service.

The Spinner component on the service form displays activity to the user so that they can see that "something" is happening.

You can also try pressing the Cancel button to see that the RAMP VM, by default, allows users to stop activities if they get impatient. If you don't want to allow users to cancel some operation, remove the "Cancel" button from the form.

Storing Data Persistently

It is frequently desirable to store data persistently so that information is retained by the RAMP application even after it has been shut down. An example of data that should be stored persistently is a user name. Users will get tired of an application if they have to type in their name every time that they run it.

RAMPScript has the concept of persistent and global variables.

  • Global variables are variables whose contents can be shared across RAMP functions. To declare a variable global, precede its name with an @, e.g. @foo is a global variable. Global variables are not retained if an application is shut down.
  • Persistent variables are variables whose contents is retained even after a RAMP application has been shut down. A persistent variable has a name preceded with a #, e.g. #bar is a persistent variable.

The processPin code persists the access token and access secret as well as store them in global variables.

setuphandler.rs
function processPin()
{
	pin = pinform.pin.text;
	
	response = webService("accessTokenHandler","requestToken",@requestToken,"requestSecret",@requestSecret,"requestPin",pin);
	
//Store in global variables
	@accessToken = mapGet(response,"accessToken");
	@accessSecret = mapGet(response,"accessSecret");
	
//Persist
	#accessToken = @accessToken; 
	#accessSecret = @accessSecret;
	
	goto("mainmenu");
}

If we successfully setup access to twitter then the access token and access secret will be retained next time we open the application. This is, however, not much use unless the global variables for access token and access secret is populated when the application starts up. This can be done in the RAMPScript main() function.

The RAMPScript main() Function

Any RAMP application can contain an optional main() function. If a main() function is present, the RAMP VM assumes that the function is responsible for all initialization of the application including specifying the first UXML component to be displayed. Add the following function to the mainhandler.rs source file:

mainhandler.rs
function main() {
//Load access token and access secret from persistence and store in global variables
	@accessToken = #accessToken;
	@accessSecret = #accessSecret;
	goto("splash");
}

It is far faster to access global variables than persistent variables, hence at application startup the access token and secret is assigned to global variables for faster access later.

Dynamic UXML

The "mainmenu" Form of our application is static:

  • It always contains three items
  • The items are always the same (although they don't need to be, it is possible to use RAMPScript to change the text dynamically)

In many scenarios we don't know how many items are needed to populate a list of items on a Form and what the text should be at the time of creating the UXML component. An example would be a Twitter timeline.

Dynamically Populating a Form

To dynamically populate a Form, first create a new UXML Form component and give it the ID "timeline". Edit the resultant code to be as follows:

timeline Form
<Form id="timeline" stylename="formstyle" backkey="back()"
		xmlns="http://virtualmobiletech.com/ramp/uxml" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_5.0.0.xsd">
	
	<FormHeading label="Timeline" stylename="formheadingstyle"/>
	
	<VerticalLayout id="container"/>
	
	<Button/>
	<Button label="Back" click="back()"/>
</Form>

Notice the VerticalLayout element with ID = "container". We will need to set the "childnodes" attribute of this element to populate the Form.

We'll assume that there is a RAMP service called "timelineHandler" that can send back the tweets of our Twitter friends. To get the tweets, we'll pass our access token, access secret and the maximum number of results that we want to display. We'll assume that we get sent back a list of the tweet authors and the messages (Twitter actually supplies far more data than just this including an ID for the message, whether the message was posted in reply to another message, the time that the message was posted, etc. but we want to keep things simple).

Create a source file timelinehandler.rs and create the following functions:

timelinehandler.rs
function getTimeline()
{
	response = webService("timelineHandler",
		"accessToken", @accessToken,
		"accessSecret", @accessSecret,
		"num_results", 10);
	
	updateAuthorList(mapGet(response, "authors"));
	
	@messages = mapGet(response, "messages");
	goto("timeline");
}

function updateAuthorList(authorList)
{
	childNodes = List();
	
	for (j = 0; j < listSize(authorList); j += 1)
	{
		author = listGet(authorList, j);
		listAdd(childNodes, clickableItem(author, "listItemClick('" ++ j ++ "')"));
	}
	
	timeline.container.childnodes = childNodes;
}
 
function clickableItem(text, click)
{
	hlChildnodes = List();
	
	listAdd(hlChildnodes, uxmlLabel("", text, "labelstyle"));
	listAdd(hlChildnodes, uxmlPicture("", "iconArrow", "picstyle"));
	
	return uxmlNewNode("horizontallayout", "", Map("stylename", "itemstyle", "ratio", "5:1", "click", click), hlChildnodes);
}

The most important line in the above code snippet shows how we process the response value returned by the web service call. The response is an instance of a Map; consequently we can use the mapGet() RAMP native function to get the authors of the most recent tweets in our timeline. We pass the value associated with the "authors" key in the map to the updateAuthorList() function. This function traverses the list and constructs UXML childnodes which it then assigns to the "timeline.container.childnodes" UXML attribute. After populating the container, we display the Form by means of a goto() call.

Writing a TimelineHandler Service

Since the client code assumes that there's a timelineHandler service, we'd better write it.

We start by extending our TwitterService class to provide a method to get the timeline of the people that we follow.

TwitterService.java
package twiservice.remote;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.http.AccessToken;
import twitter4j.http.RequestToken;

/**
 * An abstraction of the Twitter4J API that the service request handlers
 * interact with.
 * 
 */
public class TwitterService {
	
	private static final Logger logger = LoggerFactory
			.getLogger(TwitterService.class);
	
	private final String consumerKey;
	private final String consumerSecret;
	

	public TwitterService(String consumerKey, String consumerSecret) {
		super();
		this.consumerKey = consumerKey;
		this.consumerSecret = consumerSecret;
	}

        public RequestToken generateRequestToken() throws TwitterException
	{
		return getTwitterInstance().getOAuthRequestToken();
	}
	
	public AccessToken generateAccessToken(String requestToken,String requestTokenSecret,String pin) throws TwitterException
	{
		return getTwitterInstance().getOAuthAccessToken(requestToken, requestTokenSecret, pin);
	}

       public List<Status> getTweets(String token, String tokenSecret) throws TwitterException 
       {
		Twitter twitter = getTwitterInstance(token, tokenSecret);
		return twitter.getFriendsTimeline();
	}

	protected Twitter getTwitterInstance()
	{
		if(logger.isDebugEnabled())
		 logger.debug(String.format("Instantiating Twitter 4j instance with consumerKey %s, consumerSecret %s",consumerKey,consumerSecret));
		
		return new TwitterFactory().getOAuthAuthorizedInstance(consumerKey, consumerSecret);
	}
	
	protected Twitter getTwitterInstance(String token,String tokenSecret)
	{
		return new TwitterFactory().getOAuthAuthorizedInstance(consumerKey, consumerSecret, new AccessToken(token, tokenSecret));
	}
}

Again, the above code is less than ideal: we've now got code that's coupled to the twitter4j Status class. Improving it is left as an exercise for the reader.

We can now write a ServiceRequestHandler that translates the request from the client into a form suitable for the TwitterService and repackages the response into a form suitable for parsing by the RAMP VM.

TimelineHandler.java
package twiservice.integration;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import twiservice.remote.TwitterService;
import twitter4j.Status;
import com.vmt.ramp.core.domain.ServiceRequest;
import com.vmt.ramp.core.domain.ServiceResponse;
import com.vmt.ramp.core.integration.ServiceRequestHandler;

/**
 * A handler that allows a user to see her timeline.
 * 
 */

public class TimelineHandler implements ServiceRequestHandler {

	private TwitterService service;

	public TimelineHandler(TwitterService service) {
		super();
		this.service = service;
	}

	@Override
	public ServiceResponse processRequest(ServiceRequest req)
			throws Exception {
		String accessToken = (String) req.get("accessToken");
		String accessSecret = (String) req.get("accessSecret");
		int maxTweets = Integer.parseInt((String) req.get("num_results"));

		// Get the tweets in the user's timeline.
		List<Status> tweets = service.getTweets(accessToken, accessSecret);
		int numTweets = maxTweets > tweets.size() ? tweets.size() : maxTweets;

		// The Status class cannot be serialized to the RAMP client.
		// We need to create Lists of objects that can be serialized.
		// Strings can be serialized.

		List<String> authors = new ArrayList<String>();
		List<String> messages = new ArrayList<String>();

		for (int i = 0; i < numTweets; i++) {
			Status tweet = tweets.get(i);
			authors.add(tweet.getUser().getName());
			messages.add(tweet.getText());
		}

		// Send the list of authors and tweets as two separate lists.
		ServiceResponse resp = new ServiceResponse();
		resp.put("authors", authors);
		resp.put("messages", messages);
		return resp;
	}
}

There are two possible "gotchas" in the above code:

  • The “num_results” value is received by the server as a String (not an Integer or an int). The RAMP VM supports only exchanging the following types with the RAMP server: Strings, Lists, Maps and one- and two-dimensional byte arrays. If you try to send any other data type (e.g. a Twitter4J Status object or even an Integer or any other class in the java.lang package to the RAMP VM, the RAMP server will throw an IOException).
  • We put the authors of the tweets into a List and the text of the tweets into another list. Since these are List<String> objects and the RAMP VM supports Lists and Strings, they can be safely sent back to the client.

We can now register the timelineHandler in the main-context.xml file.

main-context.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"

	xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
       ">

   <!--
        You can regard this as boiler-plate.
        The advanced guide covers this in more detail.
     -->
   <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="locations" ref="propertyFiles"/>
   </bean>  
	
   <bean id="serviceRequestManager" class="com.vmt.ramp.integration.impl.ServiceRequestManagerImpl">
      <property name="mapper" ref="serviceRequestMapper" />
   </bean>

   <bean id="serviceRequestMapper" class="com.vmt.ramp.integration.impl.ServiceRequestMapper">
      <property name="handlers" ref="handlers" />
   </bean>

   <bean id="featureBundleProvider" class="com.vmt.ramp.integration.impl.MultiDirectoryFeatureBundleProviderImpl">
      <property name="rootDirectory" value="${scripts.dir}"/>
      <property name="signer" ref="featureSigner"/>
   </bean>

   <bean id="featureSigner" class="com.vmt.ramp.integration.impl.security.SoftwareSigner">
      <property name="rsaPrivateKeyDao" ref="rsaPrivateKeyDao"/>
   </bean>
   
   <bean id="featureVersionFilter" class="com.vmt.ramp.integration.impl.FeatureVersionAfterServiceRequestFilter">
      <property name="multiFBP" ref="featureBundleProvider"/>
   </bean>
   
   <bean id="rsaPrivateKeyDao" class="com.vmt.ramp.integration.impl.security.TestKeysDao" />
   
   
   <!--
        This is the important stuff: Declare your services and the classes that 
        implement the handlers.
     -->
   <util:map id="handlers">
      <entry key="timelineHandler" value-ref="timelineHandler" />
      <entry key="accessTokenHandler" value-ref="accessTokenHandler" />
      <entry key="requestTokenHandler" value-ref="requestTokenHandler" />
   </util:map>

   <bean id="timelineHandler" class="twiservice.integration.TimelineHandler">
   	<constructor-arg><ref bean="twitterService"/></constructor-arg>
   </bean>
   
   <bean id="accessTokenHandler" class="twiservice.integration.AccessTokenHandler">
   	<constructor-arg><ref bean="twitterService"/></constructor-arg>
   </bean>
   
   <bean id="requestTokenHandler" class="twiservice.integration.RequestTokenHandler">
   	<constructor-arg><ref bean="twitterService"/></constructor-arg>
   </bean>
   
   <bean id="twitterService" class="twiservice.remote.TwitterService">
   	 <constructor-arg><value>${consumer.key}</value></constructor-arg>
   	 <constructor-arg><value>${consumer.secret}</value></constructor-arg>
   </bean>

</beans>

The services can now be rebuilt (by running our Ant script) and deployed on the web server. After shutting down and restarting the web server we can view the authors of the ten most recent tweets on the Twitter client.

Processing Item Selections

If we click on one of the items an error occurs since a "listItemClick(item)" function is getting called and we haven't defined it yet.

We need to write the "listItemClick(item)" function, but first we need to create a UXML form, message.xml, for displaying the message.

message Form
<Form id="message" stylename="formstyle" backkey="back()"
		xmlns="http://virtualmobiletech.com/ramp/uxml" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_5.0.0.xsd">
	<FormHeading label="Message" stylename="formheadingstyle"/>
	
	<VerticalLayout stylename="borderstyle">
		<Label id="message_label" text="" stylename="labelstyle"/>
	</VerticalLayout>
	
	<Button label="OK" click="back()"/>
</Form>

We assigned an ID ("message_label") to the Label on the form. We can now write the "listItemClick(item)" function. In the getTimeline() function we stored the messages sent from the server in a global variable (@messages). We can now retrieve the message from the correct item position in this list using the listGet() function. We change the text on the message form by assigning the message to the text field of the message_label component.

timelinehandler.rs
// At the bottom of timelinehandler.rs file:
 
function listItemClick(item)
{
	message.message_label.text = listGet(@messages, item);
	goto("message");
}

Finishing off the Twitter Application Functionality

While the Twi application is not a production-grade application, it would be a pity if it didn't include the ability to post tweets. To allow a user to post a twitter message, we added a "tweet" form. At this stage, most of the contents of the form below should be familiar. The exception is the “maxchars” attribute on the TextInput component. Since a tweet cannot contain more than 140 characters, we make it impossible for a user to type more than 140 characters.

tweet Form
<Form id="tweet" stylename="formstyle" backkey="back()"
		xmlns="http://virtualmobiletech.com/ramp/uxml" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_5.0.0.xsd">
	<FormHeading label="Tweet" stylename="formheadingstyle"/>
	<Label text="Your tweet" stylename="labelstyle"/>
	<TextInput id="tweet_input" text="" stylename="textinputstyle" maxchars="140"/>
	<Button label="OK" click="tweet()"/>
	<Button label="Back" click="back()"/>
</Form>

We need to add a tweet() function declaration in the tweethandler.rs source file as follows:

tweethandler.rs
function tweet() {
	if (tweet_input.text == "") {
		error("You didn't type anything.");
	}
	webService("tweetHandler",
		"accessToken", @accessToken,
		"accessSecret", @accessSecret,
		"message", tweet_input.text);
	goto("mainmenu");
}

We also need to add a handler on the server for handling tweet requests and a corresponding method to the TwitterService class. The TweetHandler class should be registered in the main-context.xml file. Look at the supplied source code to see the details.

Making the User Interface more Visually Appealing on Smart Phones

The Twitter application now supports the basic functions and for the purposes of this document can be considered functionally complete. It will run on a wide variety of devices, including smart phones like Android and Apple iPhone and iPad.

However, the capability of smart phones gives application developers the freedom to develop much more visually appealing user interfaces in their applications. For this reason RAMP allows one to split the user interface which is used for feature (regular) phones and for smart phones in order to harness the UI richness which smart phones have to offer, while at the same time not overburdening feature phones with unnecessarily complex UI.

The UI can also be split on a finer granularity, if desired. For instance one could make use of this if wanting to create a fuller UI on tablets like the Apple iPad that offer more screen real estate.

In this section we'll scratch the surface of what RAMP has to offer in terms of smart phone specific components and create a slightly richer user interface in the Twitter application for smart phones.

Splitting the User Interface

To create a smart phone specific user interface, one needs to create folders with a "_smart" suffix. For example, one would create a folder named uxml_smart for any smart phone specific UXML that is required. When building the smart phone feature, the files from the uxml_smart folder will be used, as well as any files that appear in the uxml folder that don't have the same name as any in the uxml_smart folder. This means that all the existing UI in the feature is inherited by the smart phone specific feature and one can choose where to override and/or add to it. When building the generic feature, only the uxml folder will be used, exactly the same as previously.

If custom RAMPScript, CSS and/or images are also required, src_smart, css_smart and/or images_smart folders should be created. The inheritance behaviour applies to these folders in exactly the same way as the uxml_smart folder.

To split the user interface on a finer granularity, one can use more specific folder suffixes. A "_smart_tablet" suffix will target tablet devices such as the Apple iPad or the Samsung Galaxy Tab. To target specific device makes and models one can append "_{device make}" or "_{device make}_{device model}" to either the "_smart" or the "_smart_tablet" suffix. E.g. the "_smart_tablet_samsung" suffix will target Samsung tablets, while "_smart_ios" will target all Apple iPhones.

For this example, we'll just split the UI into a feature phone and smart phone version. Start by creating the uxml_smart folder in Eclipse by right clicking the project and selecting New>Folder and entering "uxml_smart".

For the smart phone specific UI we'll be replacing the mainmenu, timeline and tweet components with SmartForm UXML components. Right click the uxml_smart folder, select New>Other>RAMP>UXML Component. Enter "mainmenu" as the name, and select the SmartForm radio button.

The generated file should look like this:

mainmenu SmartForm
<SmartForm id="mainmenu" stylename="" backkey="back()"
		xmlns="http://virtualmobiletech.com/ramp/uxml"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_smart_5.0.0.xsd">
	<VerticalLayout expand="true">
		<NavigationBar text="" stylename=""/>
		<VerticalScrollView expand="true">
			<Label text="" stylename=""/>
			<TextInput id="" text="" stylename=""/>
		</VerticalScrollView>
		<NavigationBar stylename="">
			<NavigationBarButton text="OK" click="" stylename="" layout="right"/>
		</NavigationBar>
	</VerticalLayout>
</SmartForm>

The SmartForm template that is created by the wizard is roughly equivalent in representation to the Form template. It has a heading at the top, a navigation bar at the bottom and scrollable contents in the centre. It is however much more flexible. For example, since the heading is now represented by a NavigationBar, it can include buttons and isn't restricted to being at the top of the screen.

For a description of the components consult the Smart Phone UXML Component guide.

The existing mainmenu component is a UXML menu with 3 options, Tweet, View and Exit. To make this more visually appealing on smart phones, we'll place two SmartButtons on the SmartForm, one for Tweet and one for View, and we'll make a NavigationBarButton for the Exit option which we'll place on the left of the top navigation bar.

After doing this the SmartForm should look like this:

mainmenu SmartForm
<SmartForm id="mainmenu" stylename="formstyle" backkey="exit()"
		xmlns="http://virtualmobiletech.com/ramp/uxml"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_smart_5.0.0.xsd">
	<VerticalLayout expand="true">
		<NavigationBar text="Menu" stylename="topnavbarstyle">
			<NavigationBarButton text="Exit" click="exit()" stylename="navbarbuttonstyle"/>
		</NavigationBar>
		<VerticalScrollView expand="true">
			<SmartButton text="Tweet" click="tweetSelected()" stylename="smartbuttonstyle"/>
			<SmartButton text="View" click="timelineSelected()" stylename="smartbuttonstyle"/>
		</VerticalScrollView>
	</VerticalLayout>
</SmartForm>

Note that the NavigationBar at the bottom was removed since it wasn't needed. Also, styles named topnavbarstyle, navbarbuttonstyle and smartbuttonstyle were made use of. These styles should be added to a folder named css_smart, since they are used only by smart phone UXML. You can create these from scratch or copy them from the css_smart folder in the example project. You'll notice that some new style attributes have been introduced into the smart phone specific styling, e.g. a "pressed" background, font and border color can be styled. Consult the Smart Phone UXML Styling guide to see which attributes are stylable for which components.

In order to test the smart phone specific changes, you could run the application on the Android emulator or the iPhone simulator.

Here is an image of the mainmenu screen before (left) and after (right) the smart phone changes, on the Android emulator.

BeforeAfter

 

And here is an image of the mainmenu screen before (left) and after (right) the smart phone changes, on the iPhone simulator.

BeforeAfter

 

 

If you look closely, you'll see that the components differ very slightly on the different devices, even though the styling is the same. This is because the components have been designed to conform to the device specific look-and-feel while still being stylable. For example an iPhone app should have the look-and-feel of an iPhone app, otherwise users might be disconcerted by it.

Now let's move on to modifying the timeline and tweet UXML components.

For the timeline SmartForm, we have a number of options. Since the "container" component is dynamically populated we could use the normal form and still populate it with different components by changing the clickableItem function for the smart phone version. Or we could change the "container" to a ListView and populate it with ListViewItems or ListViewContainerItems. This approach is commented out in the example project if you want to try it. We'll demonstrate how to replace it with a VerticalScrollView and populate it with SmartButtons.

timeline SmartForm
<SmartForm id="timeline" stylename="formstyle" backkey="back()"
		xmlns="http://virtualmobiletech.com/ramp/uxml"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_smart_5.0.0.xsd">
	<VerticalLayout expand="true">
		<NavigationBar text="Timeline" stylename="topnavbarstyle"/>
		
		<VerticalScrollView id="container" expand="true"/>
		
		<NavigationBar stylename="navbarstyle">
			<NavigationBarButton text="Back" click="back()" stylename="navbarbuttonstyle" layout="left"/>
		</NavigationBar>
	</VerticalLayout>
</SmartForm>

Finally, for the tweet SmartForm we'll add a 'placeholder' attribute to the TextInput, which will be displayed when the TextInput is empty. This is useful for providing a hint to the user as to what should be typed.

tweet SmartForm
<SmartForm id="tweet" stylename="formstyle" backkey="back()"
		xmlns="http://virtualmobiletech.com/ramp/uxml"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://virtualmobiletech.com/ramp/uxml http://resources.virtualmobiletech.com/rampide/uxml_smart_5.0.0.xsd">
	<VerticalLayout expand="true">
		<NavigationBar text="Tweet" stylename="topnavbarstyle"/>
		<VerticalScrollView expand="true">
			<Label text="Your tweet" stylename="labelstyle"/>
			<TextInput id="tweet_input" text="" stylename="textinputstyle" maxchars="140" placeholder="Type tweet here"/>
		</VerticalScrollView>
		<NavigationBar stylename="navbarstyle">
			<NavigationBarButton text="Back" click="back()" stylename="navbarbuttonstyle" layout="left"/>
			<NavigationBarButton text="OK" click="tweet()" stylename="navbarbuttonstyle" layout="right"/>
		</NavigationBar>
	</VerticalLayout>
</SmartForm>

Here's a comparison of what these screens look like before and after the changes. These are taken from the iPhone simulator. They will look much the same on other smart phone devices and emulators.

BeforeAfter

 

 

Refactoring RAMPScript to Support the Split User Interface

Because some of the RAMPScript in the project interacts with the user interface that has changed, we need to create a src_smart folder which will contain the smart phone specific RAMPScript. But first, to ensure that the RAMPScript that will be shared between the generic and smart phone features doesn't get unnecessarily duplicated, we'll refactor the files in the src folder by separating the application logic code from the user interface interaction code.

Create a new RAMPScript file in the src folder called uihandler. Now move the clickableItem function out of timelinehandler.rs and into uihandler.rs.

The generic RAMPScript is now refactored. There is no hard-and-fast rule about refactoring, and we could have got away with copying timelinehandler.rs to the smart phone specific folder and just modifying it where necessary. But separating it like we did avoids code duplication and makes the RAMPScript much more easily maintainable and is therefore best practice.

Creating Smart Phone specific RAMPScript

Now that the generic code is refactored, we can implement the smart phone uihander.rs. Create a new RAMPScript source file using the wizard and call it uihandler.

Create a new clickableItem function which instead of dynamically creating HorizontalLayouts, dynamically creates SmartButtons.

The RAMPScript to achieve this is as follows:

uihandler.rs in uxml_smart
function clickableItem(text, click)
{
	return uxmlNewNode("smartbutton", "", Map("stylename", "smartbuttonstyle", "text", text, "click", click));
}

We're now finished creating the smart phone specific UI. Uploading the feature now will result in the RAMP Server automatically serving either the smart phone or generic feature to the device depending on whether it's a smart phone or not.

BeforeAfter

 

 

The UI wasn't vastly improved, but there are many more smart phone specific UI components available to really spice up the UI, e.g. the Toolbar, the HorizontalScrollView, the WebView, the AnimatedView, the SeekBar, the ProgressBar, the SegmentedControl, the TabBar, the Alert etc.

Learning More

This is a "Getting Started" Guide. Consequently it did not try to be definitive. It certainly didn't cover everything that you need to know but hopefully it provided enough information for you to consult:

To whet your appetite, we finish this by mentioning aspects of UXML, CSS and RAMPScript that you might find particularly useful.

More UXML

This document covered all of the top-level feature phone UXML components except for the Ticker component. The Ticker component can be used to scroll information along the bottom of the screen.

We saw several UXML components that can be placed onto a form such as Labels, MenuItems, Pictures, etc. Here are some that we didn't cover:

  • ValueStepper. This component allows users to select from a limited range of values (e.g. "Yes", "No", "Not applicable").
  • HorizontalLayout. This component allows you to put two or more UXML components on a single line (e.g. a label and text input box).
  • HorizontalRule. This component allows you to put a dividing line across the screen.

We also didn't cover all the attributes of the UXML components that we looked at. You should know about:

  • The "password" attribute for TextInputs that masks user input.
  • The "columns" attribute for menus that allows items to be placed into two columns instead of one.

There are also a number of components available for use on smart phones.

More Styling

There are a large number of CSS attributes. Two that you might find particularly useful are:

  • The image attribute for Forms and Menus. This allows you to put a background image on a screen. The image can be positioned with the image-layout attribute.
  • The padding attribute for increasing the separation distance between components.

More RAMPScript

We barely scratched the surface of RAMPScript and the RAMPScript functions in the examples.

RAMPScript has a number of primitive operations and conditional operators that we didn't look at. We saw the "=" operation for variable assignment and the "==" operator checking for equality. RAMPScript also has operators +, -, /, * and % for addition, subtraction, division, multiplication and modulo reduction for integers. It also has the operators >, <, >=, <= and != for testing logical relationships.

RAMPScript has functions for:

  • String manipulation such as getting a substring or concatenating strings.
  • Cryptographic functions for encrypting data.
  • Timer functions that allow some function to be called at repeated intervals.
  • An asynchronous alternative to the webService() function that can be run in the background.
  • Commands for dynamically adding UXML components to an existing application.
  • On Android and MIDP 2.0 for opening the web browser or making a telephone call.
  • And more...

Appendix I - Setting up Tomcat and a RAMP server on Mac OS X

This assumes that you have the RAMP SDK downloaded and will refer to the folder as {ramp_sdk_folder}.

Installing Tomcat

  • Go to http://tomcat.apache.org/download-60.cgi
  • Under Binary Distributions, Core, click on zip or tar.gz and download the file.
  • Extract the downloaded file to a location of your choice (If using Safari, it might have been automatically extracted).
  • A folder called apache-tomcat-6.0.32 (version number may differ) should appear. We'll call this {tomcat_folder}.
  • Open a terminal window and execute {tomcat_folder}/bin/startup.sh.

Setting up the environment

  • Open a terminal window
  • Change the directory to /var/log by executing: "cd /var/log".
  • Create a /var/log/vmt folder by executing: "sudo mkdir vmt" (Enter your password when asked).
  • Change ownership of the folder to your user by executing "sudo chown <user> vmt" where <user> is the name of your user
  • Change the directory to /etc by executing: "cd /etc".
  • Create a /etc/ramp folder by executing: "sudo mkdir ramp"
  • Change ownership of the folder to your user by executing "sudo chown <user> ramp" where <user> is the name of your user

The following steps can be done using the terminal window, or using Finder if you feel more comfortable with it. To get to /etc/ramp using Finder, select "Go" -> "Go to Folder" and type "/etc/ramp".

  • Copy {ramp_sdk_folder}/war/ramp-config.properties to /etc/ramp
  • Create folder /etc/ramp/integration
  • Copy {ramp_sdk_folder}/lib/ramp-integration-4.6.0.jar to /etc/ramp/integration
  • Create main-context.xml in /etc/ramp/integration with the following contents:
main-context.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"

	xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
       ">

   <!--
        You can regard this as boiler-plate.
        The advanced guide covers this in more detail.
     -->
   <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="locations" ref="propertyFiles"/>
   </bean>  
	
   <bean id="serviceRequestManager" class="com.vmt.ramp.integration.impl.ServiceRequestManagerImpl">
      <property name="mapper" ref="serviceRequestMapper" />
   </bean>

   <bean id="serviceRequestMapper" class="com.vmt.ramp.integration.impl.ServiceRequestMapper">
      <property name="handlers" ref="handlers" />
   </bean>

   <bean id="featureBundleProvider" class="com.vmt.ramp.integration.impl.MultiDirectoryFeatureBundleProviderImpl">
      <property name="rootDirectory" value="${scripts.dir}"/>
      <property name="signer" ref="featureSigner"/>
   </bean>

   <bean id="featureSigner" class="com.vmt.ramp.integration.impl.security.SoftwareSigner">
      <property name="rsaPrivateKeyDao" ref="rsaPrivateKeyDao"/>
   </bean>
   
   <bean id="featureVersionFilter" class="com.vmt.ramp.integration.impl.FeatureVersionAfterServiceRequestFilter">
      <property name="multiFBP" ref="featureBundleProvider"/>
   </bean>
   
   <bean id="rsaPrivateKeyDao" class="com.vmt.ramp.integration.impl.security.TestKeysDao" />
   
   
   <util:map id="handlers">
      
   </util:map>

</beans>

Deploying WAR file

  • Copy {ramp_sdk_folder}/war/ramp.war to {tomcat_folder}/webapps

Appendix II - Setting up Tomcat and a RAMP server on Windows

This assumes that you have the RAMP SDK downloaded and will refer to the folder as {ramp_sdk_folder}.

Installing Tomcat

  • Go to http://tomcat.apache.org/download-60.cgi
  • Under Binary Distributions, Core, click on 32-bit Windows zip and download the file.
  • Extract the downloaded file to C:\
  • A folder called apache-tomcat-6.0.32 (version number may differ) should appear under C:\
  • Execute C:\apache-tomcat-6.0.32\bin\startup.bat.

Setting up the environment

  • Create folder C:\etc
  • Create folder C:\etc\ramp
  • Copy {ramp_sdk_folder}\war\ramp-config.properties to C:\etc\ramp
  • Create folder C:\etc\ramp\integration
  • Copy {ramp_sdk_folder}\lib\ramp-integration-4.6.0.jar to C:\etc\ramp\integration
  • Create main-context.xml in C:\etc\ramp\integration with the following contents:
main-context.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"

	xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
       ">

   <!--
        You can regard this as boiler-plate.
        The advanced guide covers this in more detail.
     -->
   <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="locations" ref="propertyFiles"/>
   </bean>  
	
   <bean id="serviceRequestManager" class="com.vmt.ramp.integration.impl.ServiceRequestManagerImpl">
      <property name="mapper" ref="serviceRequestMapper" />
   </bean>

   <bean id="serviceRequestMapper" class="com.vmt.ramp.integration.impl.ServiceRequestMapper">
      <property name="handlers" ref="handlers" />
   </bean>

   <bean id="featureBundleProvider" class="com.vmt.ramp.integration.impl.MultiDirectoryFeatureBundleProviderImpl">
      <property name="rootDirectory" value="${scripts.dir}"/>
      <property name="signer" ref="featureSigner"/>
   </bean>

   <bean id="featureSigner" class="com.vmt.ramp.integration.impl.security.SoftwareSigner">
      <property name="rsaPrivateKeyDao" ref="rsaPrivateKeyDao"/>
   </bean>
   
   <bean id="featureVersionFilter" class="com.vmt.ramp.integration.impl.FeatureVersionAfterServiceRequestFilter">
      <property name="multiFBP" ref="featureBundleProvider"/>
   </bean>
   
   <bean id="rsaPrivateKeyDao" class="com.vmt.ramp.integration.impl.security.TestKeysDao" />
   
   
   <util:map id="handlers">
      
   </util:map>

</beans>

Deploying WAR file

  • Copy {ramp_sdk_folder}\war\ramp.war to C:\apache-tomcat-6.0.32\webapps

Appendix III - ISO 8583 example

Overview

This section describes an example RAMP client application and RAMP Server integration which can be integrated into an existing ISO 8583 system. The example can be found in the SDK distribution (the apps/client/j8583_example_client directory and the apps/server/j8583_example_server directory for the client and RAMP server integration respectively).

The example demonstrates the implementation of 3 kinds of ISO 8583 requests, namely Payment RequestBalance Enquiry and Funds Transfer. These are implemented as ISO 8583 Transaction Request messages (0200s) with transaction types of "00", "31" and "40" respectively.

The request/response flow of the example is designed as follows:



RAMP Client

Request
------------>

<------------
Response



RAMP Server ISO 8583 integration

ISO 8583 0200 request
           ------------>

           <------------
ISO 8583 0210 response



Bank

The Client Application

The client application has 8 options that can be selected from the main menu. 3 of these trigger ISO 8583 requests, while the rest trigger canned responses, and serve merely as visual output examples.

The options and the actions they trigger are as follows:

Client menu option

Action triggered

My Account

Linked to a canned request handler

Balance Enquiry

Triggers the ISO 8583 Balance Enquiry action

View Statement

Displays a canned statement

Transfer Funds

Triggers the ISO 8583 Funds Transfer action

Make Payment

Triggers the ISO 8583 Payment Request action

Value Added Services

Displays a canned value added services menu

Recommend to a Friend

Displays a canned recommend to a friend menu

Request Callback

Displays a canned callback menu

 

Below is a screen shot of the client application:

The RAMP/ISO 8583 Web Service

The actual construction and sending of ISO 8583 messages is handled by the RAMP Server. It has various handlers, each handling a different transaction type.

The handlers and the ISO 8583 messages they send are as follows:

Handler

ISO 8583 message

PaymentRequestHandler

ISO 8583 Transaction Request (0200) with transaction type = "00"

BalanceEnquiryHandler

ISO 8583 Transaction Request (0200) with transaction type = "31"

FundsTransferHandler

ISO 8583 Transaction Request (0200) with transaction type = "40"

The RAMP Server also handles all networking between itself and the bank. This is achieved by establishing a TCP/IP connection between the 2. All ISO 8583 messages are sent with a 2 byte length header.

Whenever a connection is established between the RAMP Server and the bank, an ISO 8583 sign on message (0800) is sent to the bank.

The Bank application

Included with the RAMP Server integration is a Bank application which acts as an issuer or switch that responds to ISO 8583 requests. This application is for testing purposes only and would be replaced by a real issuer or switch in a production system.

ISO 8583 integration deployment

Note

There are various customizations of the ISO 8583 protocol and this example is just a demonstration. The example would need to be modified and thoroughly tested before it could be integrated into a production ISO 8583 system.

  • No labels