Due to maintenance of systems that run export control checks, the download of the SAP NetWeaver Cloud SDK and the installation/update of the SAP NetWeaver Cloud Eclipse Tools from https://tools.netweaver.ondemand.com/ will not be possible from Friday 01. June 2012, 6 pm CET until Monday 04. June 2012.

 

Sorry for the inconvenience.

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> 

 

 

 

It's May again and as every year the ecosystem is gathering in Orlando to hear about the latest news related to SAP. And we sure got some big news in store for you this time around, because today, we - the  SAP NetWeaver Cloud team - are very excited to announce that we are opening for business!

 

But wait.... even though we have started to spread the word about SAP's cloud platform last year at SAP TechEd already it may have gone unnoticed by the masses, hence it may be a good idea to step back and provide some introduction to SAP NetWeaver Cloud and what it is all about.

 

So, with this blog post I'd like to introduce you to the platform by sharing some of the content from some of the first documents we have collected in our all-newSAP HANA Cloud Developer Center. Make sure to bookmark it if you are interested in development related stuff as from now this will be our base camp!

 

 

SAP NetWeaver Cloud

 

In a nutshell:

SAP NetWeaver Cloud is an open, standards-based and modular Platform as a Service for rapid development of on-demand applications.

3aas.jpg

 

 

It resides just in the middle of this trinity of service models that comprise the SAP Cloud Strategy (bottom-up):

 

  • Infrastructure as a Service,
  • Platform as a Service and
  • Software as a Service.

 

So let's have a closer look on all of these parts before we focus on the platform, shall we?

 

At the bottom we have SAP HANA, which I assume does not need any further introduction anymore. Yet, what is important to mention though is that the platform fully leverages in-memory technologies allowing you to develop applications using SAP HANA. (If you want to catch up on SAP HANA let me redirect you to the SAP HANA Developer Center.)

 

The next layer is infrastructure (IaaS), which provides all the characteristics typically associated with the term 'cloud computing' in the sense of scalability, flexibility and being elastic. Here it's all about server parks and data centers, redundancy, fail-over setups and virtualization - see DevOps.

 

Open Platform

 

overview.jpgThe platform provides the runtime environment for applications regardless of whether they are custom-build or subscription-based. From a technical perspective we are talking about an OSGi runtime environment that gives developers freedom of choice as it supports multiple programming models starting from Java EE 6 Web Profile and Spring Framework and future support for Ruby on Rails, Java 7, and other high productivity SDK’s.

 

That translates to: use whatever you know, use whatever you want!

 

The platform itself is based OpenSource frameworks like Apache and Eclipse and the standard distribution comes with a pre-defined set of frameworks typically required to develop applications to help you get started right away. Of course you are not limited to using these frameworks, but you can choose whatever libraries you want based on your needs.

 

The good thing about open standards and using OpenSource frameworks is that it comes with a low-entry barrier as there's plenty of documentation, best practices and examples freely available on the web. So regardless of whether you do in-house development or buy development services - it should be easier (and cheaper!) than ever to find people with the skills needed to develop your applications.

 

Platform Services

 

To speed up development even more SAP NetWeaver Cloud offers a growing set of central platform services like persistency, connectivity, identity and document management.  For a complete overview about all the platform services currently available please see the SAP NetWeaver Cloud Technical Overview Presentation or if you want to get your feet wet then take a look at the SAP NetWeaver Cloud Documentation.

 

Yet, that's not all .. there's more. As we move onwards there will be even more offerings on top of the platform such as SAP HANA Cloud Portal, which is currently in BETA and many more to be revealed throughout the year.

 

Wrap-up

 

As you can see we have been really busy lately polishing both the platform as well as the documentation and working on the SAP HANA Cloud Developer Center. From now on we will engage with you here on a regular basis as we believe that open platform also implies open communication and being approachable. So please do not hesitate to reach out to us: netweaver-cloud@sap.com

Cloud and SaaS have been the most discussed concepts in the last few years. A lot of startups and almost all the big names in technology have offerings in these areas. Beyond technology the X-as-a-service i.e. "pay as you go" business model is being adopted by a lot more companies. If you look closely there is a common theme in all of this

 

- usage based pricing or pay-as-you-go

- flexible pricing to take care of innovative business models

- faster deployment cycle (hours and days instead of months)

 

In the last few years I have been leading the deployments of such a solution at various organizations. I find myself explaining the solution to lot of my friends so thought of writing a small post on SCN. SAP Convergent Charging which is part of a bigger Offer-to-cash scenario along with SAP Convergent Invoicing. Convergent Charging takes care of the pricing, rating and charging while Convergent Invoicing takes care of the billing and invoicing piece. I would focus this post on the pricing and rating side of things.

 

Let me give some examples of the kind of business where such a solution is desired

 

Printer and Copier maker - as a printer and copier maker you want to have an offering where you install the printer at the customer site and along with a local partner charge the customer based on the usage. Local partner takes care of the support. This model can be used in other cases too e.g. MRI machines.

 

App stores - prime example of usage based pricing where the customers is billed on the amount/number of apps, songs, books or other media downloaded. Revenue sharing among various parties like platform provider, content provider, advertisement agency etc. are involved in this. Think of the complexities when all these parties are international with different tax jurisdictions

 

Product-as-a-Service - this is the software-as-a-service model and the provider organization may choose to implement various pricing strategies e.g. usage based, monthly subscription or a combination of each

 

Online and Mobile Advertising - we are all aware of the CPM and CPC in the online or mobile advertising world. You could choose to implement some really interesting pricing algorithms beyond the simple click measure with various revenue sharing and multiple currencies

 

Some of the other examples I personally know are postal solutions, railway ticketing, financial solution etc. I should not forget to mention that the grand daddy of usage based pricing is Telco plans.  I could give a lot more examples but suffice it to say that we have developed the solution in such a way that it can model any of the real life scenarios. Another thing, for some of the examples like Cloud or advertising the number of transactions per second could be very high. Recently a benchmark was published in which we achieved 700,000 tps (http://www.sap.com/corporate-en/press.epx?PressID=17890) . Just to put things in perspective that is every man, woman and child doing 8 transactions per day (considering the population to be 7 Billion and 86400 seconds in day)

 

In the next post I would describe some of the functional details of how we do this in SAP Convergent Charging. 

 

Helpful Links

 

Next Generation Billing - http://www.sap.com/solutions/business-process/next-generation-billing/index.epx

Follow on Twitter - @SAP_NextGenbill

 

Update 1: Moved here based on Richard Hirsch's suggestion

Filter Blog

By author:
By date: By tag: