Mobile push messaging should be an essential part of the real-time enterprise and now it's easy to achieve with sapnwncloud . Mobile push messaging enable you to instantly push important business event to the mobile phones of decision makers in the business. They can then take the appropriate action.

 

I hereby release two open-source projects that will handle most of the complexity involved in such scenarios. It's released on github under Apache 2.0 license,and and therefore should be very compatible for most purposes. Github also makes it dead easy and encourages you to fork the project, so please do!

 

For now, the projects supports Android through Google Cloud Messaging. But with some assistance from the community, I'm sure we will be able to support iOS devices (Apple Push Notification Service) and other emerging mobile platforms soon.

 

 

The reference android app implementation is available here (link to android .apk)

The reference backend implementation is hosted on #sapnwcloud  https://androidgcmbackendp013234trial.nwtrial.ondemand.com/nwcloud-androidgcm-backend/

 

 

Project: nwcloud-androidgcm-backend

Hosted at https://github.com/elsewhat/nwcloud-androidgcm-backend

 

 

The project provides a backend for handling the mobile push messages.

It has the following responsibilities:

  • Keep track of registered devices
    Handled through a REST interface called from the mobile clients

  • Provide a push message interface to clients
    Handled through a REST interface called from an onPremise solution, a mobile client or any other program

  • Send the push message to the mobile client
    Uses the framework provided by the mobile platform. Currently, only Google Cloud Messaging is used but easily extendable.
    (requires that we create a Google API project with GCM and that we generate a server API key)

This project uses JPA for persistence and Jersey for REST interface.

 

Project: nwcloud-androidgcm-app

Screenshot_2012-11-02-12-36-35.png

Hosted at https://github.com/elsewhat/nwcloud-androidgcm-app

 

This android app is a reference app that provides two main functionalities:

  • Register the android device and the app so that it can receive mobile push messages.
    This registration is done to Google's GCM service and to the #sapnwcloud backend application.
    This step is required before it can receive any messages

  • Send mobile push message to other mobile devices
    This uses only the REST interface of the #sapnwcloud backend app (requires that other mobile devices are also registered in the backend)

 


When the app is started, it contacts Google's GCM service and receives a registration key. This registration key is specific for the device and the Google API project we use (shared API project for app and the backend). The registration key and the email address of the user is then sent to the REST interface of the backend. We use email as an indicator of the device that should receive the mobile push message. 

 

The app then fetches a list of all the emails that are registered in the backend, and allows you to type a message.

 

When you select to send the message, the app will send it to the backend using the REST interface. The backend will then subsequently send it to the recipient over the Google Cloud Messaging service (for this it uses the registration key of the recipient).

 

Please note that this app only illustrates one possible way to trigger a mobile push message through the backend.

 

This project uses google-http-client-java for performing REST  (it's transparent serialization from Java objects to JSON and back is awesome)

 

Other client uses

 

You do need an app on the device in order to register to the push service. However, once it is registered the push message could be initiated from anywhere.

The #sapnwcloud backend provides a REST interface over HTTP, so all a potential client needs to support is plain HTTP.

 

Here are some examples that can be implemented easily:

  1. A workflow has entered a critical step that must be handled immediately. The workflow will call the REST interface of the #sapnwcloud backend and pass a message and the emails of the recipients. The push message is almost instantly sent to the mobile devices who can take the appropriate actions.

  2. A new prioritized work order is created and should be handled before existing ones. The onPremise system can send a message to the worker which is already using a mobile app for planning his work

 

 

Enjoy, and on behalf of the SAPMentors; keep sharing!

 

 

Node.js is getting a lot of attention from web developers and has had an amazing adoption among startups and tech companies.In my company bouvet , we haven't yet had much enquiries from customers on Node.js, but I am sure it is not far away.

"Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices."

 

10 years ago Server-side and JavaScript was an oxymoron, but now it is all the rage.

I was therefore not surprised when fellow sapmentor Dick Hirsch asked the all important question:

tweet_rhirsch.PNG

 

Even though SAP NetWeaver Cloud is extremely flexible, it requires that you are able to run the components on top of a Java Virtual Machine(JVM). Node.js runs on the Chrome JavaScript runtime V8 which is implemented in C++ and therefore you're unable to run it on top of the JVM

 

However, there exist other similar framework that run on other JavaScript runtimes which are implemented in Java. These are already running on the JVM, and can therefore easily be adapted to run on SAP NetWeaver Cloud.

 

Here are a few relevant frameworks:

  1. Ringojs
  2. Sprintstack
  3. Narwhal
  4. Helma

 

All of them use the Mozilla Rhino JavaScript engine, though some of them support other JavaScript engines as well.

 

The most active and mature of these, appear to be Ringojs (though I am no expert in this area at all).

 

Let's have a look at how we can run a Ringojs app with ServerSide JavaScript on SAP NetWeaver Cloud!

 

Update 22.10: The project is now shared in Github https://github.com/elsewhat/nwcloud-ringojs

 


How to

Deploying a Ringojs app to SAP NetWeaver Cloud is very simple. This because it has a project template for Java Web applications (tailored for Google App Engine). If you want to learn more about how ringo.js is integrated with the web container, see the JsgiServlet class.

 

Step 1

Download the ringo.js 0.8 binaries from http://ringojs.org/download and unzip to a local folder

 

Step 2

Open a command prompt and go to the bin subfolder of ringojs.

Run the command

 

ringo-admin create --google-appengine /path/to/appdir

 

 

where /path/to/appdir can for example be C:\DEV\temp\RingoJSNeo

 

Step 3

Start eclipse (with SAP NetWeaver Cloud SDK installed)

 

Step 4

Create new dynamic web project with name RingoJSNeo and use default settings.

For me the path of the project on the files system is C:\DEV\workspace_neo\RingoJSNeo\WebContent\WEB-INF

 

Step 5

Copy files from the ringojs-created project, to the web project in eclipse.

All files from C:\DEV\temp\RingoJSNeo\WEB-INF should be copied to C:\DEV\workspace_neo\RingoJSNeo\WebContent\WEB-INF

 

You can overwrite the web.xml without problems (although normally you would merge the files).

 

Eclipse will indicate a lot of errors in .js files, but you can safely ignore them.

This is how your project will look now:

(on the right you see the main.js that will be executed when an end-user access the web solution)

eclipseringoproject.PNG

 

Step 7

Open http://localhost:8080/RingoJSNeo in your browser and you should see this screen

browser_ringojsneo.png

 

 

Summary

 

The Java Virtual Machine is a very flexible piece of software and with SAP NetWeaver Cloud you can fully utilize this.

 

So the next time @hipsterhacker disses enterprise software, tell him SAP runs server-side JavaScript.

tweet_hipsterhacker.PNG

As part of my work in bouvet, I've started experimenting with the new SAP Netweaver Cloud (aka. Neo and JPaaS) and boy is there much exciting stuff to share.

 

SAP NetWeaver Cloud has a free developer license, that is currently limited to a 90 days trial. Get access by following the instructions here. Removing the 90 day trial limitation is definitively something SAP wants, and it is being worked on (ref https://twitter.com/#!/schmerdy/status/202783043075846145). The @SAPMentors will do our utmost to make sure SAP delivers on this promise.

 

Update 25.05: Added information on how to support cross-origin resource sharing .

Update 22.10: Project is now shared on Github https://github.com/elsewhat/nwcloud-rest_example

 

Overview

The goal of this blog is to show how you can expose data through a REST API from an application hosted on the SAP NetWeaver Cloud.

 

Going into this experiment, I had the following goals:

  1. Data must be persisted in SAP NetWeaver Cloud
  2. REST API must support the operations: read all objects, read single object, create single object and update single object
  3. No coding for xml and json marshalling and unmarshalling
  4. Extensible to more complex objects
  5. To be consumed by a sapui5 client (next blog?)
    (the sapui5 client can subsequently be consumed in SAP NetWeaver Cloud Portal! ref @ohad_yassin)

 

One of the main benefits of  basing the SAP NetWeaver Cloud on the JVM and Java, is that there is a huge selection of mature and production ready libraries and frameworks. You are truly standing on the shoulders of giants that have small java-coding hands.

 

I had already decided to use JPA as the persistency layer, and SAP has a great tutorial in place on how to use it with SAP NW Cloud.

For the REST framework, I quickly found out that the Java Community Process had produced a standard extension to java named JAX-RS: Java API for RESTful Web Services. The reference implementation of this, called Jersey, is well suited to be include in a SAP NetWeaver Cloud application.

 

For the data behind the REST service, I choose a simple FeedEntry object that represents an update in a social feed.

It basically consists of a sender and a text. Simple, yet useful in several contexts.

 

Demo

The REST API is publicly available on #SAPNWCloud , so you can test it right now!

 

For testing REST APIs it's common to use the command line tool curl. It is part of most linux and unix distributions and If you are on windows or amiga you can download it from here .

 

Here are a few examples you can try out yourself.

 

Read all objects call:

curl -k -i -X GET -H 'Content-Type: application/json' https://feedstream1p013234trial.netweaver.ondemand.com/feed_stream/api/feed/
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Fri, 25 May 2012 08:01:42 GMT
Server: SAP
Set-Cookie: cookie_f=370167306.26911.0000; path=/


{"feedEntry":[{"feedText":"First post with the REST API","id":"1","isComment":"f
alse","senderEmail":"dagfinn.parnas@bouvet.no","senderName":"Dagfinn Parnas"},{"
feedText":"UPDATE:Bouvet experiment with #sapnwcloud seems to work well","id":"2
","isComment":"false","senderEmail":"dagfinn.parnas@gmail.com","senderName":"Dag
finn Parnas"}]}

 

Create new feed:

curl -k -i -X POST -H 'Content-Type: application/json' -d '{"senderName":"Dagfinn Parnas",
"feedText":"Bouvet experiment with #sapnwcloud seems to work well","isComment":false,"senderEmail":"dagfinn.parnas@gmail.com"}'
 https://feedstream1p013234trial.netweaver.ondemand.com/feed_stream/api/feed

 

Update existing feed:

curl -k -i -X POST -H 'Content-Type: application/json' -d '{"feedText":"UPDATE:Bouvet experiment with #sapnwcloud seems to work well"}' 
https://feedstream1p013234trial.netweaver.ondemand.com/feed_stream/api/feed/2

 

 

How to

Show us the code!

 

Step 1

Complete the tutorial "Adding Persistence to a Neo Application Using JPA"

After doing this, you should in eclipse have two projects:

  1. A project representing your JPA data model
    (named JPAModel)
  2. A web project which demonstrates reading and updating the data persisted in your JAP data model
    (name Hello World)

 

Both of these eclipse projects will be used. The JPA project will represent our data model FeedEntry and the web project will become our REST API

 

Step 2

Download the java library files from Jersey project.

  • Go to http://jersey.java.net
  • Select download
  • Download from the link that says: "A zip of Jersey containing the Jersey jars, core dependencies (it does not provide dependencies for third party jars beyond those for JSON support) "
  • Unzip it to a local temp folder

 

(it would be much better to set up this dependency to SAP NW Cloud through Maven. I'll leave that as an exercise and a potential future blog)

 

Step 3

Next step is to copy the jersey libraries to the web project.

Copy all jersey library files (.jars) from the lib subfolder in step 2 to your web project under the path WebContent/WEB-INF/lib

 

Here is my web project structure after the copy operation.

(please note that in the screenshot I have a few extra .jar files. This doesn't matter to this blog and is because I started out with the sapui5 getting-started web project)

files_jersey2.PNG

 

Step 4

Next step is to setup jersey in the web project. Our goal is that all urls matching http://<server>/<web_app_context_root>/api/* are processed using jersey.

 

We do this setup in the Deplyoment descriptor of the web project. You can either access it from the Deployment Descriptor object in the project structure or directly to the web.xml file it represents. The web.xml file is located under WebContent/WEB-INF/web.xml.

 

This is the contents you need to have

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>feed_stream</display-name>
  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>no.bouvet.sap.neo.rest</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/api/*</url-pattern>
  </servlet-mapping> 
</web-app>

 

Please note that you may have multiple other lines in the web.xml file, for example for a HelloWorldServlet. You do not need to delete them.

 

How does this work? Let me explain the three simple steps:

  1. We define a servlet which is instansiated through the class com.sun.jersey.spi.container.servlet.ServletContainer.
    This class is included in the java libraries we copied in the previous step
  2. This servlet has a parameter which is jersey specific. This parameter must have the value of the java package name where jersey should look for REST based services. It must match the package name we use in the FeedResource class later
  3. We map the Jersey servlet to all incoming urls with pattern /api/*

 

Step 5

We now have configured jersey for our web application, and are ready to implement our REST-based service.

Before we look at the actual service implementation, we'll extend our data model to the fields we need.

 

This step is done on the JPAModel project (and not the web project).

 

Right-click JPA Content and select Open Diagram

open_digram.png

 

In the diagram, add new fields for the attributes you'd like.

I've renamed both the JPAModel project to FeedModel and the entity to FeedEntry. Note that you need to refer to the project name (or more correctly, the persistency model name) in the REST service class later.

 

Here are is my model at the end.

jpafields.png

 

My JPA model has the same query defined  as the JPA tutorial (name AllFeedEntries)

 

Step 6

The JPA Model we saw in previous step is represented in a Java class.

We must update this class, so that Jersey understands that this is a REST data object which should be marshalled and unmarshalled based on requests.

 

In order to do this, we need only to add the notation @XmlRootElement to the before the java class declaration.

In the JPA Model project, open the java class under src\org.persistence\FeedEntry.java

(you name may be slightly different).

 

For completeness sake, I've included the whole FeedEntry.java class here. Note that I've only added the @XmlRootElement here, the rest of the class is generated based on the JPA modelling you did in step 5.

 

package org.persistence;

import javax.persistence.*;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.Date;

@XmlRootElement
@Entity
@Table(name = "T_FEEDENTRY")
@NamedQuery(name = "AllFeedEntries", query = "select f from FeedEntry f")
public class FeedEntry {

          @Id
          @GeneratedValue
          private long id;
          @Basic
          private String senderName;
          @Basic
          private String feedText;
          @Basic
          private String senderEmail; 
          @Basic
          private boolean isComment;
          @Basic
          private String parent;
          @Temporal(TemporalType.TIMESTAMP)
          @Basic
          private Date timeCreated;

          public long getId() {
                    return id;
          }

          public void setId(long id) {
                    this.id = id;
          }

          public void setSenderName(String param) {
                    this.senderName = param;
          }

          public String getSenderName() {
                    return senderName;
          }

          public void setFeedText(String param) {
                    this.feedText = param;
          }

          public String getFeedText() {
                    return feedText;
          }

          public void setSenderEmail(String param) {
                    this.senderEmail = param;
          }

          public String getSenderEmail() {
                    return senderEmail;
          }


          public void setIsComment(boolean param) {
                    this.isComment = param;
          }

          public boolean isIsComment() {
                    return isComment;
          }

          public void setParent(String param) {
                    this.parent = param;
          }

          public String getParent() {
                    return parent;
          }

          public void setTimeCreated(Date param) {
                    this.timeCreated = param;
          }

          public Date getTimeCreated() {
                    return timeCreated;
          }
}

 

 

 

Step 7

All we are left with now is to implement the actual REST service class. As mentioned in the introduction, we want it to support the operations: read all objects, read single object, create single object and update single object. The operations should support both JSON and XML as input and output based on the preferences of the client. The object we are building the REST service on top of is the FeedEntry from step 6.

 

The code is very simple due to jersey's clever usage of annotations. This means that we can have the service as plain old java object (aka POJO) and therefore do not have to think about the traditional complexities of Java EE.

 

package no.bouvet.sap.neo.rest;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.sql.DataSource;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.persistence.FeedEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Bouvet Experiment: REST API for the FeedEntry class
 * 
 * Uses annotations from JAX-RS and JAXB
 * Is called from the Jersey servlet com.sun.jersey.spi.container.servlet.ServletContainer
 * 
 * This class is processed if url is http(s)://<server>/<appl_context_root>/<jersey_root>/feed/*
 * 
 * @author dagfinn.parnas@bouvet.no
 */
@Path("/feed")
public class FeedResource {
          final Logger logger = LoggerFactory.getLogger(FeedResource.class);

          //Ask jersey to populate this parameter for one of the REST methods
          @Context
          UriInfo uriInfo;

          //attributes used for reading/writing to JPA persistence
          private static DataSource ds;
          private static EntityManagerFactory emf;

          /**
           * Constructor needs to have no parameters.
           * It will initialize the datasource we are using (JPA)
           * 
           */
          public FeedResource(){
                    try {
                              initPersistencyLayer();
                    }catch (Exception e) {
                              //TODO: Handle better
                              logger.error("Failed to initialize persistency layer", e);
                    }
          }

          /**
           * Main method that returns all feeds in the persistency layer.
           * It can produce the content in either JSON or XML (based on client preferences).
           * Jersey handles the marshalling automatically.
           * 
           * Curl example (return all feeds in json format):
           * $ curl  -i -H "Accept: application/json" 
           * http://localhost:8080/feed_stream/api/feed/
           */
          @GET
          @Produces( { MediaType.APPLICATION_JSON ,  MediaType.APPLICATION_XML})
          public List<FeedEntry> getAllFeedEntries() {
                    //Get all feed entries from persistency layer
                    EntityManager em = emf.createEntityManager();
                    List<FeedEntry> resultList = em.createNamedQuery("AllFeedEntries",
                                        FeedEntry.class).getResultList();

                    //Logging
                    String message = (resultList==null)? "getAllFeedEntries returning null": "getAllFeedEntries returning " + resultList.size() + " entries";
                    logger.info(message);

                    return resultList;
          }

          /**
           * Method that returns all feeds in the persistency layer.
           * Can be used for testing in the browser, as it request
           * the media type we expose in this method
           */
          @GET
          @Produces( { MediaType.TEXT_XML })
          public List<FeedEntry> getAllFeedEntriesForHTML() {


                    EntityManager em = emf.createEntityManager();
                    List<FeedEntry> resultList = em.createNamedQuery("AllFeedEntries",
                                        FeedEntry.class).getResultList();

                    //Logging
                    String message = (resultList==null)? "getAllFeedEntries returning null": "getAllFeedEntries returning " + resultList.size() + " entries";
                    logger.info(message);

                    return resultList;
          } 

          /**
           * Return a single feed entry based on ID
           * Will be called if request has syntax /feed/<feed id>
           * It can produce the content in either JSON or XML (based on client preferences)
           * 
           * Curl example (return feed with id 2)
           * $ curl  -i -H "Accept: application/json" 
           * http://localhost:8080/feed_stream/api/feed/2
           */
          @GET
          @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
          @Path("{feedid}/")
    public FeedEntry getSingleFeed(@PathParam("feedid") String strFeedId) {
                    EntityManager em = emf.createEntityManager();
                    //Logging
                    logger.error("getSingleFeed with id:"+ strFeedId + " called");

                    try {
                              long feedId = Long.parseLong(strFeedId);
                              FeedEntry feedEntry = em.find(FeedEntry.class, feedId);
                              return feedEntry;
                    }catch (NumberFormatException e1) {
                              // TODO: Input parameter is not a long and therefore not a valid primary key
                              logger.warn("getSingleFeed for " + strFeedId + " is not a valid key", e1);
                    }catch (IllegalArgumentException e2){
                              //Invalid type of parameter . Should not happen normally
                              logger.warn("getSingleFeed for " + strFeedId + " gave exception", e2);
                    }
                    return null;
    }

          /**
           * POST a new object and store it in the persistency layer
           * Must be called with the HTTP POST method 
           * and accepts input in both JSON and XML format.
           * 
           * Curl example (creates new feed):
           * $ curl -i -X POST -H 'Content-Type: application/json' 
           * -d '{"senderName":"Jane Doe","feedText":"test","isComment":false,"senderEmail":"dagfinn.parnas@bouvet.no"}' 
           * http://localhost:8080/feed_stream/api/feed/
           * 
           * @param feedEntry
           * @return
           */
          @POST
          @Consumes( { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
          public Response createSingleFeed(FeedEntry feedEntry) {
                    //The feedEntry is automatically populated based on the input. Yeah!
                    logger.info("Creating new feed ");

                    //persist the entry
                    EntityManager em = emf.createEntityManager();
                    em.getTransaction().begin();
                    em.persist(feedEntry);
                    em.getTransaction().commit();


                    //The HTTP response should include the URL to the newly generated new entry.
                    //Probably exist a better way of doing this, but it works
                    try {
                              URI createdURI = new URI(uriInfo.getAbsolutePath()+""+ feedEntry.getId());
                              return Response.created(createdURI).build();
                    } catch (URISyntaxException e) {
                              logger.warn("Unable to create correct URI for newly created feed " + feedEntry, e);
                              //fallback is to include the input path (which will be lacking the id of the new object)
                              return Response.created(uriInfo.getAbsolutePath()).build();
                    } 
          }

          /**
           * Update one or more fields of a single feed entry
           * 
           * Curl example (updates senderEmail for feed with id 2) :
           * $ curl -i -X POST -H 'Content-Type: application/json' 
           * -d '{"senderEmail":"dagfinn.parnas@gmail.com"}' http://localhost:8080/feed_stream/api/feed/2
           */
          @POST
          @Consumes( { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
          @Path("{feedid}/")
    public Response updateSingleFeed(@PathParam("feedid") String strFeedId, FeedEntry modifiedFeedEntry) {
                    logger.info("updateSingleFeed with id:"+ strFeedId);

                    try {
                              long feedId = Long.parseLong(strFeedId);

                              EntityManager em = emf.createEntityManager();
                              FeedEntry currentFeedEntry = em.find(FeedEntry.class, feedId);
                              if(currentFeedEntry==null){
                                        logger.warn("updateSingleFeed failed as " + strFeedId + " does not exist");
                                        return Response.notModified(strFeedId + " does not exist").build();
                              }
                              //allow the post to only have one or more fields updated
                              if(modifiedFeedEntry.getParent()!=null){
                                        currentFeedEntry.setParent(modifiedFeedEntry.getParent());
                              }
                              if(modifiedFeedEntry.getSenderEmail()!=null){
                                        currentFeedEntry.setSenderEmail(modifiedFeedEntry.getSenderEmail());
                              }
                              if(modifiedFeedEntry.getSenderName()!=null){
                                        currentFeedEntry.setSenderName(modifiedFeedEntry.getSenderName());
                              }
                              if(modifiedFeedEntry.getFeedText()!=null){
                                        currentFeedEntry.setFeedText(modifiedFeedEntry.getFeedText());
                              }
                              if(modifiedFeedEntry.getTimeCreated()!=null){
                                        currentFeedEntry.setTimeCreated(modifiedFeedEntry.getTimeCreated());
                              }
                              //store in persistency store
                              em.getTransaction().begin();
                              em.persist(currentFeedEntry);
                              em.getTransaction().commit();

                              //return an ok response
                              return Response.ok().build();
                    }catch (NumberFormatException e1) {
                              // TODO: Input parameter is not a long and therefore not a valid primary key
                              logger.warn("getSingleFeed for " + strFeedId + " is not a valid key", e1);
                              return Response.serverError().build();
                    }catch (IllegalArgumentException e2){
                              //Invalid type of parameter . Should not happen normally
                              logger.warn("getSingleFeed for " + strFeedId + " gave exception", e2);
                              return Response.serverError().build();
                    }
    } 

          /**
           * Initialize the persistency layer (JPA)
           * 
           * @throws Exception
           */
          private void initPersistencyLayer() throws Exception  {

                    try {
                              logger.debug("Setting up persistency layer for FeedResource");
                              InitialContext ctx = new InitialContext();
                              ds = (DataSource) ctx.lookup("java:comp/env/jdbc/DefaultDB");
                              Map properties = new HashMap();
                              properties.put(PersistenceUnitProperties.NON_JTA_DATASOURCE, ds);

                              //IMPORTANT! The first parameter must match your JPA Model name in persistence.xml
                              emf = Persistence.createEntityManagerFactory("FeedModel", properties);
                    } catch (NamingException e) {
                              //TODO: Handle exception better
                              logger.error("FATAL: Could not intialize database", e);
                              throw new Exception(e);
                    }

          }


}

 

 

That's all there is to it.

 

Cross-origin resource sharing

In order for this service to be consumed through javascript from a html page on a different host, you need to setup cross-orign resource sharing. Basically, this means to add a few http headers to all api calls.

 

Jersey allows you to define filters that processed are called for all methods.

You need to create a new class and refer to this class in the deployment descriptor web.xml file as a property to the Jersey servlet.

 

Filter class

package no.bouvet.sap.neo.rest.filter;

import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;

/**
 * Filter for adding Access-Control-Allow-Origin headers for all API methods
 * 
 * Is registered in the properties to Jersey in web.xml
 * 
 * @author dagfinn.parnas
 *
 */
public class ResponseCorsFilter implements ContainerResponseFilter {

    @Override
    public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
              response.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
              response.getHttpHeaders().add("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE");


              //allow any request headers sent by the client
                    String requestACRHeaders = request.getHeaderValue("Access-Control-Request-Headers");
                    if(requestACRHeaders!=null && !"".equals(requestACRHeaders.trim())){
                                   response.getHttpHeaders().add("Access-Control-Allow-Headers", requestACRHeaders);
                    }

        return response;
    }
}

 

Web.xml configuration

 <init-param>
      <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
      <param-value>no.bouvet.sap.neo.rest.filter.ResponseCorsFilter</param-value>
</init-param> 

 

 

 

Filter Blog

By author:
By date: By tag: