I've added AOP to my little SpringBoot app to log inputs and outputs of the all methods:
#Aspect
#Component
#AllArgsConstructor
public class MyAspect{
private final ObjectMapper mapper;
#Pointcut("within(com.mypackage..*)")
protected void all() {}
#Before("all()")
public void before(final JoinPoint joinPoint) {
final Class<?> classData = joinPoint.getTarget().getClass();
final MethodSignature signature = (MethodSignature) joinPoint.getSignature();
final Map<Object, Object> parameters = this.getParameters(joinPoint);
try {
log.info(
"Class {} Method{} Parameters: {}",
classData.getSimpleName(),
signature.getName(),
this.mapper.writeValueAsString(parameters)
);
}
catch (final JsonProcessingException e) {
log.error("Error in JSON mapper", e);
}
}
#AfterReturning(value = "all()", returning = "returning")
public void after(final JoinPoint joinPoint, final Object returning) {
final Class<?> classData = joinPoint.getTarget().getClass();
final MethodSignature signature = (MethodSignature) joinPoint.getSignature();
try {
log.info(
"Class {} Method {} Returning {}",
classData.getSimpleName(),
signature.getName(),
this.mapper.writeValueAsString(returning)
);
}
catch (final JsonProcessingException e) {
log.error("Error in JSON mapper", e);
}
}
}
I have had a recursion problem in mapper.writeValueAsString(...). When I have an object with an attribute that inside has the same parent object (JPA relationship for example) it enters an infinite loop when it tries to deserialize this tree of objects. I have solved this problem by adding #JsonIgnoreProperties(value = {"entityParent"}) in the child entity, so that it does not deserialize the parent object again.
My current problem is that there are objects from Spring, from the framework, that I cannot edit to add this JSON property and they are giving me the same problem when deserializing.
My question is, how can I globally ignore this recursion for all objects?
Related
I want to add some log in my service,so I using Annotation and AOP.At first,I write the code like this:
#Aspect
#Component
public class LogAspect {
#Autowired
LogService logService;
#Transactional
Object log(ProceedingJoinPoint point) throws Throwable{
Object obj = null;
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
Method method = methodSignature.getMethod();
LogAnnotation myAnno = method.getAnnotation(LogAnnotation.class);
String pageId=null;
JSONObject object=(JSONObject)point.getArgs()[0];
pageId=object.getString("pageId");
obj = point.proceed(); //do business job and affect the database
int i=1/0; //test Transactional
logService.insertLog(pageId,(LogVo)obj);
return obj;
}
#Around("#annotation(com.mycompany.annotation.LogAnnotation)")
public Object triggerSome(ProceedingJoinPoint pjp) throws Throwable {
return log( pjp);
}
}
In my example,I have add #Transactional and add / by zero exception,but when do business job code still affect the database.It seems nothingt to do with the word #Transactional.How to change my code?
I have try to change all my code in to a service,but still have not Transaction.
#Aspect
#Component
public class LogAspect {
#Autowired
LogService logService;
#Around("#annotation(com.mycompany.annotation.LogAnnotation)")
public Object triggerSome(ProceedingJoinPoint pjp) throws Throwable {
return logService.commonLog( pjp);
}
}
The common log service is :
#Transactional(rollbackFor=Throwable.class)
#Override
public Object commonLog(ProceedingJoinPoint point) throws Throwable{
Object obj = null;
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
Method method = methodSignature.getMethod();
LogAnnotation myAnno = method.getAnnotation(LogAnnotation.class);
String pageId=null;
JSONObject object=(JSONObject)point.getArgs()[0];
pageId=object.getString("pageId");
obj = point.proceed();
int i=1/0;
LogService.insertLog(pageId,(LogVo)obj);
return obj;
}
when i am trying to get values by id ..i got error like this in postman
{
"timestamp": 1547708533031,
"status": 500,
"error": "Internal Server Error",
"exception":"org.springframework.http.converter.HttpMessageNotWritableException",
"message": "Could not write JSON: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.Cetegory.Entites.ArtistRegister[\"subcetgory\"]->com.Cetegory.Entites.SubCetegory_$$_jvst706_1[\"handler\"])",
"path": "/getartist/2"
}
this is contoller for get data by id
#RestController
public class RegisterController {
#Autowired
ArtistService artser;
#PostMapping(value="/addArtist",produces="application/json")
#ResponseBody public ArtistRegister addArtist(#RequestBody ArtistRegister artist) {
ArtistRegister artRegister = null;
try {
artRegister = artser.addArtist(artist);
} catch (Exception e) {
e.printStackTrace();
}
return artRegister;
}
#RequestMapping(value="/getartist/{artist_id}",method=RequestMethod.GET,produces="application/json")
#ResponseBody public ArtistRegister getArtistDetails(#PathVariable ("artist_id") int artist_id ,HttpServletRequest request,
HttpServletResponse response) throws Exception{
return artser.getArtistDetails(artist_id);
}
#RequestMapping(value="/delete/{artist_id}",method=RequestMethod.DELETE,produces="application/json")
public void deleteById(#PathVariable (value="artist_id") int artist_id,HttpServletRequest request,
HttpServletResponse response) throws Exception{
artser.deleteById(artist_id);
}
#RequestMapping(value = "/updateartist", method = RequestMethod.PUT, produces = "application/json")
public ArtistRegister updateArtist(#RequestBody ArtistRegister artreg, HttpServletRequest request, HttpServletResponse response)
throws Exception
{
return artser.updateArtist(artreg);
}
this is service
#Service
#Transactional
public class ArtistService {
#Autowired
private ArtistRepository artrep;
#Autowired
private RegisterDAO artdao;
public ArtistRegister addArtist(ArtistRegister artreg) {
ArtistRegister artReg = null;
try {
artReg = artrep.save(artreg);
} catch (Exception e) {
e.printStackTrace();
}
return artReg;
}
public ArtistRegister getArtistDetails(int artist_id) {
return artdao.getArtistDetails(artist_id);
}
public void deleteById(int artist_id) {
artdao.deleteById(artist_id);
}
public ArtistRegister updateArtist(ArtistRegister artreg) {
return artdao.updateArtist(artreg);
}
}
this is DAO
#Repository
#Transactional
public class RegisterDAO {
private static final Logger logger = LoggerFactory.getLogger(SubCetegoryDAO.class);
#Autowired
SessionFactory sessionFactory;
#Autowired
EntityManager entitymanager;
public ArtistRegister getArtistDetails(int artist_id) {
try
{
String hql = "FROM ArtistRegister a where a.artist_id=?";
return (ArtistRegister) entitymanager.createQuery(hql).setParameter(1, artist_id).getSingleResult();
}
catch (EmptyResultDataAccessException e)
{
return null;
}
catch (Exception e)
{
logger.error("Exception in getUser"+ e.getMessage());
return null;
}
}
Remove #JsonManagedReference annotation and update fetch type to LAZY, by following way:
#OneToOne(targetEntity = SubCetegory.class, cascade = CascadeType.MERGE,fetch=FetchType.LAZY)
#JoinColumn(name = "sub_cetegory_id")
What are fetch types Lazy and Eager?
The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed.
REFERENCES
Official documentation fetch type
I'm trying to implementing non-blocking call. in spring 4, But unfortunately it's throwing the below error.
Type mismatch: cannot convert from String to ListenableFuture
and also same error can not able convert from Map to ListenableFuture>.
My Method call stack is as below.
ListenableFuture<Map<String,String>> unusedQuota = doLogin(userIdentity,request,"0");
doLogin login simply return Map
is there any converter required?
what changes would be required ?
Thanks.
public class MyController {
final DeferredResult<Map<String,String>> deferredResult = new DeferredResult<Map<String,String>>(5000l);
private final Logger log = LoggerFactory.getLogger(MyController.class);
#Inject
RestTemplate restTemplate;
#RequestMapping(value = "/loginservice", method = RequestMethod.GET)
#Timed
public DeferredResult<Map<String,String>> loginRequestService(#RequestParam String userIdentity,HttpServletRequest request) throws Exception {
deferredResult.onTimeout(new Runnable() {
#Override
public void run() { // Retry on timeout
deferredResult.setErrorResult(ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).body("Request timeout occurred."));
}
});
#SuppressWarnings("unchecked")
ListenableFuture<Map<String,String>> unusedQuota = doLogin(userIdentity,request);
unusedQuota.addCallback(new ListenableFutureCallback<Map<String,String>>() {
#SuppressWarnings("unchecked")
#Override
public void onSuccess(Map<String, String> result) {
// TODO Auto-generated method stub
deferredResult.setResult((Map<String, String>) ResponseEntity.ok(result));
}
#Override
public void onFailure(Throwable t) {
// TODO Auto-generated method stub
deferredResult.setErrorResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(t));
}
});
return deferredResult;
}
private Map<String,String> doLogin(String userIdentity,HttpServletRequest request) throws Exception{
Map<String,String> unusedQuota=new HashMap<String,String>();
unusedQuota.put("quota", "100");
return unusedQuota;
}
}
}
You are NOT passing the Map object when there is an exception which is causing the issue, so your controller method needs to be changed as shown below, also move deferredResult object inside the Controller method as you should share the same instance of deferredResult for different user request.
public class MyController {
#Autowired
private TaskExecutor asyncTaskExecutor;
#RequestMapping(value = "/loginservice", method = RequestMethod.GET)
#Timed
public DeferredResult<Map<String,String>> loginRequestService(#RequestParam String userIdentity,HttpServletRequest request) throws Exception {
final DeferredResult<Map<String,String>> deferredResult = new DeferredResult<Map<String,String>>(5000l);
deferredResult.onTimeout(new Runnable() {
#Override
public void run() { // Retry on timeout
Map<String, String> map = new HashMap<>();
//Populate map object with error details with Request timeout occurred.
deferredResult.setErrorResult(new ResponseEntity
<Map<String, String>>(map, null,
HttpStatus.REQUEST_TIMEOUT));
}
});
ListenableFuture<String> task = asyncTaskExecutor.submitListenable(new Callable<String>(){
#Override
public Map<String,String> call() throws Exception {
return doLogin(userIdentity,request);
}
});
unusedQuota.addCallback(new ListenableFutureCallback<Map<String,String>>() {
#SuppressWarnings("unchecked")
#Override
public void onSuccess(Map<String, String> result) {
// TODO Auto-generated method stub
deferredResult.setResult((Map<String, String>) ResponseEntity.ok(result));
}
#Override
public void onFailure(Throwable t) {
Map<String, String> map = new HashMap<>();
//Populate map object with error details
deferredResult.setErrorResult(new ResponseEntity<Map<String, String>>(
map, null, HttpStatus.INTERNAL_SERVER_ERROR));
}
});
return deferredResult;
}
}
Also, you need to ensure that you are configuring the ThreadPoolTaskExecutor as explained in the example here.
I try to throw a specific exception when a method call via using doThrow. Then i expect to handle it by a method in its superclass which is annotated #ExceptionHandler.
1. Should i use Spy or Mock object for my controller class(it is a spring bean)
2. Should i use InjectMocks
3. Should i test within spring context because of ExceptionHandler class which is belonged to Spring
Here is my rough view of Controller class:
DefaultPageController extends SuperClass
{
#RequestMapping(method = RequestMethod.GET)
public String get(final Model model, final HttpServletRequest request, final HttpServletResponse response)
throws CMSItemNotFoundException
{....}
}
And ParentClass of my Controller(it is abstract)
public abstract class SuperClass
{...
#ExceptionHandler(InvalidCsrfTokenException.class)
public String handleInvalidCsrfTokenException(final InvalidCsrfTokenException exception, final HttpServletRequest request)
{
request.setAttribute("message", exception.getMessage());
LOG.error(exception.getMessage(), exception);
return FORWARD_PREFIX + "/404";
}
...
}
Finally my test class:
#IntegrationTest
//#RunWith(SpringJUnit4ClassRunner.class)
public class PageRedirectOnCSRFTest
{
#Mock
private Model model;
#Mock
private HttpServletRequest request;
#Mock
private HttpServletResponse response;
#Mock
private InvalidCsrfTokenException invalidCsrfTokenException;
#Mock
private MissingCsrfTokenException missingCsrfTokenException;
#InjectMocks
#Resource
private DefaultPageController controller;
#Before
public void setUp()
{
MockitoAnnotations.initMocks(this);
try
{
doThrow(invalidCsrfTokenException).when(controller).get(model, request, response);
}
catch (final Exception e)
{
}
}
//private final DefaultPageController controller = Mockito.spy(new DefaultPageController());
// private final InvalidCsrfTokenException invalidCsrfTokenException = new InvalidCsrfTokenException(
// Mockito.mock(CsrfToken.class), "1234");
// private final MissingCsrfTokenException missingCsrfTokenException = new MissingCsrfTokenException("1234");
#Test
public void testIfCalledHandleInvalidCsrfTokenException()
{
try
{
controller.get(model, request, response);
}
catch (final Exception e)
{
// YTODO Auto-generated catch block
Assert.assertTrue(e instanceof InvalidCsrfTokenException);
Mockito.verify(controller, Mockito.times(1)).handleInvalidCsrfTokenException(invalidCsrfTokenException, request);
}
}
}
Thx and brgs
I have a rest service which send an 404 error when the resources is not found.
Here the source of my controller and the exception which send Http 404.
#Controller
#RequestMapping("/site")
public class SiteController
{
#Autowired
private IStoreManager storeManager;
#RequestMapping(value = "/stores/{pkStore}", method = RequestMethod.GET, produces = "application/json")
#ResponseBody
public StoreDto getStoreByPk(#PathVariable long pkStore) {
Store s = storeManager.getStore(pkStore);
if (null == s) {
throw new ResourceNotFoundException("no store with pkStore : " + pkStore);
}
return StoreDto.entityToDto(s);
}
}
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException
{
private static final long serialVersionUID = -6252766749487342137L;
public ResourceNotFoundException(String message) {
super(message);
}
}
When i try to call it with RestTemplate with this code :
ResponseEntity<StoreDto> r = restTemplate.getForEntity(url, StoreDto.class, m);
System.out.println(r.getStatusCode());
System.out.println(r.getBody());
I receive this exception :
org.springframework.web.client.RestTemplate handleResponseError
ATTENTION: GET request for "http://........./stores/99" resulted in 404 (Introuvable); invoking error handler
org.springframework.web.client.HttpClientErrorException: 404 Introuvable
I was thinking I can explore my responseEntity Object and do some things with the statusCode. But exception is launch and my app go down.
Is there a specific configuration for restTemplate to not send exception but populate my ResponseEntity.
As far as I'm aware, you can't get an actual ResponseEntity, but the status code and body (if any) can be obtained from the exception:
try {
ResponseEntity<StoreDto> r = restTemplate.getForEntity(url, StoreDto.class, m);
}
catch (final HttpClientErrorException e) {
System.out.println(e.getStatusCode());
System.out.println(e.getResponseBodyAsString());
}
RESTTemplate is quite deficient in this area IMO. There's a good blog post here about how you could possibly extract the response body when you've received an error:
http://springinpractice.com/2013/10/07/handling-json-error-object-responses-with-springs-resttemplate
As of today there is an outstanding JIRA request that the template provides the possibility to extract the response body:
https://jira.spring.io/browse/SPR-10961
The trouble with Squatting Bear's answer is that you would have to interrogate the status code inside the catch block eg if you're only wanting to deal with 404's
Here's how I got around this on my last project. There may be better ways, and my solution doesn't extract the ResponseBody at all.
public class ClientErrorHandler implements ResponseErrorHandler
{
#Override
public void handleError(ClientHttpResponse response) throws IOException
{
if (response.getStatusCode() == HttpStatus.NOT_FOUND)
{
throw new ResourceNotFoundException();
}
// handle other possibilities, then use the catch all...
throw new UnexpectedHttpException(response.getStatusCode());
}
#Override
public boolean hasError(ClientHttpResponse response) throws IOException
{
return response.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR
|| response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR;
}
The ResourceNotFoundException and UnexpectedHttpException are my own unchecked exceptions.
The when creating the rest template:
RestTemplate template = new RestTemplate();
template.setErrorHandler(new ClientErrorHandler());
Now we get the slightly neater construct when making a request:
try
{
HttpEntity response = template.exchange("http://localhost:8080/mywebapp/customer/100029",
HttpMethod.GET, requestEntity, String.class);
System.out.println(response.getBody());
}
catch (ResourceNotFoundException e)
{
System.out.println("Customer not found");
}
Since it's 2018 and I hope that when people say "Spring" they actually mean "Spring Boot" at least, I wanted to expand the given answers with a less dust-covered approach.
Everything mentioned in the previous answers is correct - you need to use a custom ResponseErrorHandler.
Now, in Spring Boot world the way to configure it is a bit simpler than before.
There is a convenient class called RestTemplateBuilder. If you read the very first line of its java doc it says:
Builder that can be used to configure and create a RestTemplate.
Provides convenience methods to register converters, error handlers
and UriTemplateHandlers.
It actually has a method just for that:
new RestTemplateBuilder().errorHandler(new DefaultResponseErrorHandler()).build();
On top of that, Spring guys realized the drawbacks of a conventional RestTemplate long time ago, and how it can be especially painful in tests. They created a convenient class, TestRestTemplate, which serves as a wrapper around RestTemplate and set its errorHandler to an empty implementation:
private static class NoOpResponseErrorHandler extends
DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
}
}
You can create your own RestTemplate wrapper which does not throw exceptions, but returns a response with the received status code. (You could also return the body, but that would stop being type-safe, so in the code below the body remains simply null.)
/**
* A Rest Template that doesn't throw exceptions if a method returns something other than 2xx
*/
public class GracefulRestTemplate extends RestTemplate {
private final RestTemplate restTemplate;
public GracefulRestTemplate(RestTemplate restTemplate) {
super(restTemplate.getMessageConverters());
this.restTemplate = restTemplate;
}
#Override
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException {
return withExceptionHandling(() -> restTemplate.getForEntity(url, responseType));
}
#Override
public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException {
return withExceptionHandling(() -> restTemplate.postForEntity(url, request, responseType));
}
private <T> ResponseEntity<T> withExceptionHandling(Supplier<ResponseEntity<T>> action) {
try {
return action.get();
} catch (HttpClientErrorException ex) {
return new ResponseEntity<>(ex.getStatusCode());
}
}
}
Recently had a usecase for this. My solution:
public class MyErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
return hasError(clientHttpResponse.getStatusCode());
}
#Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
HttpStatus statusCode = clientHttpResponse.getStatusCode();
MediaType contentType = clientHttpResponse
.getHeaders()
.getContentType();
Charset charset = contentType != null ? contentType.getCharset() : null;
byte[] body = FileCopyUtils.copyToByteArray(clientHttpResponse.getBody());
switch (statusCode.series()) {
case CLIENT_ERROR:
throw new HttpClientErrorException(statusCode, clientHttpResponse.getStatusText(), body, charset);
case SERVER_ERROR:
throw new HttpServerErrorException(statusCode, clientHttpResponse.getStatusText(), body, charset);
default:
throw new RestClientException("Unknown status code [" + statusCode + "]");
}
}
private boolean hasError(HttpStatus statusCode) {
return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR ||
statusCode.series() == HttpStatus.Series.SERVER_ERROR);
}
There is no such class implementing ResponseErrorHandler in Spring framework, so I just declared a bean:
#Bean
public RestTemplate getRestTemplate() {
return new RestTemplateBuilder()
.errorHandler(new DefaultResponseErrorHandler() {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
//do nothing
}
})
.build();
}
The best way to make a RestTemplate to work with 4XX/5XX errors without throwing exceptions I found is to create your own service, which uses RestTemplate :
public ResponseEntity<?> makeCall(CallData callData) {
logger.debug("[makeCall][url] " + callData.getUrl());
logger.debug("[makeCall][httpMethod] " + callData.getHttpMethod());
logger.debug("[makeCall][httpEntity] " + callData.getHttpEntity());
logger.debug("[makeCall][class] " + callData.getClazz());
logger.debug("[makeCall][params] " + callData.getQueryParams());
ResponseEntity<?> result;
try {
result = restTemplate.exchange(callData.getUrl(), callData.getHttpMethod(), callData.getHttpEntity(),
callData.getClazz(), callData.getQueryParams());
} catch (RestClientResponseException e) {
result = new ResponseEntity<String>(e.getResponseBodyAsString(), e.getResponseHeaders(), e.getRawStatusCode());
}
return result;
}
And in case of exception, simply catch it and create your own ResponseEntity.
This will allow you to work with the ResponseEntity object as excepted.