How to remove/handle irrelevant or bad sort parameters from http url using Pageable interface in spring boot? - spring-data

How to remove/handle irrelevant or bad sort parameters from http url using Pageable interface in spring boot?
For e.g. I have a query like
http://localhost:8080/all?sort=firstName,asc&sort=nosuchfield,asc
How can I handle or remove the irrelevant field "nosuchfield"?
Also, how can I limit sort parameters in URL?

If the sorting field doesn't present in the database then below exception will be thrown by Spring JPA.
org.springframework.data.mapping.PropertyReferenceException: No property nosuchfield found for type <TYPE>!
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:94)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:382)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:358)
However, the exception can be handled using various types. Ultimately, you can just log it or transform it into any custom exception. As per my requirement, I have transformed it into a custom exception.
Using AOP
#Aspect
#Component
public class UnKnownColumnSortingExceptionHandler {
#AfterThrowing(pointcut = "execution(* com.repositorypackage.*.*(..))", throwing = "exception")
public void executeWhenExceptionThrowninRepository(JoinPoint jp, Throwable ex) {
if (ex instanceof PropertyReferenceException) {
throw new CustomException("Invalid Database operation");
}
}
}
Using #ControllerAdvice(Exception handling in Application wise)
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
public GlobalExceptionHandler() {}
#ExceptionHandler({PropertyReferenceException.class})
public ResponseEntity<Void> handleAllExceptions(Exception ex, WebRequest req) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Exception handling in Controller wise
Add the below piece of code to your controller
#ExceptionHandler({PropertyReferenceException.class})
public ResponseEntity<Void> handleAllExceptions(Exception ex, WebRequest req)
{
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}

Related

Spring AOP Pointcut for Spring Data Rest Controller (EndPoint)

I would like to do something on every api call to my spring boot app. I use Spring AOP to achieve this. Using:
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controller() {
}
#Pointcut("within(#org.springframework.web.bind.annotation.RestController *)")
public void restController() {
}
#After("(controller() || restController())")
public void loggingAdvice(JoinPoint joinPoint) {
// TODO: do something
}
Using that I can get all the event when API is being called. However, I am also using spring rest data for crud mechanism that automatically generate API end point, for example:
#RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
User findByEmail(String email);
}
The question is, can I create a point cut for every API end point that is generated by spring rest data?
Following pointcut will target all the RESTful endpoint calls made at "/users"
Considering the package of UserRepository is rg.so.example.datarest
#Pointcut("execution(* rg.so.example.datarest.UserRepository.*(..))")
public void dataRest() {
}
A more generic pointcut to target all the Repository implementations in a package rg.so.example.datarest would be
#Pointcut("execution(* rg.so.example.datarest..*(..))")

Spring Batch, JpaRepository and Rollback

I have a Spring Batch application(Spring Boot 2.3.5.RELEASE) that uses a JpaRepository to insert some custom log messages into a database as Spring Batch is processing. This is separate from the out of the box Spring Batch tables. Seems that when I throw an exception from my ItemProcessorAdapter, it is caught by the ItemProcessListener onProcessError() method. In this method I am performing a JpaRepository save() and flush(). No errors are logged, but once I leave this method the JpaRepository does a rollback.
Is this normal behavior? How can I get around it?
When using JpaRepository, is there a way to set a #Transactional(noRollbackFor = {xxxException.class})? I tried this and it seemed to have no effect.
Sample code snippet is below.
#Configuration
public class BatchJobConfiguration {
//Omitted for clarity....
#Bean
#StepScope
public CompositeItemProcessor<Decision,Decision> itemProcessor() {
CompositeItemProcessor<Decision,Decision> itemProcessor = new CompositeItemProcessor<>();
itemProcessor.setDelegates(Arrays.asList(
decisionValidatingItemProcessor(),
myItemProcessor(null)
));
return itemProcessor;
} // end itemProcessor()
#Bean
public BeanValidatingItemProcessor<Decision> decisionValidatingItemProcessor() {
BeanValidatingItemProcessor<Decision> beanValidatingItemProcessor = new BeanValidatingItemProcessor<>();
beanValidatingItemProcessor.setFilter(true);
return beanValidatingItemProcessor;
} // end decisionValidatingItemProcessor()
#Bean
public ItemProcessorAdapter<Decision,Decision> myItemProcessor(DecisionProcessingService service) {
ItemProcessorAdapter<Decision,Decision> adapter = new ItemProcessorAdapter<>();
adapter.setTargetObject(service);
adapter.setTargetMethod("processDecision");
return adapter;
}
#Bean
#StepScope
public DecisionItemProcessListener decisionItemProcessListener() {
return new DecisionItemProcessListener(mpJpaRepository);
}
}
#Service
public class DecisionProcessingService {
public Decision processDecision(Decision decision) throws BatchException {
....
throw new BatchException("An error occurred");
}
}
public class DecisionItemProcessListener implements ItemProcessListener<Decision,Decision> {
private MyJpaRepository mpJpaRepository;
public DecisionItemProcessListener(MyJpaRepository mpJpaRepository) {
this.mpJpaRepository = mpJpaRepository;
}
....
#Override
public void onProcessError(Decision decision, Exception e) {
MyEntityObject obj = MyEntityObject.builder()
.msg(e.getMessage())
.build();
mpJpaRepository.save(obj);
mpJpaRepository.flush();
// after this, the insert above is rolled back.
} // end onProcessError()
}
The callback you are using here ItemProcessListener#onProcessError is called with-in a transaction (driven by Spring Batch) that is going to be rolled-back due to the exception thrown by the item processor.
If you want to save data in that method, you need to use a new transaction (use the REQUIRES_NEW propagation).
EDIT: I shared a minimal complete example here: https://github.com/benas/spring-batch-lab/tree/master/issues/so64913980.

How to make Spring Data JPA repository return an exception on no result?

I'm migrating an existing project to Spring Data JPA repositories.
In the old project my DAOs looks like this:
public class MovieDAOImp implements MovieDAO {
public Movie findByTitle(String title) throws NoResultException {
/... query the db, if no Movie, throw new NoResultException */
}
}
The new Spring JPA implementation looks like this ...
public interface MovieRepository extends JpaRepository<Movie, Integer> {
Movie findByTitle(String title);
}
In the new version findByTitle will return null if there's no Movie, but I want it to throw an Exception (preferably my NoResultException, but any exception will do).
The problem I have is all of my Services are implemented expecting the NoResultException to occur.
I don't want to re-work all of my Services to check for nulls.
Is there a way to get the JpaRepository to throw an exception when there is no result?
I was unable to find a direct answer to my question, so I came up with this workaround ...
I continue to implement my own DAO and use the repo internally.
This has the advantage of giving me complete control over DAO interface, including the exceptions that get thrown, so I don't have to re-work my services.
But it has the disadvantage of adding an extra layer.
public class MovieDAOImp implements MovieDAO {
#Autowired
private MovieRepository repo;
public Movie findByTitle(String title) throws NoResultException {
Movie ret = repo.findByTitle(title);
if (ret == null) {
throw new NoResultException("Unable to find movie by title '"+title+"'.");
}
return ret;
}
}
If anyone comes up with a better way to do this, please post an answer!

handling generic HTTP exception with custom message in spring REST application

I am facing issues while handling HTTP exception in case of REST implementation
I want to display my custom Message to user in case of generic HTTP exception.
Trying to implement it using #ExceptionHandler annotation in controller advice, but that does not solve the problem.
I was looking into DefaultHandlerExceptionResolver but did not find any useful working example on the Net.
Can some one help me with this issue.
You can write your own application wide custom exception class, something like this.
#ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ResourceNotFoundException() {}
public ResourceNotFoundException(String message) {
super(message);
}
public ResourceNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
This custom exception can be thrown from any method then. For example:
protected void verifySomething() throws ResourceNotFoundException {
if(someConditionIsTrue()) {
throw new ResourceNotFoundException("Custom Message");
}
}

How to access multiple resources in a single request : Jersey Rest

I am trying to a find a good design for the following scenario.
I have a POST rest service which will be given an array of services as data. And which should in turn be calling them one by one to aggregate results on the server and send them back to the client.
#Path("/resource1")
#Path("/resource2")
#Path("/collection")
Post data to /collection
{["serviceName": "resource1", "data":"test1"], ["serviceName":"resource2","data":"test2"]}
The reason i need the resource1 and resource2 are, because those services can be called standalone also. I want to reuse the same setup if possible.
Is there any way to do this.
I am using jersey with spring.
Not sure what these resources have in common. If the post method has the same signature for all of them, you could have an abstract class or interface they implement defining the post method and can try using ResourceContext.matchResource to do this. E.g. something like this:
public abstract class AbstractResource {
public abstract String post(Object data);
}
#Path("/resource1")
public class Resource1 extends AbstractResource {
#POST
public String post(String data) {
// do something
}
}
#Path("/collection")
public class CollectionResource {
#Context
private ResourceContext rc;
#POST
#Consumes("application/json")
public String post(List<PostRequest> postRequests) {
StringBuilder result = new StringBuilder();
for (PostRequest pr : postRequests) {
// should wrap this in try-catch
AbstractResource ar = rc.matchResource(pr.resource,
AbstractResource.class);
sb.append(ar.post(pr.data));
}
return result.toString();
}
}
#XmlRootElement
public class PostRequest {
public String resource;
public String data;
}
Hopefully you got the idea and will be able to play with it and tweak it to fit your needs.