Over a million developers have joined DZone.

NetBeans in the Classroom: Using the NetBeans Rest Service Wizard

· Java Zone

Discover how powerful static code analysis and ergonomic design make development not only productive but also an enjoyable experience, brought to you in partnership with JetBrains

Ken Fogel is the Program Coordinator and Chairperson of the Computer Science Technology program at Dawson College in Montreal, Canada. He is also a Program Consultant to and part-time instructor in the Computer Institute of Concordia University's School of Extended Learning. He blogs at omniprogrammer.com and tweets @omniprof.

There are many good tutorials on creating REST web services with NetBeans that a web will reveal.There are two problems with the tutorials. First, they do not explain what the different choices are for or why you choose one over the other. Second, they do not address the specific issue of retrieving a collection from a service or if they do then there is no explanation. This article will look at these issues.

POJO versus Stateless Session Bean

When my students are taking the web services course they have already completed a course in server side programming with JSF, JPA and CDI using NetBeans as the IDE, GlassFish as the server and MySQL as the RDBMS. When we use NetBeans to create a web service we see the following choices:

If you select Web Services from Entity classes it will generate a Stateless Session bean as a façade to an abstract class. The abstract class is an implementation of a standard JPA controller that is not CDI compatible without some rewriting. My students are taught to create a JPA controller that contains the specific methods the application needs, no more or no less. This
controller can then be tested with Arquillian to ensure it is operating properly. Therefore my students are using the wizard after the controller is written.

The third option is much the same as the first except it will now generate the entity classes as well as the abstract controller and stateless session bean. There is absolutely nothing wrong with these wizards. They just don’t work the way I want my students to work.

The second choice, Web Services from Patterns, is the choice I have my students make. There are three types of patterns that can be chosen and I have my students always select Simple Root Resource.

The wizard then allows you to choose the package where the service class will be created, the path to the service on the server, the class name, MIME type and representation class.

All of these can be easily changed in the generated code. For a program that delivers records from a database of aquarium fish for which the entities and controller classes are already coded my choices are:

Here is the file that it generates:

package com.kenfogel.rest3;

import com.kenfogel.entities.Fish;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.PathParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;

/**
 * REST Web Service
 *
 * @author Ken
 */
@Path("fishies")
public class FishiesRS {

    @Context
    private UriInfo context;

    /**
     * Creates a new instance of FishiesRS
     */
    public FishiesRS() {
    }

    /**
     * Retrieves representation of an instance of com.kenfogel.rest3.FishiesRS
     * @return an instance of com.kenfogel.entities.Fish
     */
    @GET
    @Produces("application/xml")
    public Fish getXml() {
        //TODO return proper representation object
        throw new UnsupportedOperationException();
    }

    /**
     * PUT method for updating or creating an instance of FishiesRS
     * @param content representation for the resource
     * @return an HTTP response with content of the updated or created resource.
     */
    @PUT
    @Consumes("application/xml")
    public void putXml(Fish content) {
    }
}

My students can now implement any GET, POST, PUT or DELETE methods that correspond to their JPA controller or other parts of their service they wish to expose. It is a POJO and so can run in the simplest of containers.

Here is what my final service class looks like after I have edited it so that retrieves either all the
fish or only one fish. The service is using CDI to access the JPA controller class.

package com.kenfogel.rest;

import com.kenfogel.beans.FishJpaController;
import com.kenfogel.entities.Fish;
import java.util.List;
import javax.inject.Inject;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.PathParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;

/**
 * REST Web Service
 *
 * @author Ken
 */
@Path("fishies")
public class FishRest {

    @Inject
    private FishJpaController fishjpa;

    @Context
    private UriInfo context;

    /**
     * Creates a new instance of FishRest
     */
    public FishRest() {
    }

    /**
     * Retrieves all the fish
     *
     * @return an instance of com.kenfogel.entities.Fish
     */
    @GET
    @Produces("application/xml")
    public List getAllFishXml() {
        return fishjpa.findFishEntities();
    }

    /**
     * Retrieves a specific fish
     *
     * @param id
     * @return an instance of com.kenfogel.entities.Fish
     */
    @GET
    @Produces("application/xml")
    @Path("{id}/")
    public Fish getOneFishXml(@PathParam("id") int id) {
        return fishjpa.findFish(id);
    }

    /**
     * PUT method for updating or creating an instance of FishRest
     *
     * @param content representation for the resource
     */
    @PUT
    @Consumes("application/xml")
    public void putXml(Fish content) throws Exception {
        fishjpa.create(content);    }
}

When the wizard creates your REST service it also creates a second class called ApplicationConfig.java. You do not want to change anything in this class except if you wish to change the resource path. Here is where it is declared.

@javax.ws.rs.ApplicationPath("webresources")
public class ApplicationConfig extends Application {

The value of the ApplicationPath annotation is the name of the resource path so you can change it easily here.

@javax.ws.rs.ApplicationPath("awesomewebservice")

The Stateless Session Bean Choice

Stateless session beans are pooled by a Java Enterprise server. If you want to the container to pool the service objects then you can change the POJOs to stateless session beans with just a few annotations.

The REST Client

With the service complete, it is time to create the client. The wizard is straightforward and there is just one dialog of choices. By selecting the REST service from your project, NetBeans will create a client with methods for everything found in the server service class.

Here is the code generated by the wizard:

package com.kenfogel.fishfxtable.restclient2;

import javax.ws.rs.ClientErrorException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.WebTarget;

/**
 * Jersey REST client generated for REST resource:FishRest [fishies]<br>
 * USAGE:
 * <pre>
 *        FishRSClient2 client = new FishRSClient2();
 *        Object response = client.XXX(...);
 *        // do whatever with response
 *        client.close();
 * </pre>
 *
 * @author Ken
 */
public class FishRSClient2 {
    private WebTarget webTarget;
    private Client client;
    private static final String BASE_URI = 
				"http://localhost:8080/RestServerS15/webresources";

    public FishRSClient2() {
        client = javax.ws.rs.client.ClientBuilder.newClient();
        webTarget = client.target(BASE_URI).path("fishies");
    }

    public <T> T getOneFishXml(Class<T> responseType, String id) 
throws ClientErrorException {
        WebTarget resource = webTarget;
        resource = resource.path(java.text.MessageFormat.
format("{0}", new Object[]{id}));
        return resource.request(javax.ws.rs.core.MediaType.APPLICATION_XML).
get(responseType);
    }

    public <T> T getAllFishXml(Class<T> responseType) throws ClientErrorException {
        WebTarget resource = webTarget;
        return resource.request(javax.ws.rs.core.MediaType.APPLICATION_XML).
get(responseType);
    }

    public void putXml(Object requestEntity) throws ClientErrorException {
        webTarget.request(javax.ws.rs.core.MediaType.APPLICATION_XML).
put(javax.ws.rs.client.Entity.
entity(requestEntity, javax.ws.rs.core.MediaType.APPLICATION_XML));
    }

    public void close() {
        client.close();
    }
}

To retrieve a single fish based on the primary key field I can now simply add the following method to my client program:

    /**
     * Just for fun we show a single fish
     */
    public void showAFish() {
        FishRSClient fishRSClient = new FishRSClient();
        Fish aFish = fishRSClient.getFishByIdXml(Fish.class, "1");
        fishRSClient.close();
        System.out.println(aFish);
    }

As the comment in the generated FishRSClient class says, create the REST client object, call upon the appropriate method and then close the client. Look at the method I am calling and compare it to its signature:

Fish aFish = fishRSClient.getFishByIdXml(Fish.class, "1");

public <T> T getOneFishXml(Class<T> responseType, String id) 
throws ClientErrorException {

Notice that you must pass as a parameter the Class class as the responseType. This allows the generically coded getOneFishXML to return the correct class. Things go off the rails when I want a list of fish. The signature of the method is:

public <T> T getAllFishXml(Class<T> responseType) throws ClientErrorException {
So I write following the pattern of the first example:
List<Fish> list = fishRSClient.findAllXml(List<Fish>.class);

It won’t work. The problem is that List.class is invalid. There is no such syntax. How then do you indicate that the response type is any type of collection class? The solution is to use the GenericType class. Here is where tutorials either just don’t show a collection being returned from a service or do show it but don’t explain what is going on.

The GenericType comes from javax.ws.rs.core. It exists to deal with this specific problem when a response type itself contains a generic component such as List. To use it you must change the responseType in the service client.

public <T> T getAllFishXml(GenericType<T> responseType) throws ClientErrorException {
        WebTarget resource = webTarget;
        return resource.request(javax.ws.rs.core.MediaType.APPLICATION_XML).
get(responseType);
}

Now we need to change the way we call this method. We must declare an object of type GenericType and at the same time we must define the GenericType class. This is a little strange but it suggests to methat GenericType is a kludge fix to this problem. If you know better than please leave a comment. Here is the working method:

    public void displayTheTable() throws SQLException {
        FishRSClient fishRSClient = new FishRSClient();
        // genericType is used when the return is a list
        GenericType<List<Fish>> gType = new GenericType<List<Fish>>() {};
        List<Fish> list = fishRSClient.findAllXml(gType);
        fishRSClient.close();
        fishDataTable.setItems(FXCollections.observableArrayList(list));
    }

On the line that declares the GenericType object named gType you will see that on the left side there is open and close braces. These are required.

If you are curious, this is the output of the JavaFX application that consumes the service:

Doing away with the wizard on the client side

If you want to code your requests to the service directly without the wizard generated class then read Adam Bien’s excellent post at:

http://www.adam-bien.com/roller/abien/entry/the_executable_feel_of_jax.

Final Credit

When I ran in to this problem, I searched long and hard and here was the stack overflow question and answer that showed me the solution:

http://stackoverflow.com/questions/15275431/rest-listentity-return-causing-errors

Learn more about Kotlin, a new programming language designed to solve problems that software developers face every day brought to you in partnership with JetBrains.

Topics:

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}