This is part 2 of this blog and it assumes you have read Part 1.

In part 1 of this blog we have achieved a basic “ping” service that responds to a get request with just some static answer – “pong”.

Normally you want to access a database and do some nice stuff. The best way to achieve a nice service layer is (in my opinion) to use EJB. You can control access rights, transactions etc. right by annotating your EJB in a nice and simple way. And you get your EntityManager or Datasource for JPA or JDBC directly injected without so much “boiler plate” code. So I want to show how to incopreate EJB in our setup. So the task I want to solve is adding an EJB with an entity manager injected and returning the result as a JSON object.

So first turn your project to a JPA project by adding this project facet in Eclipse.

Then we need a simple EJB:


/**
* EJB
*/
package com.sap.myapp;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
/**
* Simple EJB
*/
@LocalBean
@Stateless
public class MyEJB {
  @PersistenceContext EntityManager em;
  public MyJSONResult doSomething() {
    MyJSONResult result = new MyJSONResult();
    result.setState(em != null ? "em is injected :-)" : " em not inject :-(");
    return result;
  }
}

It does nothing else as to give me the state of the injected entity manager. For the sake of completeness I have to add the MyJSONResult class, which is just a simple POJO:


/**
* JSON bean
*/
package com.sap.myapp;
/**
* Simple Bean as result of EJB call
*/
public class MyJSONResult {
  private String state;
  /**
   * @return the state
   */
  public String getState() {
    return state;
  }
  /**
   * @param state the state to set
   */
  public void setState(String state) {
    this.state = state;
  }
}

If we would have the Java Web Profile level 7 not 6 on HCP this would be very easy (and I could not write this blog) all you would have to do is add your EJB to your new service. So my service would look like:


/**
* REST
*/
package com.sap.myapp;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
* Simple REST Service
*/
@Path("/service")
public class MyService {
  @EJB MyEJB myEjb;
  @GET
  @Path("/")
  @Produces(MediaType.APPLICATION_JSON)
  public Response myService() throws ServletException {
    return Response.ok().entity(myEjb.doSomething()).build();
  }
}

This would result in a Nullpointer Exception as the EJB is not injected. So there is some more magic needed as long as we stay with Jave Web Profile 6. We have to do a JNDI lookup of the bean before we can use it. By doing JNDI we get a fully working container managed EJB and we have to do that in every service method that wants to use EJB.

So the extended service will look like the following:


/**
* REST
*/
package com.sap.myapp;
import javax.ejb.EJB;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
* Simple REST Service
*/
@Path("/service")
public class MyService {
  @EJB MyEJB myEjb;
  @GET
  @Path("/")
  @Produces(MediaType.APPLICATION_JSON)
  public Response ping() throws ServletException {
    lookupEJBs();
    return Response.ok().entity(myEjb.doSomething()).build();
  }
  private void lookupEJBs() throws ServletException {
    if (myEjb == null) {
      try {
        InitialContext ic = new InitialContext();
        myEjb = (MyEJB) ic.lookup("java:comp/env/ejb/MyEJB");
      }
      catch (NamingException e) {
        throw new ServletException(e);
      }
      catch (Exception e) {
        throw new ServletException(e);
      }
    }
  }
}

For the JNDI lookup to work you have to add your EJB to the web.xml:


  <ejb-local-ref>
    <ejb-ref-name>ejb/MyEJB</ejb-ref-name>
    <local>com.sap.myapp.MyEJB</local>
  </ejb-local-ref>

So you tried it all out and your ping-pong service works but your new service gives a 404? Then you forgot to add your new service class to “MyApplication” as we did with the PingService in Part 1 😉

Thats it, if you call now your application with the relative path “/rest/service” you get a JSON response.


{"state":"em is injected :-)"}

Explore the power of Jersey, even stuff as file upload can be done with a few simple annotations. If someone is interested in that I may add a third part to this small blog series 🙂

To report this post you need to login first.

3 Comments

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

  1. Anthony MULLER

    Hi Franck,

    Nice blog series, thanks!

    Personally, I have more experience about Apache CXF implemention of JAX-RS and to use JAXB for the databinding.

    But your sample is smart too, I just don’t like very much the ejb description for JNI lookup in the REST service class. Especially if there are a lot of EJB to declare…. :-/

    Is there a way to make the discovering automatic? (using a scan from the container for instance)

    Best regards,

    Anthony

    (0) 
  2. Anthony MULLER

    An other question about implementing a Java REST webservice and an HTML5 application in HCP.

    How can the HTML5 application discover/consume easily the REST webservice? As they will not be located in the same domain, what about any cross origin issues?

    Besr regards,

    Anthony

    (0) 
    1. Frank Schulten Post author

      Up to your first question, there is a way of lets say half automatic I use in bigger projects. There I use one EJB that loads any other other EJB by injection and has getters for them. This way I have to put only one EJB in my web.xml and have to do only one JNDI lookup.

      This will no longer be neccessary when HCP updates to Java Web Profile 7.

      For your second question there are three solutions. You can either pack the UI5 application in the same project as you develop it in the webcontent folder or you can create a HTML5 app in HCP and use a destination for the connection to your HCP backend. I also created some jersey filter that added cross domain capability. This was very handy for local UI5 development with connection to a HCP “real” backend (as I needed the database).

      (0) 

Leave a Reply