This article explains the basic concepts of Jakarta RESTful web services (specifically, JAX-RS, formerly the Java API for RESTful web services). Inside, we detail the step-by-step implementation of web services using Apache Wink.
Before we begin, let’s revisit some of the essential terms you should know to better understand web services, REST, and related concepts.
Extensible Markup Language (XML): XML is a A standard for document markup that uses generic syntax to mark-up data or information with easy to digest, human-readable tags. The XML standard is endorsed by the World Wide Web Consortium (W3C).
JavaScript Object Notation (JSON): JSON is a data-interchange format, lightweight in nature, based upon JavaScript. JSON is programming-language neutral. However it does use conventions from developer languages including C, C++, C#, Java, JavaScript, Perl, and Python.
JSON Schema: This is a JavaScript object notation document describing the structure of JSON and that also constrains the information inside of other JSON documents.
RESTful: Refers to software and services that adhere to Representational State Transfer (REST) constraints.
XML: Extensible Markup Language.
XML Namespace: A collection of names that are identified by a Uri reference. They are used in XML documents for element types and attribute names.
XML Schema: An XML document that describes the structure and constrains the contents of other XML documents.
XML Schema Definition Language: An XML syntax for writing XML schemas, recommended by the World Wide Web Consortium (W3C).
HATEOAS: From Wikipedia: “Hypermedia as the Engine Of Application State (HATEOAS) is a constraint of the Rest Application Architecture that distinguishes it from other network application architectures. With HATEOAS, a client interacts with a network application whose application servers provide information dynamically through hypermedia. A Rest client needs little to no prior knowledge about how to interact with an application or server beyond a generic understanding of hypermedia.
Introduction to JAX-RS and REST
This product web service was built using Apache Wink. It can add, update, delete and retrieve products in Memory. REST stands for Representational State Transfer and is based on the concepts of Roy Fielding’s dissertation work as part of his thesis. It works on HTTP and has the following differences when compared to SOAP:
It works using the HTTP protocol, as opposed to SOAP, which uses its own protocol.
It is capable of using HTTP GET, PUT, POST, and DELETE. SOAP only uses POST.
It relies on the HTTP infrastructure. SOAP is transport neutral.
Producers and consumers are aware of content exchange.
At this time, many non-functional requirements or any WS-* standards are not supported.
Installations Required for JAX-RS and REST
Creating a Server/Web Service
The following are the steps needed to build the web service using Apache Wink:
Step One: Create a Dynamic Web Project in Eclipse.
Step Two: Build a Core Application that performs CRUD Operations.
/** * */ package me.sumithpuri.rest.persistence; import java.util.ArrayList; import java.util.List; import me.sumithpuri.rest.vo.Product; /** * @author sumith_puri * */ public class ProductPersistenceManager { private List productDatabase = new ArrayList(); private static ProductPersistenceManager persistenceManager; private static int id=0; private ProductPersistenceManager() { } public void add(Product product) { System.out.println("database: added one product"); // atomic id creation id++; product.setId(id); productDatabase.add(product); } public List get() { System.out.println("database: retrieved all products"); return productDatabase; } public void update(long productId, String productName) { System.out.println("database: modified one product"); for(int i=0;i<productDatabase.size();i++) { Product product = productDatabase.get(i); if(product.getId()==productId) { product.setName(productName); productDatabase.remove(i); productDatabase.add(i,product); } } return; } public void delete(long productId) { System.out.println("database: deleted one product"); for(int i=0;i<productDatabase.size();i++) { Product product = productDatabase.get(i); if(product.getId()==productId) productDatabase.remove(i); } return; } public static ProductPersistenceManager getInstance() { if(persistenceManager==null) { synchronized(ProductPersistenceManager.class) { if(persistenceManager==null) { persistenceManager = new ProductPersistenceManager(); } } } return persistenceManager; } }
Step Three: Add the Supporting JAR Files in the Build Path.
Step Four: Create the REST Web Service using Apache Wink (GET, POST, DELETE, PUT).
package me.sumithpuri.rest.webservice; import java.util.List; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import me.sumithpuri.rest.persistence.ProductPersistenceManager; import me.sumithpuri.rest.vo.Product; /** * @author sumith_puri * */ @Path("product") public class ProductWebService { ProductPersistenceManager persistenceManager = ProductPersistenceManager.getInstance(); @GET @Produces(MediaType.TEXT_PLAIN) public String getProducts() { List products = persistenceManager.get(); String productList = new String(); for(Product producti: products) { productList+=producti.toString() + "\n"; } // return as plain text - other types include xml, json return productList; } @POST public String addProducts(String productStr) { Product product = new Product(); product.setName(productStr); persistenceManager.add(product); return productStr; } @DELETE @Path("/{id}") public void deleteProduct(@PathParam(value="id") long id) { persistenceManager.delete(id); return; } @PUT @Path("/{id}") public void modifyProduct(@PathParam(value="id") long id, String productName) { persistenceManager.update(id, productName); return; } }
Step Five Configure the Application for Apache Wink.
To allow Apache Wink to locate this service as a REST web service, you can either define an additional class or configure an application file. We are using an application file to mention all our web services.
You can place this under WEB-INF/and name the file simply ‘application’ (without any extension).
me.sumithpuri.rest.webservice.ProductWebService
Next, you need to specify the servlet-related configuration for allowing Apache Wink REST Servlet to locate this application configuration. You do that by specifying the location of ‘application’ as the parameter ‘applicationConfigLocation’ in WEB-INF/web.xml
products restService org.apache.wink.server.internal.servlet.RestServlet applicationConfigLocation /WEB-INF/application restService /rest/*
Step Six: Deploy as an Apache Tomcat Web App, directly:
Create a Client/Web Service Client
The steps to test the web service or write a REST client are as follows:
Step One: Create a Java project in Eclipse.
Step Two: Include the Support Client JAR files in the Classpath.
Step Three: Build the Client to Test or Access the GET method.
Step Four: Build the Client to Test or Access the POST method.
Step Five:. Build the Client to Test or Access the DELETE method.
Step Six: Build the Client to Test or Access the PUT method.
package me.sumithpuri.rest.client; import javax.ws.rs.core.MediaType; import org.apache.wink.client.ClientConfig; import org.apache.wink.client.Resource; import org.apache.wink.client.RestClient; /** * @author sumith_puri * */ class ProductRESTClient { static String REST_WEB_SERVICE="http://localhost:8080/products/rest/product"; static ClientConfig clientConfig = new ClientConfig(); /** * @param args */ public static void main(String[] args) throws Exception { try { ProductRESTClient restClient = new ProductRESTClient(); System.out.println("Apache Wink Based REST Client"); System.out.println("Sumith Kumar Puri (c) 2015"); System.out.println("============================="); restClient.configureClient(); System.out.println(); restClient.invokeGET(); System.out.println(); String product="Sumith Puri" + (int) (Math.random()*9999); restClient.invokePOST(product); System.out.println(); product="Sumith Puri" + (int) (Math.random()*9999); restClient.invokePOST(product); System.out.println(); product="Sumith Puri" + (int) (Math.random()*9999); restClient.invokePOST(product); System.out.println(); product="Sumith Puri" + (int) (Math.random()*9999); restClient.invokePOST(product); System.out.println(); restClient.invokeGET(); System.out.println(); restClient.invokeDELETE(2L); System.out.println(); restClient.invokeGET(); System.out.println(); product="Sumith Puri" + (int) (Math.random()*9999); restClient.invokePOST(product); System.out.println(); product="Sumith Puri" + (int) (Math.random()*9999); restClient.invokePOST(product); System.out.println(); restClient.invokeDELETE(4L); System.out.println(); restClient.invokeGET(); System.out.println(); restClient.invokePUT(3L,"Sumith Puri"); System.out.println(); restClient.invokeGET(); } catch (Exception e) { e.printStackTrace(); } } public void configureClient() { } public void invokeGET() { System.out.println("Testing GET command...."); RestClient restClient = new RestClient(clientConfig); Resource resource = restClient.resource(REST_WEB_SERVICE); String response = resource.accept("text/plain").get(String.class); System.out.printf(response); System.out.println("...GET command is successful"); } public void invokePOST(String product) { System.out.println("Testing POST command..."); RestClient restClient = new RestClient(clientConfig); Resource resource = restClient.resource(REST_WEB_SERVICE); resource.contentType(MediaType.TEXT_PLAIN).accept(MediaType.TEXT_PLAIN).post(String.class,product); System.out.println("...POST command is successful"); } public void invokePUT(Long id, String productName) { System.out.println("Testing PUT command..."); RestClient restClient = new RestClient(clientConfig); Resource resource = restClient.resource(REST_WEB_SERVICE+"/"+id); resource.contentType(MediaType.TEXT_PLAIN).accept(MediaType.TEXT_PLAIN).put(String.class, productName); System.out.println("...PUT command is successful"); } public void invokeDELETE(Long id) { System.out.println("Testing DELETE command..."); RestClient restClient = new RestClient(clientConfig); Resource resource = restClient.resource(REST_WEB_SERVICE+"/"+id); resource.contentType(MediaType.TEXT_PLAIN).accept(MediaType.TEXT_PLAIN).delete(); System.out.println("...DELETE command is successful"); } }
The following is the output from the first run of the client:
Conclusion
In this blog, you have seen how to build REST web services using Apache Wink for basic data types. Next, I will write about how to include Jackson or Jettison as the stream reader or stream writer JSON libraries so that we can read and write complex or application object types. Stay tuned!
Editor’s Note: You can read part two of this series here: REST Web Services with JAX-RS, JSON, and JAXB.