GET / POST with RESTful Client API
There are many stuff in the internet how to work with RESTful Client API. These are basics. But even though the subject seems to be trivial, there are hurdles, especially for beginners.
In this post I will try to summarize my know-how, how I did this in real projects. I usually use Jersey (reference implementation for building RESTful services). See e.g. my other post. In this post, I will call a real remote service from JSF beans. Let’s write a session scoped bean RestClient.
package com.cc.metadata.jsf.controller.common; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; import java.io.Serializable; import javax.annotation.PostConstruct; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.context.FacesContext; /** * This class encapsulates some basic REST client API. */ @ManagedBean @SessionScoped public class RestClient implements Serializable { private transient Client client; public String SERVICE_BASE_URI; @PostConstruct protected void initialize() { FacesContext fc = FacesContext.getCurrentInstance(); SERVICE_BASE_URI = fc.getExternalContext().getInitParameter('metadata.serviceBaseURI'); client = Client.create(); } public WebResource getWebResource(String relativeUrl) { if (client == null) { initialize(); } return client.resource(SERVICE_BASE_URI + relativeUrl); } public ClientResponse clientGetResponse(String relativeUrl) { WebResource webResource = client.resource(SERVICE_BASE_URI + relativeUrl); return webResource.accept('application/json').get(ClientResponse.class); } }
In this class we got the service base URI which is specified (configured) in the web.xml.
<context-param> <param-name>metadata.serviceBaseURI</param-name> <param-value>http://somehost/metadata/</param-value> </context-param>
Furthermore, we wrote two methods to receive remote resources. We intend to receive resources in JSON format and convert them to Java objects. The next bean demonstrates how to do this task for GET requests. The bean HistoryBean converts received JSON to a Document object by using GsonConverter. The last two classes will not be shown here (they don’t matter). Document is a simple POJO and GsonConverter is a singleton instance which wraps Gson.
package com.cc.metadata.jsf.controller.history; import com.cc.metadata.jsf.controller.common.RestClient; import com.cc.metadata.jsf.util.GsonConverter; import com.cc.metadata.model.Document; import com.sun.jersey.api.client.ClientResponse; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.annotation.PostConstruct; import javax.faces.bean.ManagedBean; import javax.faces.bean.ManagedProperty; import javax.faces.bean.ViewScoped; /** * Bean getting history of the last extracted documents. */ @ManagedBean @ViewScoped public class HistoryBean implements Serializable { @ManagedProperty(value = '#{restClient}') private RestClient restClient; private List<Document> documents; private String jsonHistory; public List<Document> getDocuments() { if (documents != null) { return documents; } ClientResponse response = restClient.clientGetResponse('history'); if (response.getStatus() != 200) { throw new RuntimeException('Failed service call: HTTP error code : ' + response.getStatus()); } // get history as JSON jsonHistory = response.getEntity(String.class); // convert to Java array / list of Document instances Document[] docs = GsonConverter.getGson().fromJson(jsonHistory, Document[].class); documents = Arrays.asList(docs); return documents; } // getter / setter ... }
The next bean demonstrates how to communicate with the remote service via POST. We intent to send the content of uploaded file. I use the PrimeFaces’ FileUpload component, so that the content can be extracted as InputStream from the listener’s parameter FileUploadEvent. This is not important here, you can also use any other web frameworks to get the file content (also as byte array). More important is to see how to deal with RESTful Client classes FormDataMultiPart and FormDataBodyPart.
package com.cc.metadata.jsf.controller.extract; import com.cc.metadata.jsf.controller.common.RestClient; import com.cc.metadata.jsf.util.GsonConverter; import com.cc.metadata.model.Document; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.core.header.FormDataContentDisposition; import com.sun.jersey.multipart.FormDataBodyPart; import com.sun.jersey.multipart.FormDataMultiPart; import org.primefaces.event.FileUploadEvent; import java.io.IOException; import java.io.Serializable; import javax.faces.application.FacesMessage; import javax.faces.bean.ManagedBean; import javax.faces.bean.ManagedProperty; import javax.faces.bean.ViewScoped; import javax.faces.context.FacesContext; import javax.ws.rs.core.MediaType; /** * Bean for extracting document properties (metadata). */ @ManagedBean @ViewScoped public class ExtractBean implements Serializable { @ManagedProperty(value = '#{restClient}') private RestClient restClient; private String path; public void handleFileUpload(FileUploadEvent event) throws IOException { String fileName = event.getFile().getFileName(); FormDataMultiPart fdmp = new FormDataMultiPart(); FormDataBodyPart fdbp = new FormDataBodyPart(FormDataContentDisposition.name('file').fileName(fileName).build(), event.getFile().getInputstream(), MediaType.APPLICATION_OCTET_STREAM_TYPE); fdmp.bodyPart(fdbp); WebResource resource = restClient.getWebResource('extract'); ClientResponse response = resource.accept('application/json').type(MediaType.MULTIPART_FORM_DATA).post( ClientResponse.class, fdmp); if (response.getStatus() != 200) { throw new RuntimeException('Failed service call: HTTP error code : ' + response.getStatus()); } // get extracted document as JSON String jsonExtract = response.getEntity(String.class); // convert to Document instance Document doc = GsonConverter.getGson().fromJson(jsonExtract, Document.class); ... } // getter / setter ... }
Last but not least, I would like to demonstrate how to send a GET request with any query string (URL parameters). The next method asks the remote service by URL which looks as http://somehost/metadata/extract?file=<some file path>
public void extractFile() { WebResource resource = restClient.getWebResource('extract'); ClientResponse response = resource.queryParam('file', path).accept('application/json').get( ClientResponse.class); if (response.getStatus() != 200) { throw new RuntimeException('Failed service call: HTTP error code : ' + response.getStatus()); } // get extracted document as JSON String jsonExtract = response.getEntity(String.class); // convert to Document instance Document doc = GsonConverter.getGson().fromJson(jsonExtract, Document.class); ... }
Reference: GET / POST with RESTful Client API from our JCG partner Oleg Varaksin at the Thoughts on software development blog.
You may also want to have a look at https://github.com/taimos/httputils