REST-Schnittstelle für JEE6 - Java Enterprise Edition 6

10.09.2012

REST-Schnittstelle für JEE6 - Java Enterprise Edition 6

In diesem Artikel wird erklärt, wie man mit ein paar Zeilen Quellcode ein Enterprise Java Bean (EJBs) um eine REST-Schnittstelle erweitert. Die Schnittstelle wird GET, POST und PUT unterstützen.

Es wird im Folgenden vorausgesetzt, dass das Beispiel aus JEE6-Tutorial durchgearbeitet wurde und die Klassen bekannt sind.

In dem Artikel soll eine EJB (Enterprise Java Bean) so erweitert werden, dass sie über eine REST-Schnittstelle angesprochen werden kann. Der Einfachheit halber verwenden wir in dem Beispiel die Klasse UrlManagerBean aus dem JEE6-Tutorial und erweitern sie um die Annotation @Path("/urlmanager"). Damit wird der Pfad der URL angegeben unter der der Service später zu erreichen ist.

package org.hameister.urlmanager;

import java.util.ArrayList;
import java.util.List;
import javax.ejb.Stateless;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

/**
 *
 * @author Hameister
 */
//@Interceptors(UrlManagerInterceptor.class)
@Stateless
@Named
@Path("/urlmanager")
public class UrlManagerBean {

    @PersistenceContext
    EntityManager em;

    public void addUrl(UrlManagerEntity entity) {
        System.out.println("Added");
        em.persist(entity);
    }


    public List<String> getUrls() {
        List<String> urls = new ArrayList<String>();

         Query query = em.createQuery("SELECT url FROM UrlManagerEntity url");
         List<UrlManagerEntity> resultList = query.getResultList();
         for(UrlManagerEntity entity : resultList) {
             urls.add(entity.getUrl());
         }
        return urls;
    }

Als erstes ergänzen wir eine Funktion, die die zuletzt hinzugefügte URL bestimmt und einen String zurückliefert. Dazu ergänzen wird die Annotation @GET, die angibt, dass es sich um ein HTTP-GET handelt. Bei @Path("lasturl") handelt es sich um einen weiteren Unterpfad für den Service. Der Wert @Produces("text/plain") legt den Rückgabetyp des REST-Services fest.

@GET
@Path("lasturl")
@Produces("text/plain")
public String lastUrl() {
     Query query = em.createQuery("SELECT url FROM UrlManagerEntity url");
     List<UrlManagerEntity> resultList = query.getResultList();
     return resultList.get(resultList.size()-1).getUrl();
}

Es ist anzumerken, dass die Überprüfung, ob die resultList leer oder null ist, der Übersichtlichkeit wegen, weggelassen wurde!

In dem Beispiel eben wurde ein einfacher String zurückgeliefert. Allerdings ist es auch möglich, komplexere Datenstrukturen als Rückgabewerte anzugeben. In dem folgenden Beispiel wird wieder die zuletzt hinzugefügte URL bestimmt, aber diesmal wird direkt das Objekt UrlManagerEntity an den Aufrufenden zurückgegeben. Als Rückgabetyp wurde mit der Annotation @Produces gesagt, dass es sich um das JSON-Format handelt.

@GET
@Path("lasturlmanager")
@Produces("application/json")
public UrlManagerEntity lastUrlManager() {
     Query query = em.createQuery("SELECT url FROM UrlManagerEntity url");
     List<UrlManagerEntity> resultList = query.getResultList();
     return resultList.get(resultList.size()-1);
}

Allerdings ist es auch möglich eine Liste mit Objekten vom Typ UrlManagerEntity zurückzuliefern. Dazu werden diesmal für @Produces die Typen {"application/xml", "application/json"} eingetragen. Das bedeutet, dass die Methode sowohl XML als auch JSON als Ergebnis liefern kann.

@GET
@Path("/findAll")
@Produces({"application/xml", "application/json"})
public List<UrlManagerEntity> findAll() {
     Query query = em.createQuery("SELECT url FROM UrlManagerEntity url");
     List<UrlManagerEntity> resultList = query.getResultList();
     return resultList;
}

Als letzte Methode soll noch die Möglichkeit angeboten werden, dass neue URLs über die REST-Schnittstelle in die Datenbank geschrieben werden können. Dazu definieren wir eine @POST-Methode, die JSON als Eingabetyp versteht und als Objekt ein entity vom Typ UrlManagerEntity übergeben bekommt.

@POST
@Path("/saveUrl")
@Consumes("application/json")
public void saveUrl(UrlManagerEntity entity) {
    em.persist(entity);
}

Damit die problemlose Serialisierung von UrlManagerEntity funktioniert, ist es notwendig Änderungen an der Klasse UrlManagerEntity vorzunehmen. Es werden die beiden Annotationen @XmlRootElement und @XmlAccessorType(XmlAccessType.FIELD) ergänzt.

Bei meinen Tests war es außerdem erforderlich, dass ein default-Konstruktor mit der Sichtbarkeit protected vorhanden war, damit die Testanwendung Test RESTful Web Services die Rückgabewerte der REST-Methoden richtig anzeigt. Ich bin allerdings der Meinung, dass das normalerweise nicht notwendig ist und nur auf ein Problem mit Jersey im Glassfish zurückzuführen ist.

package org.hameister.urlmanager;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author Hameister
 */
@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class UrlManagerEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String url;

    UrlManagerEntity() {
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Long getId() {
        return id;
    }

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

...

}

Zum Testen des REST-Service mit NetBeans muss als erstes die Anwendung auf dem Glassfish deployt werden. Beim ersten Deployment fragt NetBeans wie die Anwendung registriert werden soll.

Create New Project

Da es sich um eine JEE6-Anwendung handelt, sollte der erste Punkt ausgewählt werden. Dadurch wird eine Wrapper-Klasse ApplicationContext.java generiert.

Anschliessend selektiert man das Projekt und wählt im Kontextmenü den Menüpunkt Test RESTful Web Services... aus. In dem Dialog ist der Punkt Locally Generated Test Client (suitable for Internet Explorer, Firefox) selektiert.

Create New Project

Daraufhin öffnet sich der Standard-Browser mit der Seite Test RESTful Web Services. Dabei handelt es sich um eine Anwendung zum Testen der REST Services.

Create New Project

Auf der Seite sind alle deployten WebServices zu sehen, die nun einfach getestet werden können.

Beispielsweise kann eine neue URL in die Datenbank eingefügt werden indem man den Service saveURL mit dem Content {"url":"http://www.hameister.org"} aufruft. Als Format wird dabei JSON verwendet:

Create New Project

Es ist natürlich auch möglich den WebService von anderen Anwendungen aus anzusprechen. Beispielsweise kann auch Curl verwendet werden. Einfache GET-Anfragen können auch über einen Browser abgesetzt werden.

Weitere Informationen

Die Java API for RESTful Web Services (JAX-RS) 1.1 gehört zu JSR-311. Hier findet man das PDF mit der Spezifikation dazu.