New Rest API in Rest Extender not working in Liferay - java

I have Create one module of type rest in Liferay7 Community Edition. The one application class created with some predefined rest-api.I have written my own api.but the api written by me are not working only predefined api are working. Please find my code below:
package com.codemaster.application;
import java.util.Collections;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Application;
import org.osgi.service.component.annotations.Component;
#ApplicationPath("/greetings")
#Component(immediate = true, service = Application.class)
public class ClickRestApplication extends Application {
public Set<Object> getSingletons() {
return Collections.<Object>singleton(this);
}
#GET
#Produces("text/plain")
public String working() {
return "It works!";
}
#GET
#Path("/morning")
#Produces("text/plain")
public String hello() {
return "Good morning!";
}
#GET
#Path("/morning/{name}")
#Produces("text/plain")
public String morning(
#PathParam("name") String name,
#QueryParam("drink") String drink) {
String greeting = "Good Morning " + name;
if (drink != null) {
greeting += ". Would you like some " + drink + "?";
}
return greeting;
}
#GET
#Path("/demo")
#Produces("text/plain")
public String verify() {
return "Verify User!";
}
#GET
#Path("/dummy")
#Produces("text/plain")
public String dummy()
{
return "Dummy Response";
}
}
What is the issue in my code?

Related

Develop Spring Boot API to get response based on range

I'm learning Java and as part of it, trying to develop a Spring Boot API that communicates with MongoDB Atlas cluster. I'm trying to write an API that would return items based on the price range value where min and max values are provided dynamically. Please help me understand where am I doing a mistake by going through the code below. I strongly feel it has to do with #Query and #PathVariable, I feel #RequestParam should be used in place of #PathVariable, however, I'm not sure what to use in place of #Query in the repository.
Here is the code of the repository
package com.webshoppingbackend.springboot.repository;
import java.util.List;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import com.webshoppingbackend.springboot.model.WebShoppingItem;
public interface WebShoppingRepository extends MongoRepository<WebShoppingItem, String> {
#Query("{category:?0}")
List<WebShoppingItem> getItemsByCategory(String category);
#Query("{brand:?0}")
List<WebShoppingItem> getItemsByBrand(String brand);
#Query("{price:{$lt:?0,$gt:?1}}")
List<WebShoppingItem> getItemsBasedOnPrice(long higherPrice, long lowerPrice);
}
Here is the code of the controller
package com.webshoppingbackend.springboot.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.webshoppingbackend.springboot.model.WebShoppingItem;
import com.webshoppingbackend.springboot.services.WebShoppingService;
#RestController
#RequestMapping("/webshopping")
public class WebShoppingController {
#Autowired
private WebShoppingService service;
#GetMapping
public List<WebShoppingItem> getAllItems() {
return service.getWebShoppingItems();
}
#GetMapping("/{itemId}")
public WebShoppingItem getItem(#PathVariable String itemId) {
return service.getItemById(itemId);
}
#GetMapping("/category/{category}")
public List<WebShoppingItem> getItemsByCategory(#PathVariable String category) {
return service.getItemsByCategory(category);
}
#GetMapping("/brand/{brand}")
public List<WebShoppingItem> getItemsByBrand(#PathVariable String brand) {
return service.getItemsByBrand(brand);
}
#GetMapping("/{price}")
public List<WebShoppingItem> getItemsBasedOnPriceRange(#PathVariable long higherPrice,
#PathVariable long lowerPrice) {
return service.getItemsBasedOnPriceRange(higherPrice, lowerPrice);
}
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public WebShoppingItem addItem(#RequestBody WebShoppingItem webShoppingItem) {
return service.addToWebShoppingItems(webShoppingItem);
}
#PutMapping
public WebShoppingItem modifyItem(#RequestBody WebShoppingItem webShoppingItem) {
return service.updateWebShoppingItem(webShoppingItem);
}
#DeleteMapping("/{itemId}")
public String deleteItem(#PathVariable String itemId) {
return service.deleteWebShoppingItem(itemId);
}
}
Here is the code in the service provider
package com.webshoppingbackend.springboot.services;
import java.util.List;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.webshoppingbackend.springboot.model.WebShoppingItem;
import com.webshoppingbackend.springboot.repository.WebShoppingRepository;
#Service
public class WebShoppingService {
#Autowired
private WebShoppingRepository repository;
// CRUD
public WebShoppingItem addToWebShoppingItems(WebShoppingItem webShoppingItem) {
webShoppingItem.setItemId(UUID.randomUUID().toString().split("-")[0]);
return repository.save(webShoppingItem);
}
public List<WebShoppingItem> getWebShoppingItems() {
return repository.findAll();
}
public WebShoppingItem getItemById(String itemId) {
return repository.findById(itemId).get();
}
public List<WebShoppingItem> getItemsByCategory(String category) {
return repository.getItemsByCategory(category);
}
public List<WebShoppingItem> getItemsByBrand(String brand) {
return repository.getItemsByBrand(brand);
}
public List<WebShoppingItem> getItemsBasedOnPriceRange(long higherPrice, long lowerPrice) {
return repository.getItemsBasedOnPrice(higherPrice, lowerPrice);
}
public WebShoppingItem updateWebShoppingItem(WebShoppingItem webShoppingItem) {
WebShoppingItem itemToBeUpdated = repository.findById(webShoppingItem.getItemId()).get();
itemToBeUpdated.setCategory(webShoppingItem.getCategory());
itemToBeUpdated.setProductName(webShoppingItem.getProductName());
itemToBeUpdated.setImageSource(webShoppingItem.getImageSource());
itemToBeUpdated.setBrand(webShoppingItem.getBrand());
itemToBeUpdated.setPrice(webShoppingItem.getPrice());
itemToBeUpdated.setRating(webShoppingItem.getRating());
itemToBeUpdated.setShippingCharges(webShoppingItem.getShippingCharges());
itemToBeUpdated.setProductDetails(webShoppingItem.getProductDetails());
return repository.save(itemToBeUpdated);
}
public String deleteWebShoppingItem(String itemId) {
repository.deleteById(itemId);
return "Deleted Web Shopping Item";
}
}
Here is the request I'm sending through Talend API Tester to test my code.

Why does java springboot print an empty json in the browser?

The Java Rest Api prints on http://localhost:8080/books
[{},{},{}]
instead of the booklist object. I use a main method a book_controller and a book model. Firstly I add a couple of books in the list in the method getbooks() and then I return them as a list.
Why does this happen?
application.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
#SpringBootApplication (exclude = SecurityAutoConfiguration.class)
public class Lab6NosApplication {
public static void main(String[] args) {
SpringApplication.run(Lab6NosApplication.class, args);
}
}
Book_controller.java
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import ch.qos.logback.classic.Logger;
import net.minidev.json.JSONArray;
#Controller
public class Book_controller implements ErrorController, Serializable {
#GetMapping("books")
public #ResponseBody List<book> getbooks() {
List<book> bookList = new ArrayList<book>();
bookList.add(new book(1,"lokesh","gupta"));
bookList.add(new book(2,"lokesh","gupta"));
bookList.add(new book(3,"lokesh","gupta"));
java.lang.System.out.print(bookList);
return bookList;
}
#RequestMapping("/error")
#ResponseBody
public String handleError(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
return String.format("<html><body><h2>Error Page</h2><div>Status code: <b>%s</b></div>"
+ "<div>Exception Message: <b>%s</b></div><body></html>",
statusCode, exception==null? "N/A": exception.getMessage());
}
#Override
public String getErrorPath() {
return "/error";
}
}
Book.java
public class Book {
public Book(Integer id, String title, String author) {
super();
this.id = id;
this.title = title;
this.author = author;
}
private Integer id;
private String title;
private String author;
//getters and setters
#Override
public String toString() {
return "Employee [id=" + id + ", title=" + title
+ ", author=" + author + "]";
}
}
Thank you!
You see it empty because your Book class doesn't have public getters or properties, so the serializer won't be able to access it's values.
Add getters to your Book class:
public Integer getId(){
return this.id;
}
public String getTitle(){
return this.title;
}
public String getAuthor(){
return this.author;
}
You can also improve your code, with better naming such as BookController instead of Book_controller. Have a look at java naming conventions.
Finally, take a look at a #RestController. If you use #RestController, you don't need to have #ResponseBody

How to parse request parameter (query and path param) and request body in same POJO in REST API

I have a rest API (PUT verb) which accepts both request body and path params:
Ex:
curl --data {a:1, b:2} -X PUT "https://example.com/users/{username}/address/{addressname}"
I am trying to fetch both request body and path param in one POJO
Response myAPI(#BeanParam Users user){
system.out.println(user.username);
system.out.println(user.a);
Users class
public class Users{
#PathParam(username)
private String userName;
......
private String a;
......
}
But I am getting value of user.a as null.
How to parse both request body and param in same class?
You can do this with a custom annotation and an InjectionResolver. What the InjectionResolver does is allow you to create a custom injection point with your own annotation. So you could do something like
public class Users {
#PathParam(username)
private String userName;
#Body
private String a;
}
When implementing the InjectionResolver, you would grab the actual body from the ContainerRequest using the ContainerRequest#readEntity(Class) method. You would determine the Class to pass by doing some reflection on the Field that you can obtain inside the InjectionResolver. Below is a complete example using Jersey Test Framework. Run it like any other JUnit test.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
/**
* Only one required dependency to run this test. Note this is using 2.25.1.
* If you are using 2.26 or later, the implementation will be different,
* and this will not work. You need to use the Jersey packaged `InjectionResolver`,
* not the HK2 one. And you will also need to bind that `InjectionResolver`
* to `GenericType` instead of `TypeLiteral` in the `AbstractBinder#configure()`.
*
* <dependency>
* <groupId>org.glassfish.jersey.test-framework.providers</groupId>
* <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
* <version>2.25.1</version>
* <scope>test</scope>
* </dependency>
*/
public class BeanParamTest extends JerseyTest {
#Path("test")
#Consumes("application/json")
#Produces("application/json")
public static class TestResource {
#POST
#Path("{username}")
public String post(#BeanParam ModelBean bean) {
return bean.toString();
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig(TestResource.class)
.register(new AbstractBinder() {
#Override
protected void configure() {
bind(BodyInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<Body>>() {})
.in(Singleton.class);
}
});
}
#Test
public void testIt() {
final Response res = target("test/peeskillet")
.request()
.post(Entity.json("{\"foo\":\"bar\"}"));
System.out.println(res.readEntity(String.class));
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD})
public #interface Body {}
public static class ModelBean {
#PathParam("username")
private String username;
#Body
private String body;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
#Override
public String toString() {
return "ModelBean{" +
"username='" + username + '\'' +
", body='" + body + '\'' +
'}';
}
}
public static class BodyInjectionResolver implements InjectionResolver<Body> {
#Inject
private javax.inject.Provider<ContainerRequest> requestProvider;
#Override
public Object resolve(Injectee injectee, ServiceHandle<?> serviceHandle) {
if (injectee.getParent().isAnnotationPresent(Body.class)) {
AnnotatedElement parent = injectee.getParent();
if (parent instanceof Field) {
Class<?> entityType = ((Field) parent).getType();
return requestProvider.get().readEntity(entityType);
}
}
return null;
}
#Override
public boolean isConstructorParameterIndicator() {
return false;
}
#Override
public boolean isMethodParameterIndicator() {
return false;
}
}
}

Jersey client unable to convert ArrayList to xml

I have developed one restful webservice using jersey and spring.
Below is the service method which servers the request by accepting the list of Login objects.
Method in service class
#POST
#Path(value = "/login")
#Produces(MediaType.APPLICATION_XML)
public String getLogin(Login login);
#POST
#Path(value = "/update")
#Consumes(MediaType.APPLICATION_XML)
public void updatePassword(List<Login> userList)
Login class
#XmlRootElement
public class Login {
//some code
}
I am using the jersey client to consume this webservice as below.
loginList = ArrayList of Login objects
Client client = Client.create();
WebResource webResource = client.resource("http://localhost:8080/user/update");
ClientResponse response = webResource.accept(MediaType.APPLICATION_XML).type(MediaType.APPLICATION_XML).post(ClientResponse.class, loginList);
But below is the exception which I am getting.
Caused by: com.sun.jersey.api.client.ClientHandlerException: A message body writer for Java type, class java.util.ArrayList, and MIME media type, application/xml, was not found
at com.sun.jersey.api.client.RequestWriter.writeRequestEntity(RequestWriter.java:288)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler._invoke(URLConnectionClientHandler.java:204)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:147)
But My /login i.e. getLogin method works perfectly.
I am not able to figure out what exactly is causing this issue.
Can anyone please help me what could be the wrong happening in this.
Thanks in advance.
I guess you need to use GenericEntity for generic types, because of type erasure. And JAXB needs to know the type. The GenericEntity stores the type through it parameter. So basically, you just need to do
.post(ClientResponse.class, new GenericEntity<List<Login>>(list){});
UPDATE
complete test using Jersey Test Framework
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-grizzly2</artifactId>
<version>1.19</version>
<scope>test</scope>
</dependency>
Login
#XmlRootElement
public class Login {
private String name;
private String password;
public Login() {
}
public Login(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Override
public String toString() {
return "Login{" + "name=" + name + ", password=" + password + '}';
}
}
Test
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.core.DefaultResourceConfig;
import com.sun.jersey.spi.container.servlet.WebComponent;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.WebAppDescriptor;
import com.sun.jersey.test.framework.spi.container.TestContainerFactory;
import com.sun.jersey.test.framework.spi.container.grizzly2.web.GrizzlyWebTestContainerFactory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import org.junit.Test;
public class ArrayTest extends JerseyTest {
#Path("/login")
public static class LoginResource {
#POST
#Consumes(MediaType.APPLICATION_XML)
public String post(List<Login> logins) {
return new HashSet<>(logins).toString();
}
}
public static class AppConfig extends DefaultResourceConfig {
public AppConfig() {
super(LoginResource.class);
}
}
#Override
public TestContainerFactory getTestContainerFactory() {
return new GrizzlyWebTestContainerFactory();
}
#Override
public WebAppDescriptor configure() {
return new WebAppDescriptor.Builder()
.initParam(WebComponent.RESOURCE_CONFIG_CLASS,
AppConfig.class.getName())
.build();
}
#Test
public void doTest() {
List<Login> list = new ArrayList<>();
list.add(new Login("pee", "skillet"));
ClientResponse response = resource().path("login")
.type(MediaType.APPLICATION_XML)
.post(ClientResponse.class, new GenericEntity<List<Login>>(list){});
System.out.println("Status " + response.getStatus());
System.out.println(response.getEntity(String.class));
}
}
If you remove the GenericEntity, you will get the error.

Jersey REST-API returns a HTTP Error 500 (but only client-side)

I am currently working on a REST-API. I am using Jersey for that.
I programmed a couple of interfaces before, and they all worked fine. Now, I'm doing it exactly the same way (in my opinion), but it somehow won't work.
The Problem is that userGet() does not log any error (the error does not occur inside the TRY-CATCH), and it SEEMS like the method is executed successfully. But the client receives only "500 Internal Server Error"-responses.
Do you have any idea how to find out where the error occures? Or do you already know what I've done wrong?
This is my class which should be returned by the API:
package myproject.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class UserModel {
#XmlElement(name = "id")
private long id = -1;
#XmlElement(name = "strUsername")
private String username = null;
#XmlTransient
private String encryptedPassword = null;
#XmlElement(name = "Consultant", nillable = true)
private Consultant consultant = null;
public UserModel(long id, String username, String encPw, Consultant co) {
this.id = id;
this.username = username;
this.encryptedPassword = encPw;
this.consultant = co;
}
public static UserModel fromUser(User u) {
return new UserModel(u.getId(), u.getName(), u.getEncryptedPassword(), u.getConsultant());
}
public User toUser() {
return User.fromUserModel(this);
}
public long getId() {
return id;
}
public String getUsername() {
return username;
}
public String getEncryptedPassword() {
return encryptedPassword;
}
public Consultant getConsultant() {
return consultant;
}
public String toString() {
return "id=" + id + ";username=" + username + ";encPw=" + encryptedPassword;
}
}
My REST-API:
package myproject.rest;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.log4j.Logger;
import org.json.JSONObject;
import myproject.User;
import myproject.UserModel;
import myproject.DBUtil;
import myproject.SecurityUtil;
#Path("/app/user")
public class HandlerUser extends RestEndpoint {
private static Logger log = Logger.getLogger(HandlerUser.class);
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response userGet() {
Connection con = null;
try {
con = DBUtil.getDBConnection();
//SecurityUtil.checkRight(request);
log.info("Collect all Users");
List<UserModel> usermodels = new ArrayList<UserModel>();
Iterator<User> it = User.getAll(con).iterator();
while(it.hasNext()) {
usermodels.add(it.next().toUserModel());
}
GenericEntity<List<UserModel>> entity = new GenericEntity<List<UserModel>>(usermodels) {};
return Response.ok().entity(entity).build();
}
catch(Exception e) {
log.error("An error occured: " + e.toString());
return Response.status(401).entity(e.toString()).build();
}
finally {
DBUtil.close(null, null, con);
}
}
}
EDIT: User.toUserModel() converts a User-Object to a UserModel-Object (which works fine)

Categories