REST with Java (JAX-RS) using Jersey - jersey

I developed a restful web service via Jersey in Java (JAX-RS) : http://www.vogella.com/articles/REST/article.html
Then I used the Hibernate Technology to map the data to the database.
Finally I developed an android application to display data.
This is an example of a method in my Web Service :
#GET
#Path("/project_id/username/get/{projectId}/{username}/")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response deliverableList(#PathParam("projectId") long projectId,
#PathParam("username") String username) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
List<Deliverable> list = null;
try {
list= (List<Deliverable>) session.createQuery(
"from Deliverable as d where d.project.id= :id").setLong("id", projectId).list();
} catch (HibernateException e) {
e.printStackTrace();
session.getTransaction().rollback();
}
session.getTransaction().commit();
return Response.status(201).entity(list).build();
}
as you see I used "Response.status(201).entity(list).build()" to transfer the list of data. Is it a good way? if not what is your suggestion to transfer the data. Please support your explanation with some codes and examples.

Response.ok().enity(object).build() is the correct way to return data
You really want to move your hibernate stuff to a data access tier... it's going to be hard to manage intermingled with your service tier
I completely disagree with smcg about using a helper method to map the java to json. Use the jax-rs annotations on your beans to do it unless you have really complicated requirements: see http://wiki.fasterxml.com/JacksonAnnotations

It seems to me like you are relying on something to automagically map your Java objects to JSON - probably Jackson. I do not personally prefer this method. Instead, I use Jettison and create my own mapping from Java to a Jettison JSONObject object. Then I use the JSONObject (or JSONArray) as the entity. My return statement would be something like this:
return Response.ok().entity(myObjectAsJSON).build();
In the case of returning a list of things, use a JSONArray instead of a JSONObject.
You'll want a helper method to map the Java object to JSON.
public JSONArray deliverableListToJSON(List<Deliverable> deliverables)
throws JSONException {
JSONArray result = new JSONArray();
for(Deliverable deliverable : deliverables) {
JSONObject deliverableJSON = new JSONObject();
deliverableJSON.put("importantValue", deliverable.getImportantValue());
result.put(deliverableJSON);
}
return result;
}
This approach gives you more flexibility and doesn't force you to have public getters and setters for all your fields.

Related

SpringMVC where to put common code between controller's methods

I'm working on an existing codebase and I'm seeing this pattern in all the controller methods. Same variables are declared in the beginning and the code is placed inside the try catch block which is also same across all the methods. I was wondering if there's a way to push the common code across methods inside a BaseController. So that I don't have to declare the common variables inside each method and the try catch block functionality is also delegated to someplace else.
At first, I created a BaseController class, annotated it with #Controller annotation and extended my controller to be its subclass. Then I moved all the common variables to the BaseController. The problem is, once I modify these variables inside the controller's method, they retain their values even in the next request which is problematic.
#RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
public ResponseEntity delete(#PathVariable("id") Integer id)
{
HashMap response = new HashMap();
boolean success = false;
List errors = new ArrayList();
HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
String message = "";
try
{
purchaseInvoiceService.delete(id);
success = true;
message = "Purchase Invoice Deleted";
httpStatus = HttpStatus.OK;
} catch (Exception e)
{
errors.add(new ErrorMessage("error", e.getMessage()));
e.printStackTrace();
}
response.put("success", success);
response.put("errors", errors);
response.put("message", message);
return new ResponseEntity(response, httpStatus);
}
I want to refactor this patter so that in each method I just have to contain only the call to the service and conditionally setting the success and httpstatus variable (present in BaseController) and then returning the response using response() method present in BaseController which adds the data variable and it's return type is ResponseEntity.
Edit 1:
This endpoint returns a list of all purchase invoices, currently, its just returning the HashMap which gets converted to JSON. The point I'm trying to make is that these response, success, errors, httpStatus variables and the part where all these variables are put in response HashMap() are a part of every method inside each controller, I'd like to refactor these to something similar to ResponseFactory as well. So I'm thinking to pass the List to ResponseFactory as well which will then structure all the response and return in the form of ResponseEntity. Just want to know if I'm doing it correctly.
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity getAll() {
HashMap response = new HashMap();
boolean success = false;
List errors = new ArrayList();
HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
String message = "";
Map data = new HashMap();
try {
List<PurchaseInvoice> purchaseInvoices = purchaseInvoiceService.getAll();
data.put("purchaseInvoices", purchaseInvoices);
success = true;
message = "Purchase Invoice List";
httpStatus = httpStatus.OK;
} catch (Exception e) {
errors.add(new ErrorMessage("error", e.getMessage()));
e.printStackTrace();
}
response.put("success", success);
response.put("errors", errors);
response.put("message", message);
response.put("data", data);
return new ResponseEntity(response, httpStatus);
}
Your phrase: "Then I moved all the common variables to the BaseController" sounds confusing.
A controller in spring is just a Singleton with an additional "ability" provided by spring: its something that is exposed as a web endpoint (less relevant for your specific question).
Being Singleton means that there is one instance in the ApplicationContext.
So if the variables were moved like this:
class BaseController {
protected Field1 field1;
protected Field2 field2;
....
}
Then there is certainly a problem, you've actually introduced a state to the controller, and this state is shared among all the requests.
Long story short, don't create stateful controllers
Having said that the idea of refactoring is good. Just the way to refactor probably is wrong:
Instead, I suggest to consider the following refactoring:
Create class responseFactory with some static methods:
class ResponseFactory {
public static ResponseEntity success(String message) {
here prepare the map with boolean success and everything
}
}
Now the controller becomes:
class Controller {
public ResponseEntity delete(#PathVariable("id") Integer id) {
purchaseInvoiceService.delete(id); // I'll talk about exceptions later
return ResponseEntityFactory.success("Purchase Invoice Deleted");
}
}
Now as for exceptions - this is somewhat confusing - the code basically says that the response would be successful despite the errors.
So if you have to leave it like this, the ResponseEntityFactory will have to get the List of errors as well or something, but in general, Spring has a pretty powerful exception handling mechanism to map the exceptions thrown on the backend (service, DAO, whatever) to the meaningful non-200 response.

Rest api filtering in spring boot

I have a project in spring boot, in my controller I have many methods with similar functionality.
Methods for searching post, popular, latest etc and the urls with slight variation like -
url 1 - search/{topicId}
url 2 - search/popular/{topicId}
url 3 - search/latest/{topicId}
What I want, is to have a single method with filter in url like search/{topicId}?filter=popular
How to achieve this in spring boot?
OOPs... it does not depend on SpringBoot. It is simply a URL mapping...You can accept the type as a request param and can process as per business.....
#Controller
public class BookController {
#GetMapping(value = "/search/{topicId}")
#ResponseBody
public List<Object> getBooksByType(#RequestParam String type) {
try{
if("popular".equalsIgnoreCase(type)){
//do your business stuffs
}else if ("latest".equalsIgnoreCase(type)){
//do your business stuffs
}
}catch(Exception e){
e.printStackTrace();
}
return new ArrayList<>();
}
}

Stream the data content directly from database to the HTTP

Right now we are holding file in our postgresql database and mapping that content using byte[] field in our entity. I need to investigate if we could
stream the content data direct from the database to the HTTP output stream, and do the same thing in opposite way so stream binary data from HTTP into database using jpa Blob data type. I know that Blob has methods getBinaryStream and setBinaryStream so its may work, and we do not need hold data into memory.
What I am concern are database transaction, because we are mapping entity into DTO, and the second thing is broken Http request and data may be lost in some point.
Is there are anybody who had any experience with that solution ?
Solution for stream-reading data from BLOBs:
Existing BLOB data are streamed by passing OutputStream (provided by the servlet container) into transactional method which writes entity blob data to the stream from inside transaction. Note that the content type of response is set before writing the data.
Entity class:
public class Attachment {
private java.sql.Blob data;
public java.sql.Blob getData() { return data; }
}
Service method:
#Transactional(readOnly = true)
public void copyContentsTo(long attachmentId, OutputStream outputStream) throws IOException {
Attachment dbAttachment = attachmentRepository.findOne(attachmentId);
try (InputStream is = dbAttachment.getData().getBinaryStream()) {
IOUtils.copy(is, outputStream);
} catch (SQLException e) {
throw new ParameterException("Cannot extract BLOB for attachment #" + attachmentId, e);
}
}
REST API Spring Controller method:
#GetMapping(value = "/api/project-attachment/{attachment-id}/content")
#ResponseStatus(HttpStatus.OK)
public void getAttachmentContent(
#PathVariable("attachment-id") long attachmentId,
HttpServletResponse response,
OutputStream stream) throws IOException {
response.setContentType(getMime(attachmentId));
attachmentService.copyContentsTo(attachmentId, stream);
}
Lucasz, Spring Content for JPA does exactly what you are asking. Designed to make it really easy to create Spring applications that handle content (documents, images, video's etc). It supports a range of backend stores one of which being relational DBs and obviously they uses BLOBs.
This JPA module will stream uploaded files from the request input stream directly to the database and vice versa thus it never stores the entire file in-memory which would obviously cause problems with very large files.
It would save you from having to write ANY of the code in #tequilacat's answer.
Probably worth a look.

How to handle exceptions with Swagger?

I am building some test APIs using swagger (1.5) and JAX-rs with Jersey (1.13) and I m trying to implement exception handling. For example I have the following code when receiving the results from my DB (Elasticsearch)
#POST
#Path("/category")
#ApiOperation(value="returns products")
#Produces({ "application/json" })
public Response getPostCategories(
#ApiParam(value="keyphrase, required=true) #QueryParam("keyphrase") String keyphrase,
#ApiParam(value="category) #QueryParam("category") String category,
#Context SecurityContext securityContext)
throws WebApplicationException {
SearchRequest searchRequest = new SearchRequest();
searchRequest.setKeyphrase(keyphrase);
searchRequest.setCategory(category);
SearchCategoryQuery categoryQuery = new SearchCategoryQuery();
String searchResponse = null;
try
{
searchResponse = categoryQuery.searchCategory(searchRequest);
}
catch (WebApplicationException ex)
{
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity("results no found").type(javax.ws.rs.core.MediaType.APPLICATION_JSON).build());
}
return Response.ok(searchResponse).build();
}
However, in the output swagger always prints the same response
What I need instead is to receive the error messages I specify in each exception. Any ideas?
Swagger by itself does not handle application exceptions as yet.
You will either need to create custom Exception classes (that extend java.lang.exception) or use the existing ones (like WebApplicationException that you are already using) and make the API definition throw these errors. So basically you need to use Java/J2EE/Jersey to throw proper exceptions. Swagger UI will display them for you.
Check this link for details on REST exception handling with Spring.

Spring Boot RequestMapping with non-standard produces value returning 406 error when returning JAXB annotated object

I'm creating a Spring Boot app to replace a legacy api application, so all the routes/headers/etc are already set in stone. In that legacy app we used custom Accept headers to include both the version and the content type. So our Accept header is like:
catalog.v1.xml or catalog.v2.json etc.
Here is my request mapping for the method that is handling the request. I'm trying to handle the v1.xml one now. Spring is finding the correct method and the whole method is executed and it returns my JAXB annotated object:
#RequestMapping(value = "/catalog", method = RequestMethod.GET, produces="application/catalog.v1.xml")
How do I make sure Spring finds this matching handler method based on my Accept header, but knows that the output should be XML and marshall my JAXB object accordingly?
You need to provide Spring MVC with an HttpMessageConverter for your custom media type. To do so, I'd take advantage of Spring Boot automatically adding any HttpMessageConverter beans to Spring MVC's default configuration by configuring a bean that knows how to convert application/catalog.v1.xml:
#Bean
public Jaxb2RootElementHttpMessageConverter catalogXmlConverter() {
Jaxb2RootElementHttpMessageConverter xmlConverter = new Jaxb2RootElementHttpMessageConverter();
xmlConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("application", "catalog.v1.xml")));
return xmlConverter;
}
So once I realized I was changing the wrong configuration, and from deep debugging into Spring code, I realized I needed to replace or modify the message converter behavior. Here is my solution below. They don't make it super easy. If anyone has a simpler way of doing this, please let me know. This works.
public void extendMessageConverters(List<HttpMessageConverter<?>> converters)
{
for (HttpMessageConverter<?> converter : converters) {
if (converter.getClass() == MappingJackson2HttpMessageConverter.class){
MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter)converter;
MediaType jsonType = new MediaType("application", "catalog.v2.json");
MediaType jsonpType = new MediaType("application", "catalog.v2.jsonp");
List<MediaType> mediatTypes = new ArrayList<>(jacksonConverter.getSupportedMediaTypes());
mediatTypes.add(jsonpType);
mediatTypes.add(jsonType);
jacksonConverter.setSupportedMediaTypes(mediatTypes);
}
else if (converter.getClass() == Jaxb2RootElementHttpMessageConverter.class){
Jaxb2RootElementHttpMessageConverter xmlConverter = (Jaxb2RootElementHttpMessageConverter) converter;
MediaType xmlType = new MediaType("application", "catalog.v1.xml");
// Since the SupportMediaTypes list is unmodifiable, we have to create a new one based on it
// and replace it completely
List<MediaType> mediatTypes = new ArrayList<>(xmlConverter.getSupportedMediaTypes());
mediatTypes.add(xmlType);
xmlConverter.setSupportedMediaTypes(mediatTypes);
}
}
}

Resources