Spring default consumes and produces - json

I'm writing a servlet that will use a bunch of RestControllers to provide functionality.
All of that will use JSON almost exclusively, so I would like a compact way to say: Unless specified otherwise, consume and produce MediaType.APPLICATION_JSON_VALUE for everything.
I thought I found a nice solution on another SO question.
However, as already pointed out in a comment there, this solution causes trouble.
#RestController
#RequestMapping(value = "/relationship/type", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE, method = {
RequestMethod.GET
})
public class DRelationshipTypeResource {
// #GetMapping("/all")
#RequestMapping(value = "/all", method = RequestMethod.GET)
public List<DRelationshipTypeDTO> getAll() {
return DRelationshipTypeService.getAll();
}
This controller also will feature POST/PUT/DELETE plus some more GETs. I removed them for now to minimize possible causes of errors.
Calling this route produces a 415 error.
Even worse, I would really like to be able to use
#GetMapping("/all")
instead of the more verbose #RequestMapping Overload for the getAll()-Method, but that also produces the same 415 error.
Server debug console spits out this when the request arrives:
2019-01-29 10:20:54.627 WARN 10712 --- [io-9999-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type '' not supported]
2019-01-29 10:20:54.628 ERROR 10712 --- [io-9999-exec-10] o.a.c.c.C.[Tomcat].[localhost] : Exception Processing ErrorPage[errorCode=0, location=/error]
java.lang.NoSuchMethodError: javax.servlet.http.HttpServletRequest.getHttpServletMapping()Ljavax/servlet/http/HttpServletMapping;
at org.apache.catalina.core.ApplicationHttpRequest.setRequest(ApplicationHttpRequest.java:690) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.catalina.core.ApplicationHttpRequest.<init>(ApplicationHttpRequest.java:114) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.catalina.core.ApplicationDispatcher.wrapRequest(ApplicationDispatcher.java:917) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:358) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:394) [tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:253) [tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:175) [tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) [tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417) [tomcat-embed-core-9.0.14.jar:9.0.14]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.14.jar:9.0.14]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_181]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_181]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.14.jar:9.0.14]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_181]
and returns a HTTP Status 415 – Unsupported Media Type to the client making the request.
To clarify further, if I use a "dumb" class such as this, everything works fine, with the content correctly being returned as JSON.
#RestController
#RequestMapping("relationship/type")
public class DRelationshipTypeResource {
#GetMapping("/all")
public List<DRelationshipTypeDTO> getAll() {
return DRelationshipTypeService.getAll();
}

As the stack trace, clearly telling content-type is empty (' ').
I think Content-Type is not passed while making the GET call. If you pass Content-Type as 'application/json' it should work.
You have defined consumes and produces at the class level, which means by default all the REST services should pass headers, Content-Type and Accept in order to consume the service.

It's missing to add / at beginning on your path and add method type GET:
#RequestMapping(value = "/relationship/type",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE,
method = {RequestMethod.GET}))

To accept all request type just overwrite consumes value.
#RequestMapping(value = "/all", consumes="*/*", method = RequestMethod.GET)
public List<DRelationshipTypeDTO> getAll() {
return DRelationshipTypeService.getAll();
}

The issue was with my requests not explicitly having a Content-Type application/json header, as pointed out by https://stackoverflow.com/a/54418436/2436002 .
To clear up some of the apparent misinformation about all this, everything worked just as I expected now, with very readable, clean and spring-like code. Maybe it can help others looking for an example.
#RestController
#RequestMapping(value = "relationship/type", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public class DRelationshipTypeResource {
#GetMapping("/all")
public List<DRelationshipTypeDTO> getAll() {
return DRelationshipTypeService.getAll();
}
#GetMapping("/{query}")
public DRelationshipTypeDTO get(#PathVariable("query") String query) {
return DRelationshipTypeService.get(query);
}
#PostMapping
public ResponseEntity<Void> create(DRelationshipTypeDTO dto) {
String label = DRelationshipTypeService.create(dto);
URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{label}").buildAndExpand(label).toUri();
return ResponseEntity.created(uri).build();
}
#PutMapping("{label}")
public ResponseEntity<Void> update(#PathVariable("label") String label, DRelationshipTypeDTO dto) {
DRelationshipTypeService.update(label, dto);
return ResponseEntity.noContent().build();
}
#DeleteMapping("{label}")
public ResponseEntity<Void> delete(#PathVariable("label") String label) {
DRelationshipTypeService.delete(label);
return ResponseEntity.noContent().build();
}
Not 100% yet on the best method for URI-Building during the POST /Create, but that's a different issue, and it at least works fine (proper location header for HTTP201 response).

Related

Spring Boot JSONP with MappingJacksonValue response and strict MIME type error

I've been reading a lot about JSONP support with Spring 4, but I still lack a clean explanation to make it work with the right media-type (under chrome)
1) I added the JsonpAdvice cfr Jackson JSONP Support
#ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
2) My controller is wrapping the response with MappingJacksonValue*
#RequestMapping(value = '/api/test', method = RequestMethod.GET)
#ResponseBody
public Object test(HttpServletRequest request) {
List<String> result = new ArrayList<String>();
result.add("hello");
result.add("world");
if(request.getParameter('callback')){
MappingJacksonValue value = new MappingJacksonValue(result)
value.setJsonpFunction(request.getParameter('callback'))
return value
}
return result
}
not sure the MappingJacksonValue is necessary or if MappingJackson2HttpMessageConverter will take care of that?
3) I added explicit media-types in application.yml:
spring:
profiles.active: development
jackson.property-naming-strategy: SNAKE_CASE
mvc:
media-types:
json: 'application/json'
xml: 'application/xml'
jsonp: 'application/javascript'
However I still get the following error in Chrome:
Refused to execute script from 'https://example.com/api/test?callback=jQuery22406993800323428312_1481922214995&_=1481922214996'
because its MIME type ('application/json') is not executable, and strict MIME type checking is enabled.
Any step missing? or too much configuration?
After debugging my JsonpAdvice.groovy, I found out that AbstractJsonpResponseBodyAdvice is expecting a list of String: private final String[] jsonpQueryParamNames;
My initial code was using a simple String. Here is the fix:
#ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super(["callback"])
}
}

Consuming JSON with Jersey 2.3 throws XML exception

I'm trying to make Jersey 2.3 + Moxy work with my custom objects. Everything is ok when I'm producing JSON from those objects, but refuses to work when I want to consume them by POST. Code first:
Custom object:
#XmlRootElement
public class ContentAction extends Action {
private String contentType;
private Integer contentLength;
public ContentAction() {
setType(ActionType.CONTENT);
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public Integer getContentLength() {
return contentLength;
}
public void setContentLength(Integer contentLength) {
this.contentLength = contentLength;
}
}
Resource fragment (path is declared on the class):
#POST
#Produces(JsonHelper.JSON_UTF8)
public Action saveAction(#QueryParam("action") ContentAction action) throws IOException {
ActionEntity entity = actionConverter.toEntity(action);
entity.setBeacon(beaconService.findById(action.getBeacon().getId()));
return actionConverter.convert(actionService.save(entity));
}
API caller (class responsible for sending requests):
URI uri = buildUri(path, params);
HttpPost httpPost = new HttpPost(uri);
httpClient.execute(httpPost);
buildUri simply creates URI from address & parameters
The action is converted to JSON as follows: mapper.writeValueAsString(action)
And the exception:
org.glassfish.jersey.server.internal.inject.ExtractorException: Error unmarshalling JAXB object of type "class com.kontakt.platform.apicommon.model.ContentAction".
at org.glassfish.jersey.server.internal.inject.JaxbStringReaderProvider$RootElementProvider$1.fromString(JaxbStringReaderProvider.java:195)
at org.glassfish.jersey.server.internal.inject.AbstractParamValueExtractor.convert(AbstractParamValueExtractor.java:138)
at org.glassfish.jersey.server.internal.inject.AbstractParamValueExtractor.fromString(AbstractParamValueExtractor.java:129)
at org.glassfish.jersey.server.internal.inject.SingleValueExtractor.extract(SingleValueExtractor.java:83)
at org.glassfish.jersey.server.internal.inject.QueryParamValueFactoryProvider$QueryParamValueFactory.provide(QueryParamValueFactoryProvider.java:88)
at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:81)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker.getParamValues(JavaResourceMethodDispatcherProvider.java:121)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:195)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:104)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:353)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:343)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:255)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:318)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:235)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:983)
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:359)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:372)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:335)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:218)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1008)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:1852)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
Caused by: javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.]
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.createUnmarshalException(AbstractUnmarshallerImpl.java:335)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.createUnmarshalException(UnmarshallerImpl.java:512)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:209)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:175)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:140)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:123)
at org.glassfish.jersey.server.internal.inject.JaxbStringReaderProvider$RootElementProvider$1.fromString(JaxbStringReaderProvider.java:190)
... 40 more
Caused by: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:198)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:177)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:441)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:368)
at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1388)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:998)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:607)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:116)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:489)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:835)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:123)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1210)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:568)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:203)
... 44 more
When I remove #XmlRootElement, Jersey tries to create object from constructor with string parameter (if added) = no automatic bean creation.
I've been stuck with this for two days and I'd be very grateful for any help.
I think the problem is that you need to set the content type. Since you are not specifying the content type, jersey is assuming it is reciving xml but it is in fact json. The error message is from jaxb trying to read the xml header and finding invalid characters. Try using the jersey client instead of an http post. Also I think you might what to change your #Produces(MediaType.APPLICATION_JSON) for consistency.
#Produces(MediaType.APPLICATION_JSON)
Example POST CODE
The most important part is that I am setting the type (type(MediaType.Application_JSON) that I am going to post.
WebResource webResource = createRestClient(true).resource(
REST_BASE_PATH + "/service");
ClientResponse response = webResource.type(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.post(ClientResponse.class, contentActionObject);
Assert.assertTrue(response.getStatus() == 200);
If you do need to use the HttpPost object then there should be a configuration option to set http headers. I have done this using the the HttpUrlConnection. You would have to set your content type to be application/json instead of xml.
HttpURLConnection connection = null;
connection = (HttpURLConnection) new URL(requestUrl).openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/xml");
connection.setRequestProperty("Content-Type", "application/xml");
EclipseLink MOXy will be picked up as the JSON-binding provder by Jersey for media types that follow the following pattern.
*/json (i.e. application/json and text/json)
/+json
Based on your exception it appears as though the media type have have represented with JsonHelper.JSON_UTF8 does not match this pattern.
I know this is a little old, but I have been encountering the essentially the identical issue using Jersey 2.3 + Moxy. Same scenario and the identical exception is occurring. The only difference is that my POST method is annotated with:
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
I did find that when if I passed the same JSON representation in the request body instead of as a query parameter that it was unmarshalled successfully. This would require changing your POST method declaration to:
public Action saveAction(ContentAction action)
Not sure if this would be a work around for you. If you found a resolution I would be interested in hearing what it was.

how to mock a return value from another class within a method java (mockito)

I am new to mockito and i want to make a unit test for user validation. Please find below the method i want to perform the unit test:
#RequestMapping(method = RequestMethod.POST, value = "/login")
public ModelAndView validateViewLogin(#ModelAttribute Person person,
BindingResult result, HttpServletRequest request) {
ModelAndView mav = new ModelAndView();
String userName = person.getFirstName();
String password = person.getPassword();
boolean isUserValid = false;
if (userName != null && password != null) {
isUserValid = userManagerService.validateUserLogin(userName,
password);
}
if (!isUserValid) {
mav.setViewName("home");
return mav;
}
mav.addObject("isUserValid", isUserValid);
mav.setViewName("login");
return mav;
}
As you can see above isUserValid method returns a boolean and my method i want to test returns a ModelAndView.
Please see my unit test below:
`#Test public void testValidateOk() {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
Person person = new Person();
ModelAndView mav = new ModelAndView();
mav.setViewName("login");
person.setFirstName("John");
person.setPassword("123");
LogInController controller = new LogInController();
UserManagerServiceImpl mockpdao = mock(UserManagerServiceImpl.class);
ReflectionTestUtils.setField(controller, "userManagerService", mockpdao);
// given
given(controller.validateViewLogin(person, result, request)).willReturn(mav);
// when
ModelAndView validateViewLogin=
controller.validateViewLogin(person, result, request);
// then
assertEquals("home", validateViewLogin.getViewName());
}`
when i run my unit test i get the following error:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
ModelAndView cannot be returned by validateUserLogin()
validateUserLogin() should return boolean
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. This exception might occur in wrongly written multi-threaded tests.
Please refer to Mockito FAQ on limitations of concurrency testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
at com.gemstone.presentation.LogInControllerTest.testValidateOk(LogInControllerTest.java:49)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Any ideas how i can resolve this issue please?
I'm not familiar with BDD style Mockito, but I'm guessing that the line
given(controller.validateViewLogin(person, result, request)).willReturn(mav);
means that you are asking the controller to return the given model and view whenever the validateViewLogin method is called with the specified person, result and request. However the controller is not a mock, so this may be what is causing your error. What you should be doing instead is specifying the behaviour of how your mock user manager service should behave.
I notice that you are creating a mock of the UserManagerServiceImpl class. Given that it ends with 'Impl' I am guessing that there is a correspondng UserManagerService interface that you could mock instead. Mocktio can mock concrete classes, but it can not do this as easily as mocking an interface. Therefore if there is indeed an interface then I would mock that instead just to be safe.
You are injecting your mock using ReflectionTestUtils. This probably isn't the cause of your error, but if it is possible for you to do so then I'd recommend adding a public setter to your controller to inject it more safely and easily.
Taking the above points, I would write your test like the following:
#Test public void validateViewLogin_validLogin_returnsHomePage() {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
Person person = new Person();
person.setFirstName("John");
person.setPassword("123");
LogInController controller = new LogInController();
UserManagerService mockUserService = mock(UserManagerService.class);
// Configure mock user service to accept the person
when(mockUserService.validateUserLogin("John", "123")).thenReturn(true);
// Inject mock user service into controller
controller.setUserManagerService(mockUserService);
// Attempt the validation
ModelAndView mav =
controller.validateViewLogin(person, result, request);
// Check the result
assertEquals("home", mav.getViewName());
}
Since I'm not familiar with the BDD syntax I have configured the mock using the line
when(mockUserService.validateUserLogin("John", "123")).thenReturn(true);
but I assume that this is equivalent to
given(mockUserService.validateUserLogin("John", "123")).willReturn(true);

NO JSON Result from Spring3 MVC DispatcherServlet due to AOP Configuration

When we configure Spring AOP the JSON Results disappear for : AOPExression1
<aop:pointcut id="dmhMethodExecution"
expression="within(com.aditya.dmh..*)" />
So I added an exclusion for : AOPExpression1 as AOpExpression2
<aop:pointcut id="dmhMethodExecution"
expression="within(com.aditya.dmh..*)
and !within(com.aditya.dmh.controller..*)" />
in the ASPECTJ Expression
Still I donot see my JSON results from the controller which is a restful implementation.
package com.aditya.dmh.controller;
#Controller
public class EmployeeController {
private EmployeeServiceInterface employeeService;
#Autowired
public void setEmployeeService(EmployeeServiceInterface employeeService) {
this.employeeService = employeeService;
}
#RequestMapping("/employeeservices/1/allemployees.view")
public #ResponseBody Result<EmployeeModel> getEmployees(){
return employeeService.getEmployees(0, 10);
}
}
When I use log4j for the DEBUG messages I see the following:
15:37:04.214 [http-8090-1] DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'dmhServiceDispatcher': assuming HandlerAdapter completed request handling
15:37:04.214 [http-8090-1] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request
When I remove the AOP the JSON results start to appear and I see that the additional Debug Message.
17:11:36.270 [http-8090-2] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Written [com.aditya.Result#8a85268] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#62ba2e48]
Looking at the Spring forums I understand that the Convertor is automatically configured when the
<mvc:annotation-driven/>
is used.
Is my problem of configuring AOP have anything to do with the RequestResponseBodymethodProcessor not being called.
Does this have anything to do with the proxies created around my controller when I use AOPExpression1. Why would an exclusion as in AOPExpression2 still have the problem.
Anyhelp would be appreciated
I belive that to intercept a request to a controller you should do it with MVC interceptors and not with aspects. What I did is to put into the applicationContext.xml this:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/employeeservices/1/allemployees.view"/>
<bean class="com.aditya.dmh.interceptor.ResultInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
Now, the class ResultInterceptor is where you put the code you want to be done, for instance:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("--- preHandle --- ");
return true;
}
At least this is the way I did it.
Hope it helps.
This is a bit of a speculation:
I think what is happening is a CGLIB based dynamic proxy is getting created for your controller (although you have excluded it explicitly in your new pointcut expression), if this happens then #RequestMapping annotations are not correctly detected(by `) and so the controller is not there to handle your REST request.
Can you try a few things:
Have an interface for the controller with the exact same methods that the controller handles, and put the #RequestMapping annotations there, this will handle cases where the dynamic proxy is created and should work as expected even if the dynamic proxy gets created..
Play around a little more with your pointcut expression to see why a proxy for you controller may be getting created.
THE SOLUTION FOR OUR PROBLEM IN THIS CONTEXT
We found out that the whole thing was with the Around Advice in AOP Configuration that we have had.
Before Fix
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
joinPoint.proceed();
long totalTime = System.currentTimeMillis() - startTime;
log.debug(buildLogMessage(new StringBuilder().append(METHOD_AROUND_ID)
.append("[").append(totalTime).append("] ").toString(),
joinPoint));
return returnValue;
}
After Fix
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object returnValue = joinPoint.proceed();
long totalTime = System.currentTimeMillis() - startTime;
log.debug(buildLogMessage(new StringBuilder().append(METHOD_AROUND_ID)
.append("[").append(totalTime).append("] ").toString(),
joinPoint));
return returnValue;
}
the void effectively made sure that the Response Object sent by the logAround was not passed on back to the RequestResponseBodyMethodProcessor
Once we had it captured & returned the cglib proxies sent the response back to the processor & had the response sent back to the client.

Unexpected error using newRequest in FunctionalTests

I don't understand why I have this error.
When I use newRequest, I have a RuntimeException when calling the
makeRequest(request); method.
The exception message is : "play.mvc.results.NotFound : POST /"
But what is odd, is that in the .url, I specify "/dashboard", not
"/" (and of course, the url is well indicated in the routes file for POST requests!)
Thanks for your help.
Here is my test class :
public class DashboardTest extends FunctionalTest {
protected Request ajaxRequest;
#Before
public void _setUp() {
Fixtures.deleteDatabase();
Fixtures.loadModels("fixtures/accounts.yml");
ajaxRequest = newRequest();
//ajaxRequest.headers.put("X-Requested-With", new Header("X-
Requested-With", "XMLHttpRequest"));
ajaxRequest.method = "POST";
ajaxRequest.url = "/dashboard";
}
#Test
public void testAuthenticateWithValidDataAjax() {
ajaxRequest.params.put("email", "john.sm...#gmail.com");
Response response = makeRequest(ajaxRequest);
assertIsOk(response);
assertContentType("application/json", response);
}
}
Looking at the API documentation, the .url specifies that it needs the Full URL. What I would suggest you do instead, is to use the .action instead.
The Javadoc for the this is
Full action (ex: Application.index)
or specify the full URL, which would include
http://localhost:9000/dashboard
Your final option, if you are still having problems, is to use the createRequest method on the Http.Request object, which gives you complete control over the Request object you are creating. The signature looks like this
createRequest
public static Http.Request createRequest(java.lang.String _remoteAddress,
java.lang.String _method,
java.lang.String _path,
java.lang.String _querystring,
java.lang.String _contentType,
java.io.InputStream _body,
java.lang.String _url,
java.lang.String _host,
boolean _isLoopback,
int _port,
java.lang.String _domain,
boolean _secure,
java.util.Map<java.lang.String,Http.Header> _headers,
java.util.Map<java.lang.String,Http.Cookie> _cookies)