I am currently working on some web dev project in Java, i have implemented a frontcontroller, which job is to instantiate new controllers, depending on the path.
So when the user is running ?q=user/login ex. the front controller should instatiate the UserController, that i am trying to do with this piece of code.
String q = request.getParameter("q");
try {
String[] page = q.split("/");
// Make first char upper, to match class name conventions.
page[0] = (page[0].substring(0, 1).toUpperCase() + page[0].substring(1).toLowerCase()).trim();
Class contDes = Class.forName("dk.elvar.rocks." + page[0]+ "Controller");
Constructor co = contDes.getConstructor();
co.newInstance(request, response, page);
This results in a
java.lang.NoSuchMethodException: dk.elvar.rocks.UserController.<init>()
at java.lang.Class.getConstructor0(Class.java:2706)
at java.lang.Class.getConstructor(Class.java:1657)
at dk.elvar.rocks.FrontController.doGet(FrontController.java:35)
I've tryed to look it up at google, and bugs as, declaring a constructor in loaded object, make the class public, is already there.
UserController:
public class UserController extends HttpServlet {
private final String USERNAME = "Martin";
private final String PASSWORD = "David";
private static final long serialVersionUID = 1L;
HttpServletRequest request;
HttpServletResponse response;
public UserController(HttpServletRequest request, HttpServletResponse response, String[] action) {
this.request = request;
this.response = response;
if(action[1].equalsIgnoreCase("login")) {
this.renderLoginAction();
}
if(action[1].equalsIgnoreCase("val-login")) {
this.validateLoginAction();
}
}
You probably get this exception because that class does not have a default constructor. You can get a constructor with parameters by passing them to the getConstructor method:
Constructor co = contDes.getConstructor(
HttpServletRequest.class,
HttpServletResponse.class,
String[].class);
co.newInstance(request, response, page);
Generally its not recommended to use other then default constructor, as web.xml or struts-config.xml uses to instantiate the servlet using reflection.
If you want to get instance of your class other than default constructor,
Class userController = Class.forName("<packages.>UserController");
Constructor userControllerConstructor = userController.class.getConstructor(HttpServletRequest.class, HttpServletResponse.class, String[].class);
UserController userControllerObj = (UserController)constructor.newInstance(request,response, action);
Related
I'm trying to write unit test case for HttpHandler class which has rest template call for delete. I've crated a usercontroller class to make resttemplate call in order to test the functionality of sendDelete in HttpHandler class. Can someone help me too understand what is the correct way to write unit test case for sendDelete method in HtttpHandler class?
I have a class HttpHandler. It has a function sendDelete where it calls resttemplate.exchange method
#Service
public class HttpHandler {
public <T,R> ResponseEntity<Void> sendDelete(String url, HttpHeaders httpHeaders, R requestBody, Class<T> responseClass) {
//create an instance of rest template
RestTemplate restTemplate = new RestTemplate();
HttpEntity<R> entity = new HttpEntity<R>(requestBody, httpHeaders);
logger.info("DELETE request to " + url + " with body: " + JsonUtil.jsonizeExcludeNulls(requestBody));
//make an HTTP DELETE request with headers
ResponseEntity<Void> response = restTemplate.exchange(url, HttpMethod.DELETE, entity, Void.class);
logger.info("DELETE" + url + ": " + JsonUtil.jsonize(response));
return response;
}
}
I'm using junit5. Below is the unit test case for sendDelete method in above class:
#LocalServerPort
private int port;
private String baseUrl;
#Autowired
private HttpHandler httpHandler;
#BeforeEach
public void setBaseUrl(){
this.baseUrl = "http://localhost:"+ port + "/users";
}
#Test
public void testSuccessDeleteUserById() throws Exception{
this.baseUrl = baseUrl + "/1";
//create headers
HttpHeaders httpHeaders = new HttpHeaders();
//set content type
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
//make an HTTP DELETE request with headers
ResponseEntity<Void> actual = httpHandler.sendDelete(baseUrl, httpHeaders, null, Void.class);
assertEquals(404, actual.getStatusCodeValue());
}
Below is the user controller class
#RestController
public class UserController {
#DeleteMapping("/users/{userId}")
public ResponseEntity<Void> deleteUser(#PathVariable("userId") int userId){
return new ResponseEntity<Void>(HttpStatus.NOT_FOUND);
}
}
Thank you for your time!
There are two ways to do it.
Mocking RestTemplate. To do it, first, you have to make the RestTemplate a field, and inject it through the constructor (or any other way). This allows you to inject a mock object. Then, the rest is plain and simple mocking.
You can use MockWebServer. This way you do not need to change anything. It is just a web server that your method will send the request to. After the method call finishes, you can access the recorded request and make some validations.
Here's a crude example. If you will have a lot of those tests, then you can move the web server initialization to a #BeforeEach method and the destroying to #AfterEach method.
public class HttpHandlerTest {
private final HttpHandler handler = new HttpHandler();
#Test
#SneakyThrows
public void testDelete() {
MockWebServer mockWebServer = new MockWebServer();
mockWebServer.start(9889);
mockWebServer.enqueue(
new MockResponse().setResponseCode(200)
);
String url = "http://localhost:9889";
Hello hello = new Hello("hello world");
final ResponseEntity<Void> entity = handler.sendDelete(url, null, hello, Hello.class);
assertNotNull(entity);
assertEquals(200, entity.getStatusCode().value());
final RecordedRequest recordedRequest = mockWebServer.takeRequest();
assertEquals("DELETE", recordedRequest.getMethod());
mockWebServer.close();
}
}
// just an example class to use as a payload
class Hello {
String text;
public Hello() {
}
public Hello(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Note. Even though you will not opt for the first solution, I recommend you do abandon initializing RestTemplate for each request. You better use WebClient instead. If you do so, the first solution will not work anymore, while the second will remain intact.
The below code is working fine. I need to reuse this methods for all requests. How to make it as generics?
public class ApiResponse {
}
public class QuoteRespWrapper extends ApiResponse{
private String responseType;
private QuoteRespValue responseValue;
}
public class PolicyRespWrapper extends ApiResponse{
private String responseType;
private PolicyRespValue responseValue;
}
public QuoteRespWrapper callService(String endPoint, String payload, Class<? extends ApiResponse> respClass) throws Exception {
private static List<JacksonJsonProvider> providerList = singletonList(JacksonConfig.jacksonJsonProvider());
String userName="user1";
String password="password1";
WebClient client = WebClient.create(endPoint, providerList, userName, password, null);
Response webClientresponse = client.post(payload);
QuoteRespWrapper strResponse = webClientresponse.readEntity(QuoteRespWrapper.class);
return strResponse;
}
I need to modify the below line based on class type. It can be any subclass of ApiResponse (QuoteRespWrapper,PolicyRespWrapper,....). I need to pass
argument .class dynamically to get the response.
QuoteRespWrapper strResponse = webClientresponse.readEntity(QuoteRespWrapper.class);
You can use toString to produce the class name output as expected (e.g. class java.lang.String):
webClientresponse.readEntity(respClass.toString());
public ApiResponse callService(String endPoint, String payload, Class<? extends ApiResponse> respClass) throws Exception {
private static List<JacksonJsonProvider> providerList = singletonList(JacksonConfig.jacksonJsonProvider());
String userName="user1";
String password="password1";
WebClient client = WebClient.create(endPoint, providerList, userName, password, null);
Response webClientresponse = client.post(payload);
ApiResponse strResponse = (ApiResponse) webClientresponse.readEntity(respClass);
return strResponse;
}
The above code fulfilled my need.
I have a custom dispatcher servlet which extends the default DispatcherServlet to do some validation for all requests.
Since I will get all the parameters from a request(getInputStream()->Map) to do some validation, I want to pass the params to controller or add the params to the context where I can get them again from the cotroller.
Now I just put all the params to a global Map, but I wonder if there are some simple ways.
public class CustomDispatcherServlet extends DispatcherServlet {
private static final long serialVersionUID = 7250693017796274410L;
#Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
doFilter(request, response);
super.doDispatch(request, response);
}
...
private void doFilter(HttpServletRequest request, HttpServletResponse response) {
WLNResponse<String> error = null;
try {
boolean isSignValid = checkSignValidity(request);
...
private boolean checkSignValidity(HttpServletRequest request) throws IOException {
// pass this params to controller or somewhere I can get from controller
Map<String, Object> params = WebUtils.readParams(request);
...
The way I would go at validating params in the controller itself. for instance
#Controller
public ControllerClass
{
#RequestMapping(value = "/someurl", method = RequestMethod.GET, params = {
"requestParam"})
public void someMethod(#RequestParam(value = "requestParam") String requestParam)
{
System.out.println("This is the value of the RequestParam requestParam " + requestParam);
}
}
This way you can do your validation within the controller.
The only thing this doesn't solve for is if the request being made is not resolved to a valid controller. For that I would use the annotation #controllerAdvice.
Currently, I simply use the request.setAttribute() to put params to the attributes and get it from the controller.....
I am designing a REST API using JAX-RS. The endpoint looks like the following:
#GET
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response get(
#QueryParam("param1") final String param1,
#QueryParam("param2") final String param2) {
// Code goes here
}
I have nearly 7-8 parameters. So I would like to do something like the following:
#GET
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response get(#Context MyContextObject context) {
// Code goes here
}
And I have the context object as follows:
final class MyContextObject {
// get methods
public MyContextObject(final Builder builder) {
// set final fields
}
private final String param1;
private final String param2;
public static final class Builder {
// builder code goes here
}
}
Can you please advise how I can do that?
Thanks in advance.
If you want to do by creating separate bean class as you said, you would need to get the query parameters in bean class like below.
final class MyContextObject {
// get methods
public MyContextObject(final Builder builder) {
// set final fields
}
private #QueryParam("param1") final String param1;
private #QueryParam("param2") final String param2;
//and so on...
public static final class Builder {
// builder code goes here
}
}
If you do so, the query parameters get bounded to these private variables in the bean class and you would get them in your rest service using getters.
I filter the BasicAuth of my REST-services and store the username in the HttpServletRequest
Related Code of the AuthFilter
public class AuthFilter implements ContainerRequestFilter {
#Context
public transient HttpServletRequest servletRequest;
#Override
public ContainerRequest filter(ContainerRequest containerRequest) throws WebApplicationException {
// all the auth filter actions...
// Set data
servletRequest.setAttribute("auth_username", username);
return containerRequest;
}
}
I then have a resource parent class (called BaseResource with only some general attributes) and specific classes which extends it
example specific class
#Path("/Plant")
public class PlantResource extends BaseResource {
private List<Plant> plantlist = new LinkedList<Plant>();
#GET
#Path("/GetPlantById/plantid/{plantid}")
#Produces("application/json")
public String getPlantById(#PathParam("plantid") String plantid, #Context HttpServletRequest hsr) {
String username = (String)hsr.getAttribute("auth_username");
// do something
}
}
As you can see I handle the HttpServletRequest via "#Context HttpServletRequest hsr" to the function (as described in there: Get HttpServletRequest in Jax Rs / Appfuse application?) . This works fine and I can access the data correctly!
What I want now to do is to access this Data in the constructor of the parent class, so I don't have to do it in every function of my specified resources, but in a single place
My try:
public class BaseResource {
#Context protected HttpServletRequest hsr; // Also tried private and public:
/* ... */
public BaseResource() {
String username = (String)hsr.getAttribute("auth_username"); // line 96
System.out.println("constructur of BaseResource" + username);
}
}
But this ends up in:
Aug 05, 2013 3:40:18 PM com.sun.jersey.spi.container.ContainerResponse mapMappableContainerException
Schwerwiegend: The RuntimeException could not be mapped to a response, re-throwing to the HTTP container
java.lang.NullPointerException
at de.unibonn.sdb.mobilehelper.resources.BaseResource.<init>(BaseResource.java:96)
It looks like the HttpServletRequest isn't set there. So how can I access it in the constructor of my parent class?
Fields of BaseResource are injected after an instance is created, so you can't refer to them in the constructor itself. Either create a property method in your BaseResource:
public class BaseResource {
#Context
protected HttpServletRequest hsr;
/* ... */
protected String getUsername() {
return (String)hsr.getAttribute("auth_username");
}
}
or create a hierarchy like:
public class BaseResource {
protected HttpServletRequest hsr;
/* ... */
public BaseResource(HttpServletRequest hsr) {
this.hsr = hsr;
String username = (String)hsr.getAttribute("auth_username");
System.out.println("constructur of BaseResource" + username);
}
}
and
#Path("/Plant")
public class PlantResource extends BaseResource {
private List<Plant> plantlist = new LinkedList<Plant>();
public PlantResource(#Context HttpServletRequest hsr) {
super(hsr);
}
#GET
#Path("/GetPlantById/plantid/{plantid}")
#Produces("application/json")
public String getPlantById(#PathParam("plantid") String plantid) {
String username = (String)hsr.getAttribute("auth_username");
// do something
}
}
You will have to pass it up via a function. The JAX-RS annotations like #Context aren't available in the parent, as you noted. They are also not inherited down. Additionally, you cannot do it at construction time since #Context references are not guaranteed to be available during construction (depends on how container creates the resources).