The aim of this blog is to expose a new Odata Service based on a JPA entity and run the service as an Hana Cloud Java Application.

It must be considered merely an exercise, since normally the odata services are provided by backend systems.

In this blog i’ll use the JPAProducer API provided by odata4j library.

For the excercise we’ll use the “persistence-with-ejb” sample, delivered with Hana Cloud Platform SDK. I’ve used version 2.x

First of all we need to get odata4j. I prefered to get the latest source version. Here the commands to build and install via maven.

cd ~/apps/libs
git clone https://code.google.com/p/odata4j/
cd odata4j
mvn clean compile install

At thispoint odata4j is installed in local maven repository.

Next step is to configure maven dependencies in eclipse project:

Odata4j can produce odata services using jersey or cxf.

We’re going to use jersey.

Add the following dependencies in <workspace>/persistence-with-ejb/pom.xml

<dependency>
  <groupId>org.odata4j</groupId>
  <artifactId>odata4j-core</artifactId>
  <version>0.8.0-SNAPSHOT</version>
  <type>pom</type>
</dependency>
<dependency>
  <groupId>org.odata4j</groupId>
  <artifactId>odata4j-jersey</artifactId>
  <version>0.8.0-SNAPSHOT</version>
</dependency>

Then copy the odata4j-dist-0.8.0-SNAPSHOT.jar file located in local maven repository (org/odata4j/odata4j-dist/0.8.0-SNAPSHOT/) in WebContent/WEB-INF/lib directory of the eclipse project.

NB: If you’re using HANA as database you have also to copy the jar file com.sap.core.persistence.osgi.hdb.platform_x.x.x.rt2.jar that is located in HCP_SDK/repository/plugins/ into WebContent/WEB-INF/lib.

/wp-content/uploads/2013/11/image1_322904.jpg

Next we need to to create the Odata Producer.

I’ll use the standard org.odata4j.producer.jpa.JPAProducer. Here the code for the factory:

package com.sap.cloud.sample.odata;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.odata4j.producer.ODataProducer;
import org.odata4j.producer.ODataProducerFactory;
import org.odata4j.producer.jpa.JPAProducer;
public class PersonProducerFactory implements ODataProducerFactory {
    private String persistenceUnitName = "persistence-with-ejb";
    private String namespace = "Example";
    private int maxResults = 50;
    @Override
    public ODataProducer create(final Properties arg0) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory(
                persistenceUnitName);
        JPAProducer producer = new JPAProducer(
                emf,
                namespace,
                maxResults);
        return producer;
    }
}

Next we need to create the real odata service that uses our producer.

This means we need to configure a specific odata4j servlet and configure it.

Add the the specific servlet definitions at the end web-app sertion in web.xml file.

<!-- Odata4j producer -->
<!-- Servlet 1: Expose the OData service endpoint -->
<servlet>
          <servlet-name>OData</servlet-name>
          <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
          <init-param>
                    <param-name>javax.ws.rs.Application</param-name>
                    <param-value>org.odata4j.jersey.producer.resources.ODataApplication</param-value>
          </init-param>
          <init-param>
                    <param-name>odata4j.producerfactory</param-name>
                    <param-value>com.sap.cloud.sample.odata.ExampleProducerFactory</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
          <servlet-name>OData</servlet-name>
          <url-pattern>/odata/example.svc/*</url-pattern>
</servlet-mapping>
<!-- Servlet 2: Enable crossdomain access for browser clients -->
<servlet>
          <servlet-name>CrossDomain</servlet-name>
          <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
          <init-param>
                    <param-name>javax.ws.rs.Application</param-name>
                    <param-value>org.odata4j.producer.resources.RootApplication</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
          <servlet-name>CrossDomain</servlet-name>
          <url-pattern>/odata/*</url-pattern>
</servlet-mapping>

Next we can re-deploy and run the application in hanatrial.ondemand.com.

NB: remember to configure the target database in persistence.xml if you’re using HANA Database: com.sap.persistence.platform.database.HDBPlatform

If everything is fine, we can reach the Odata Service here:

https://persistenceejb2p079297trial.hanatrial.ondemand.com/persistence-with-ejb/odata/example.svc/

Here the metadata:

https://persistenceejb2p079297trial.hanatrial.ondemand.com/persistence-with-ejb/odata/example.svc/$metadata

Since we’re using jersey, also the WADL is available:

https://persistenceejb2p079297trial.hanatrial.ondemand.com/persistence-with-ejb/odata/example.svc/application.wadl

References

Homepage of odata4j

How to host an odata4j producer with Apache Tomcat

Example of JPAProducer

Overview of odata4j maven module structure

To report this post you need to login first.

7 Comments

You must be Logged on to comment or reply to a post.

  1. VINCENZO TURCO

    Hi Francesco,

    great blog, thanks for sharing.

    I am currently investigating options for exposing OData services on the Cloud, and I found out another OData library

    A usage scenario is described here

    I would highly appreciate any opinion on this library and how it compares to the one you described in  the blog (easier/more powerful/…).

    I am also creating a SDN thread for this subject, any feedback is welcome.

    Thanks a lot

    Regards

    (0) 
  2. Jens Glander

    Hi Francesco,

    I started originally with the well known PersonsList GettingStarted application tutorial also with the odata4j approach like you did with your very nice blog.

    Later also the ESPM application scenarios, with a more complex JPA model serving a WebShop and Retailer application (see http://scn.sap.com/docs/DOC-46869) were developed and based initially on odata4j.

    Nevertheless it turned out that our ESPM team had several issues to get the more complex ESPM JPA model exposed as OData service when using odata4j.

    For that reason we based our SAP HCP example applications (PersonsList and ESPM) where JPA and OData connectivity is used, technically on the Olingo OData libraries.

    At least our developer experience was better when using Olingo OData with JPA and also we support this new open source project by own committers.

    Would be nice if you could also have a look at the Olingo approach (see e.g. todays updated PersonsList Application) and let us know about your further experience.   

    Best regards,

    Jens

    (0) 
    1. Francesco Bersani Post author

      Hi Jens

      thank you for your comment.

      i’ve seen the olingo project, very interesting.

      It also seems that the project is more active than odata4j. For sure i’ll use this for next odata implementation.

      I’ve used odata4j for a very stupid reason, it was the first hint in google search. Probably beacuse it’s an older project.

      Which kind of issues had you using odata4j?

      Best Regards

      Francesco

      (0) 
      1. Stephen Cherian

        Hi Francesco,

          To answer your question regarding what kind of issues we faced with OData4j

        • No support for function imports
        • immutable entity set names. Usually the name of an OData entity set is the plural word of the corresponding entity type’s name. Unfortunately the JPAProducer included in odata4j just takes the JPA entity type name for the OData entity set name as well and doesn’t provide any means to adapt it.
        • Non standard property names (lower/upper case) . Properties of JPA entity beans are usually named according to the standard lower camel case naming convention, whereas properties in an OData metadata service document usually follow the upper camel case naming convention.

        These could be overcome in Olingo. As mentioned by Jens you can refer to ESPM application scenarios with a more detailed implementation with JPA, Olingo for OData connectivity.

        Best Regards

        Stephen

        (0) 
  3. Luc MARGARON

    Hello,

    To make it run on Java Web Tomcat 8 Server, I had to include in web_inf\lib

    core4j-0..jar, jersey_bundle-1.19.1.jar joda_time-1.19.jar odata4j-core odata4j-cxf  and odata4j-jersey.

    However I get an exception on hana cloud platfrom :

    2016 07 29 15:32:09#+00#ERROR#org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/ejb_jpa]##anonymous#http-bio-8041-exec-8#na#i022455sapdev#ejbjpa#web#i022455sapdev#StandardWrapper.Throwablejava.lang.RuntimeException: javax.naming.NameNotFoundException: Name [CDIExtension] is not bound in this Context. Unable to find [CDIExtension]. at com.sun.jersey.server.impl.cdi.CDIExtension.getInitializedExtension(CDIExtension.java:183) at com.sun.jersey.server.impl.cdi.CDIComponentProviderFactory.(CDIComponentProviderFactory.java:95) at com.sun.jersey.server.impl.cdi.CDIComponentProviderFactoryInitializer.initialize(CDIComponentProviderFactoryInitializer.java:76) at com.sun.jersey.spi.container.servlet.WebComponent.configure(WebComponent.java:572) at com.sun.jersey.spi.container.servlet.ServletContainer$InternalWebComponent.configure(ServletContainer.java:332) at com.sun.jersey.spi.container.servlet.WebComponent.load(WebComponent.java:604) at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:207) at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:394) at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:577) at javax.servlet.GenericServlet.init(GenericServlet.java:244) at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1282) at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1195) at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:866) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:134) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) at org.eclipse.virgo.web.enterprise.security.valve.OpenEjbSecurityInitializationValve.invoke(OpenEjbSecurityInitializationValve.java:44) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505) at com.sap.core.jpaas.security.auth.service.lib.AbstractAuthenticator.invoke(AbstractAuthenticator.java:168) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956) at com.sap.core.tenant.valve.TenantValidationValve.invokeNextValve(TenantValidationValve.java:168) at com.sap.core.tenant.valve.TenantValidationValve.invoke(TenantValidationValve.java:94) at com.sap.js.statistics.tomcat.valve.RequestTracingValve.invoke(RequestTracingValve.java:38) at com.sap.core.js.monitoring.tomcat.valve.RequestTracingValve.invoke(RequestTracingValve.java:27) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:436) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1078) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:812) Caused by: javax.naming.NameNotFoundException: Name [CDIExtension] is not bound in this Context. Unable to find [CDIExtension]. at org.apache.naming.NamingContext.lookup(NamingContext.java:819) at org.apache.naming.NamingContext.lookup(NamingContext.java:167) at com.sun.jersey.server.impl.cdi.CDIExtension.getInitializedExtension(CDIExtension.java:181) … 33 common frames omitted Any Idea ?

    (0) 
  4. mahendran N

    Hi Experts.,

                    

                       I want to achieve deep entity creation using JPA. Anyone please guide me on the same?

    -Mahi

    (0) 
    1. Michael Appleby

      Unless you are asking for clarification/correction of some part of the Document, please create a new Discussion marked as a Question.  The Comments section of a Blog (or Document) is not the right vehicle for asking questions as the results are not easily searchable.  Once your issue is solved, a Discussion with the solution (and marked with Correct Answer) makes the results visible to others experiencing a similar problem.  If a blog or document is related, put in a link.  Read the Getting Started documents (link at the top right) including the Rules of Engagement. 

      NOTE: Getting the link is easy enough for both the author and Blog.  Simply MouseOver the item, Right Click, and select Copy Shortcut.  Paste it into your Discussion.  You can also click on the url after pasting.  Click on the A to expand the options and select T (on the right) to Auto-Title the url.

      Thanks, Mike (Moderator)

      SAP Technology RIG

      (0) 

Leave a Reply