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.
Related
I have a Spring Boot application (2.4.1), where an OffsetDateTime field is returned as float value from RestController. Example:
"created_at": 1616080724.531610100
I tried all the suggested solutions in this thread. None of them worked for me.
I also tried to add a very simple end-point that only returns OffsetDateTime:
#GetMapping("/test")
public OffsetDateTime test() {
return OffsetDateTime.now();
}
The result is the same, it's returned as float value.
Then I tried the same end-point in a minimal Spring Boot project and it's returned in ISO format as expected:
"2021-03-18T15:39:14.5295632+01:00"
This all points to some transitive dependency messing up with the default Jackson serializers used by Sprint Boot. But mvn dependency:tree doesn't give me any suspicious dependencies (e.g. no gson marshaller dependency).
I also tried enabling TRACE logging, and I can see that the object written in HttpEntityMethodProcessor has the correctly formatted created_at time:
TRACE org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor - Writing [class MyObject {
....
createdAt: 2021-03-18T16:37:34.113316500+01:00
...
But it still ends up as float on the client side (testing on browser and with Postman). What could be the problem here?
After some debugging in Jackson classes, I found out that InstantSerializerBase#serialize method was being called with the default SerializerProvider (DefaultSerializerProviderImpl), which had the SerializationFeature.WRITE_DATES_AS_TIMESTAMPS feature enabled. That resulted in serializing OffsetDateTime values as epoch seconds + nanos.
I was able to fix the problem by adapting our WebMvcConfigurer implementation as follows:
#Configuration
#EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
// Some other configuration
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter(objectMapper()));
}
private ObjectMapper objectMapper() {
return new ObjectMapper()
.disable(WRITE_DATES_AS_TIMESTAMPS)
.registerModule(new JavaTimeModule());
}
}
After this change, OffsetDateTime fields are finally serialized in ISO format; e.g.
"created_at": "2021-03-19T17:05:27.785646+01:00"
Soution with configureMessageConverters is exacty what I needed. I have the same problem and you really helped me. Thanks!
Maybe you should report the solution to Spring
We use Apache Camel in Talend ESB Studio v6.4
In an ESB route, we consume JMS messages, process them then send them to an HTTP server. But that target server is down for maintainance every saturday from 6pm to 10pm.
How can we "pause" message consuming or message processing during that period ? I think quartz only works with file/ftp endpoints.
We could use a Processor component to check in Java if we are in the down period, but what to do after that ?
There are several ways to do this. One camel specific way to do it is through CamelControlBus. It takes in a routeId and performs an action (start/stop/resume etc) on it - Read more here to get an understanding Camel ControlBus
However, there is another approach that you can take. You can create a POJO bean that has 3 methods
shouldRouteStop() : to check the current time and decide if it should stop your route.
startRoute() : Starts a route if it is suspended
stopRoute() : Suspends a route if it is started
A simple implementation can be as follows:
public class ManagementBean {
public boolean shouldRouteStop() {
// Mocking the decision here
return new Random().nextBoolean();
}
public void startRoute(org.apache.camel.CamelContext ctx) throws Exception {
if (ctx.getRouteStatus("GenerateInvoices") == ServiceStatus.Suspended)
// replace the argument with your route Id
ctx.resumeRoute("GenerateInvoices");
}
public void stopRoute(org.apache.camel.CamelContext ctx) throws Exception {
if (ctx.getRouteStatus("GenerateInvoices") == ServiceStatus.Started)
// replace the argument with your route Id
ctx.suspendRoute("GenerateInvoices");
}
}
Make sure that the jms-route that you wish to control has a routeId and add this bean to your base/default CamelContext like this
main.bind("manageRouteBean", new ManagementBean());
Create another timer based route, that checks on each tick, if the route should be stopped or not and then suspends or resumes the route by routeId. This route can be implemented like below:
public class MonitoringRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
onException(Exception.class).log(exceptionMessage().toString());
from("timer:time?period=10000")
.choice()
.when().simple("${bean:manageRouteBean?method=shouldRouteStop}")
.log("Route Should Stop")
.bean(ManagementBean.class, "stopRoute(*)")
.otherwise()
.log("Route Should Start")
.bean(ManagementBean.class, "startRoute(*)")
.end();
}
}
Note that startRoute and stopRoute take the argument as *. This is camel way of automatically binding parameters based on type.
Finally, you can add this route to the main camel context like : main.addRouteBuilder(new MonitoringRoute());
For a complete implementation, have a look at this github repo
I have the following resource that consumes a JSON being mapped to a POJO.
#Path("example")
public class ExampleResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response addThesis(MyObject myObject) {
return Response.ok().entity("Test").build();
}
}
Here's the POJO class:
public class MyObject {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
When I send a POST request with the body {"title":"Test title"} everything works fine. The response is Test, as expected. However, when I change the request to {"titlee":"Test title"} the server replies with this:
Unrecognized field "titlee" (class com.my.package.MyObject), not marked as ignorable (one known property: "title"])
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#6dc6a46a; line: 2, column: 11] (through reference chain: com.my.package.MyObject["titlee"])
Obviously this is an exception thrown and returned by Jersey. How can I intercept this exception and return a custom status code and message?
What I've tried so far is to implement my own ExceptionMapper:
#Provider
public class MyJsonExceptionMapper implements ExceptionMapper<JsonProcessingException> {
public Response toResponse(JsonProcessingException e) {
return Response.status(400).entity("JSON Processing Error").build();
}
}
Unfortunately the response stays the same. When I implement an ExceptionMapper for a custom exception and throw the corresponding exception in the resource method though, everything works fine. I assume this has to do with the default ExceptionMapper for JsonProcessingException overriding my own one. Then I tried to create a generic mapper ("implements ExceptionMapper"), but again no success.
I've looked literally everywhere and tried many things including extending ResourceConfig and registering my mapper, but nothing has worked so far.
Some more information that might help to narrow the problem down: I am using Grizzly2 as the HTTP server which I am deploying as a Fat JAR.
The dependency part of my pom.xml looks like this:
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.24</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
<version>2.24</version>
</dependency>
</dependencies>
Any advice is highly appreciated.
Ok, this is dumb and hack-ish, but worked for me:
register(JacksonJaxbJsonProvider.class);
This is due to the following "nice default behavior" in the Jackson feature entry point:
if (!config.isRegistered(JacksonJaxbJsonProvider.class)) {
// add the default Jackson exception mappers
context.register(JsonParseExceptionMapper.class);
context.register(JsonMappingExceptionMapper.class);
:(
But, I'd still prefer an answer that fixes the problem "for real" - ie. without pre-registering components so that the feature cannot configure them properly...
I also faced this issue. If JacksonFeature is registered, you can simply register JacksonJaxbJsonProvider as a workaround.
When the JacksonFeature is in the classpath, it is automatically discovered by Jersey. Another approach to fix it is disabling auto discovery by setting ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE to true. As a result of this, you would need to register other features manually.
Alternatively you can get rid of the jersey-media-json-jackson artifact and use jackson-jaxrs-json-provider instead. With this, you will get rid of JacksonFeature and then you can register your own exception mappers.
One last option and probably what seems to be the correct solution (as pointed in Kysil Ivan's answer) you can write your own exception mapper and then give it a high priority, such as 1. If you use auto discovery, just annotate it with #Provider and #Priority:
#Provider
#Priority(1)
public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
...
}
If you manually register your provider, you can give your provider a binding priority:
#ApplicationPath("/")
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(JsonParseExceptionMapper.class, 1);
}
}
See this answer for more details.
We use JAX-RS on Wildfly to implement our web services and use the following to accomplish what you are trying to do with Jersey on Glassfish. Maybe it has similar features which you could look up. Our steps are:
The service is a stateless EJB, use EJB interceptor to trap exception
and populate request scoped object with details
Implement a PostProcessInterceptor which reads from request scoped object and modifies response before service returns. (This is specific to JAX-RS)
I use Jersey and I have the following Rest function which returns a JSON string when my server is deployed:
#GET
#Path("getallemployees")
#Produces("application/json")
public Response getAllEmployees() {
//building the entity object which is List<Employee>
return Response.ok(entity).build();
}
I need to develop some unit tests (not integration testing) and I want to somehow mock the HTTPRequest that invokes this method and then get the json String. The best option would be to use mockito for this.
Is there any suggestion on how to do it ?
Thanks !!
The problem is that the method returns a Response object to the caller which is deep within the framework code. It doesn't return JSON strings.
You can use Mockito, if you need to mock something inside the method itself. That should work.
But you may need to take the value returned by the method and convert it to JSON like this if you are using Jackson with Jersey.
Response response = getAllEmployees();
Object retval = response.getEntity();
try {
ObjectMapper mapper = new ObjectMapper();
// I like this formatting. You can change it.
mapper.configure(Feature.INDENT_OUTPUT, true);
mapper.configure(Feature.WRITE_ENUMS_USING_TO_STRING, true);
mapper.configure(Feature.USE_ANNOTATIONS, false);
mapper.configure(Feature.FAIL_ON_EMPTY_BEANS, false);
mapper.setSerializationInclusion(Inclusion.NON_NULL);
mapper.getSerializationConfig().setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
mapper.getSerializationConfig().withSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
String json = mapper.writeValueAsString(retval);
... assert something about the string
} catch (JsonProcessingException e) {
// do something
} catch (IOException e) {
// do something
}
Some of this is guess work and speculation on my part but it may help. You could try using the Jersey Test Framework with the InMemoryTestContainerFactory:
It starts Jersey application and directly calls internal APIs to handle request created by client provided by test framework. There is no network communication involved. This containers does not support servlet and other container dependent features, but it is a perfect choice for simple unit tests.
It looks like to use it, all you need to do is extend JerseyTest and then override getTestContainerFactory() and follow the rest of the instructions, e.g.:
public class EmployeeResourceTest extends JerseyTest {
#Override
protected Application configure() {
// set up employee resource with mock dependencies etc...
return new ResourceConfig().registerInstances(employeeResource);
}
#Test
public void getAllEmployees() {
final String response = target("getallemployees").request().get(String.class);
// assert etc...
}
}
I used registerInstances instead of registerClasses in configure() as it looks like you can present a ready made Resource but set up with any mock dependencies you may want - although I haven't tried this myself.
The test class is a bit inflexible as you can only do one-time set up of dependencies in the configure() method, so it might be worth investigating using the MockitoJUnitRunner - although I'm not sure if it will work with the JerseyTest inheritance. It could allow you to do add behaviour to mocks in each #Test method, e.g.:
#Mock
private EmployeeResourceDependency dependency;
#InjectMocks
private EmployeeResource employeeResource;
// configure() as above but without mock setup up etc...
#Test
public void getAllEmployees() {
given(dependency.getEmployees()).willReturn(...);
// etc...
But like I said it might not be possible to mix them at all.
I'm mapping my request's JSON POST data into an object using Spring's #RequestBody annotation and MappingJacksonHttpMessageConverter. However after that I'd like to read the data in String form to do some additional authentication. But when the marshalling has happened, the InputStream in HttpServletRequest is empty. Once I remove the #RequestBody parameter from the method the reading of POST data into a String works as expected.
Do I have to compromise by giving up the #RequestBody and doing the binding somehow manually or is there a more elegant solution?
So, basically you need to compute a hash of the request body. The elegant way to do it is to apply a decorator to the InputStream.
For example, inside a handler method (in this case you can't use #RequestBody and need to create HttpMessageConverter manually):
#RequestMapping(...)
public void handle(HttpServletRequest request) throws IOException {
final HashingInputStreamDecorator d =
new HashingInputStreamDecorator(request.getInputStream(), secretKey);
HttpServletRequest wrapper = new HttpServletRequestWrapper(request) {
#Override
public ServletInputStream getInputStream() throws IOException {
return d;
}
};
HttpMessageConverter conv = ...;
Foo requestBody = (Foo) conv.read(Foo.class, new ServletServerHttpRequest(wrapper));
String hash = d.getHash();
...
}
where hash is computed incrementally in overriden read methods of HashingInputStreamDecorator.
You can also use #RequestBody if you create a Filter to apply the decorator. In this case decorator can pass the computed hash to the handler method as a request attribute. However, you need to map this filter carefully to apply it only to the requests to specific handler method.
In your urlMapping bean you can declare list of additional interceptors:
<bean id="urlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<bean class="org.foo.MyAuthInterceptor"/>
</list>
</property>
</bean>
Those interceptors have access to HttpServletRequest, though if you read from the stream the chances are that parameter mapper won't be able to read it.
public class AuthInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
...
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mav) {
...
}
}
If I understand this correctly, one common way used with JAX-RS (which is somewhat similar to Spring MVC with respect to binding requests) is to first "bind" into some intermediate raw type (usually byte[], but String also works), and manually bind from that to object, using underlying data binder (Jackson). I often do this to be able to fully customize error handling of data binding.