Over a million developers have joined DZone.

Pling for iPhone

· Mobile Zone

Introduction

The purpose of this article is to give an overview and also some best practices on how to use the JBOSS RestEasy framework as a supporting service layer for an Apple IOS based application hosted in a cloud environment such as Google App Engine.

Technology stack

Client

iPhone IOS 4.2

JSON-Framework for IOS

Facebook Connect for IOS

Server

JBOSS RestEasy

UrbanAirship Notification Service


Architecture Overview

The following diagram highlights the main components used in the Pling application.

Communication between IOS and Google App Engine is handled by:

·       IOS Foundation Framework using NSURL related classes

·       RestEasy framework on Google App Engine for handling the incoming IOS issues requests

Communication between Google App Engine and UrbanAirship for handling push notifications is handled by:

·      Google App Engine URLFetchService

·       Jackson to serialize and de-serialize JSON data

We shall note that Google App Engine can only communicate with external web services using the provided URLFetchService framework. Frameworks that rely on HttpClient can however take advantage of Google App Engine by providing custom classes for implementing the underlying network access (HttpClientManager, HttpConnection or ClientConnectionManager, ManagedClientConnection depending on the HttpClient version). Pling does not rely on such feature.

RestEasy Implementation

Using and configuring RestEasy in Google App Engine is quite easy.

After dropping the RestEasy framework libraries into Eclipse, it is time to configure the web.xml file in the war/WEB-INF directory.

As Google App Engine application instances are started on demand (unless you pay for reserved instances.), startup time is critical and therefore automatic RestEasy providers and resources scanning must be turned off. This increases response time on the first REST request as the Pling instance is re-started by Google App Engine.

As a consequence of turning off resources and providers scanning, the web.xml file must contain dedicated entries for implemented Pling related classes.

There are two resources classes:

·       User Management service for handling user registration and keep a mapping between push notification tokens (IOS specific) and Facebook user identifier (provided upon successful login through Facebook Connect)

·       Push Notification service for handling IOS device registration and push notification request. 2 types of requests are implemented for Pling:

o   A request for pushing location information to a Facebook Pling friend

o   A request for requesting location information from a Facebook Pling friend

There is one Provider class:

·       Pling Exception Mapper for handling errors occurring in the application layer

Below is an extract for the RestEasy specific elements pertaining to the web.xml file.

<context-param>
<param-name>resteasy.scan.providers</param-name>
<param-value>false</param-value>
</context-param>

<context-param>
<param-name>resteasy.scan.resources</param-name>
<param-value>false</param-value>
</context-param>

<context-param>
<param-name>resteasy.resources</param-name>
<param-value>
com.plingmyphone.service.impl.UserManagementServiceImpl,
com.plingmyphone.service.impl.PushNotificationServiceImpl
</param-value>
</context-param>

<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>

<context-param>
<param-name>resteasy.providers</param-name>
<param-value>
com.plingmyphone.service.PlingExceptionMapper
</param-value>
</context-param>

<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>



Exception Handling

All exceptions that need to be communicated back to the IOS client are handled by the JAX-RS ExceptionMapper and ResponseBuilder functionalities.

In Pling, all exceptions are channeled via a unique exception using the 409 HTTP status.

Below is the code related to our custom Pling JSON exception.
 

@Provider
public class PlingExceptionMapper implements ExceptionMapper<com.plingmyphone.service.PlingException>
{
public Response toResponse(PlingException exception)
{
ResponseBuilder rb =
Response.status(409)
.entity("{\"error\": { \"type\": \"" +
exception.getClass().getSimpleName() +
"\", \"message\": \"" + exception.getMessage() + "\"}}")
.type("application/json");
return rb.build();
}
}

Services

User management and push notification services are the 2 REST entry points.

HTTP methods used in these services are:

·       POST, using a query parameter and a domain class that automatically gets deserialized from JSON into Java (Thanks to JAX-RS / RestEasy!)

·       GET, using a path parameter and returning a domain class that automatically gets serialized into JSON from Java (Thanks, once again, to JAX-RS / RestEasy!)

·       PUT, using path, query parameters as well as a domain class that automatically gets deserialized from JSON into Java (Thanks to… JAX-RS / RestEasy!)

·       DELETE, using path and query parameters.

Any related Push Notification methods are also using the JAX-RS DefaultValue in order to default to the UrbanAirship development instance. Apple, defines 2 instance types:

·       Development, push notification sandbox

·       Production, push notification

The following diagram shows the 2 instances configured on the UrbanAirship web console.



Below is an example of a method using the DefaultValue for driving the selection of the underlying UrbanAirship instance.

@Override
@POST
@Path("/msg")
public void pushMessage(@QueryParam("envType") @DefaultValue("DEV") String envType,
PlingNotification notification)
{

}


PlingNotification is a domain object that is both used by:

·       RestEasy for JSON serialization/de-serialization

·       JPA for underlying Google App Engine datastore management

If the envType query parameter is omitted in the REST call, the push notification instance will be DEV, therefore pointing to the UrbanAirship pling instance (see diagram above), if the envType parameter is set to PROD, the pling-prod UrbanAirhsip instance will be used.

The following section highlights the definition of the PlingNotification class.

@Entity
@Table(name = "PlingNotification")

@XmlRootElement
public class PlingNotification implements Serializable
{

}


PlingNotification is marked as a JPA entity and given a name as a table for the underlying persistent capabilities offered by Google App Engine.

The following diagram depicts what is available on the Pling specific Google App Engine Web dashboard.



The Java XML RootElement binding annotation is used here for allowing RestEasy to serialize/de-serialize the PlingNotification object from JSON to Java and Java to JSON.


IOS Access to exposed RestEasy services

Access to user management and push notification services RestEasy endpoints on IOS platform is done with a combination of built-in network and URL access capabilities as well as an external JSON framework for serializing and de-serializing JSON to IOS Objective-C representation.

2 REST request types are used in the Pling application:

·       Asynchronous calls using [NSURLConnection connectionWithRequest…]

·       Synchronous calls using [NSURLConnection sendSynchronousRequest…]

Example of an asynchronous request for pushing a location notification to a RestEasy endpoint is:

NSMutableURLRequest *pushNotificationRequest; 
NSString *UAServer = @"http://url_to_gae_instance/service/notification/msg";

// Construct URL to access RestEasy endpoint

pushNotificationRequest = [[NSMutableURLRequest alloc] initWithURL:url];
[pushNotificationRequest setHTTPMethod:@"POST"];

// Send along our device alias as the JSON encoded request body
[pushNotificationRequest addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[pushNotificationRequest setHTTPBody:[jsonNotification
dataUsingEncoding:NSUTF8StringEncoding]];
[[NSURLConnection connectionWithRequest:pushNotificationRequest delegate:self] start];

[pushNotificationRequest release];

 
Asynchronous calls rely on the NSURLConnection delegate message (similar to a callback):

- (void)connection:(NSURLConnection *)theConnection didReceiveResponse:(NSURLResponse *)response

 

Example of a synchronous request for retrieving details related to a push notification message from a RestEasy endpoint is:

NSURLResponse *response = nil;
NSError *error = nil;
PlingNotification *plingNotification = nil;
// Construct the url to access RestEasy endpoint
pushNotificationRequest = [[NSMutableURLRequest alloc] initWithURL:url];
[pushNotificationRequest setHTTPMethod:@"GET"];

[pushNotificationRequest addValue:@"application/json" forHTTPHeaderField:@"Accept"];

NSData *returnData = [NSURLConnection sendSynchronousRequest:pushNotificationRequest returningResponse:&response error:&error];

[pushNotificationRequest release];

// Convert returned JSON data to a PlingNotification object
if(returnData != nil)
{
plingNotification = [[PlingNotification alloc] initWithJSON:[[[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding] autorelease]];
}


As PlingNotification is a complex object, JSON Framework on IOS allows to extend the object for providing the helper methods for serializing and de-serializing JSON to Objective-C and Objective-C to JSON.

The 2 methods are:

·       - (id) initWithJSON:(NSString *)JSONString

·       - (id)proxyForJson

The first method is used to convert JSON into the object itself. The second one converts the object into JSON.

The following shows a snippet of each method implementation:

- (id) initWithJSON:(NSString *)JSONString
{
self = [super init];
if (self != nil)
{
SBJsonParser *parser = [SBJsonParser new];
NSDictionary *jsonObject = (NSDictionary *)[parser objectWithString:JSONString];

NSLog(@"NB ENTRIES IN JSON: %d", [jsonObject count]);
NSLog(@"JSON STRING: %@", JSONString);

self.fromFacebookUID = [jsonObject objectForKey:@"fromFacebookUID"];
// Do the rest here (many fields…)

}
return self;
}

 

- (id)proxyForJson 
{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
fromFacebookUID, @"fromFacebookUID",
// more key value pairs here
nil];

for(NSString *key in [dict allKeys])
{
NSLog(@"=====> KEY: %@", key);
}
return dict;
}

 

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}