I am new to Spring AOP. I need to execute methods only if the user is authorized.
Here's my code.
#Before("some pointcut")
public HttpStatus checkUserAuthentication(String userName)
{
if( userAuthorized(userName) )
{
//go on executing method
} else {
return HttpStatus.FORBIDDEN;
}
}
Is there any alternative for ProceedingJoinPoint.proceed when using JoinPoint or can I use ProceedingJoinPoint with #Before advice? How to proceed with executing the if statement if the user is authorized.
I solved this using #Around advice. and changing the return type to Object so that it can return ProceedingJoinPoint on successfull verification.
#Around("some pointcut")
public Object checkUserAuthentication(ProceedingJoinPoint pjp, String userName)
{
if( userAuthorized(userName) )
{
return pjp.roceed();
} else {
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
}
}
using #Around as advice the control can be passed to the method after verification.
Related
I am developing REST API in Spring boot with Hibernate.
I have this function in my controller
#PostMapping("/profile")
public ResponseEntity<String> saveProfile(#Valid #RequestBody SaveProfileVM saveProfileVM,
BindingResult bindingResult)
throws JsonProcessingException {
if (bindingResult.hasErrors()) return super.fieldExceptionResponse(bindingResult);
Profile profile;
boolean optimisticLockException = true;
int retryCount = 0;
do {
try {
profile = accountService.saveProfile(saveProfileVM.getAccountId(),
saveProfileVM.getName(),
saveProfileVM.getEmail());
optimisticLockException = false;
retryCount++;
} catch (ObjectOptimisticLockingFailureException exception) {
retryCount++;
System.out.println(exception.getMessage());
}
} while (optimisticLockException && retryCount < MAX_OPTIMISTIC_LOCK_EXCEPTION_RETRY_COUNT);
return ResponseEntity.status(HttpStatus.OK).body(objectMapper.writeValueAsString(profile));
}
and MAX_OPTIMISTIC_LOCK_EXCEPTION_RETRY_COUNT is 3
I don't want to duplicate the do..while and try..catch blocks in every method where I need to check ObjectOptimisticLockingFailureException
do {
try{}
catch{}
} while()
Is there any way that I can pass accountService.saveProfile() to a general method that has the do..while and try..catch block so that I don't have to copy and paste the blocks to every method I need?
Every controller extends a BaseController so, it might be good to have the general method in BaseController?
#RestController
#RequestMapping("/account")
public class AccountController extends BaseController {
Can you guys give an idea please?
You can use spring-retry. More details
#Retryable(value = ObjectOptimisticLockingFailureException.class, maxAttempts = 3)
public void saveProfile(Long accountId, String name, String email){..}
I want to log Controller and the rest of packages differently. I know I can use 2 separate methods for this but these 2 methods are very similar, so I want to add a code to check that would look something like this
#Around("controllerPoint() || theRest()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
if( called from controllerPoint() ) {
execute this short section of code # (1)
}
// rest of code
What would this code be like?
Also, if after I execute (1) and I want to pass a variable to this same method again when it executes for other packages, how can I do it?
you can invoke the method like the below which will return your method name
joinPoint.getSignature().getName()
You could get method name, from join point:
#Aspect
#Configuration
public class TrackingConfig {
#Around("execution(* your.package.Controller.*(..))")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
if ("theRest".equals(methodName)) {
System.out.println("AROUND! theRest ");
} else if ("controllerPoint".equals(methodName)) {
System.out.println("AROUND! controllerPoint ");
}
return pjp.proceed();
}
}
When I go to /confirmation-account link, in tomcat console I can see that if and else block is also executed. I can see:
print from ColorConsoleHelper.getGreenLog("loginView") and from ColorConsoleHelper.getGreenLog("confirmationAccountView")
This is really strange behavior. Why?
#RequestMapping(value = "/confirmation-account", method = RequestMethod.GET)
#Transactional
public ModelAndView displayConfirmationAccountPage(ModelAndView modelAndView, #RequestParam Map<String, String> requestParams) {
final int ACTIVE_USER = 1;
// find the user associated with the confirmation token
UserEntity userEntity = userService.findUserByConfirmationToken(requestParams.get("token"));
// this should always be non-null but we check just in case
if (userEntity!=null) {
// set the confirmation token to null so it cannot be used again
userEntity.setConfirmationToken(null);
// set enabled user
userEntity.setEnabled(ACTIVE_USER);
// save data: (token to null and active user)
saveAll(userEntity.getTrainings());
/*
RedirectAttributes won't work with ModelAndView but returning a string from the redirecting handler method works.
*/
modelAndView.addObject("successMessage", "Konto zostało pomyślnie aktywowane!");
modelAndView.setViewName("loginView");
ColorConsoleHelper.getGreenLog("loginView");
} else {
ColorConsoleHelper.getGreenLog("confirmationAccountView");
modelAndView.addObject("errorMessage", "Link jest nieprawidłowy...");
modelAndView.setViewName("confirmationAccountView");
}
return modelAndView;
}
public void saveAll(List<TrainingUserEntity> trainingUserEntityList) {
for ( TrainingUserEntity trainingUserEntity : trainingUserEntityList) {
entityManagerService.mergeUsingPersistenceUnitB(trainingUserEntity);
}
}
public void mergeUsingPersistenceUnitB(Object object) {
EntityManager entityManager = getEntityManagerPersistenceUnitB();
EntityTransaction tx = null;
try {
tx = entityManager.getTransaction();
tx.begin();
entityManager.merge(object);
tx.commit();
}
catch (RuntimeException e) {
if ( tx != null && tx.isActive() ) tx.rollback();
throw e; // or display error message
}
finally {
entityManager.close();
}
}
Below solution & explanation:
Because of /confirmation-account link is invoke twice, what is caused by dynamic proxy and #Transactional method annotated in controller It is mandatory to check how many displayConfirmationAccountPage method is invoked. It is workaround.
What do you think it is good or not to annotated #Transactional controller method?
I'm looking at some existing code and wanted to know what happen's in the following scenario with Spring's #Transactional annotation? Consider the following example:
A POST request hits a #Controller annotated with #Transactional:
#ResponseBody
#Transactional
#RequestMapping(value="/send", method=RequestMethod.POST)
public void send(#RequestBody Response response) {
try {
DBItem updatedDbItem = repository.updateResponse(response);
if (updatedDbItem == null){
//some logging
}
} catch(Exception ex) {
//some logging
}
}
The controller calls a non #transactional repository method which sets a value and in turns calls a another #Transactional method:
#Override
public DBItem updateResponse(Response response) {
try {
DBItem dBItem = findResponseById(response.getKey());
if (dBItem != null){
dBItem.setSomeField(response.getValue());
return updateDataBaseItem(response);
}
} catch (Exception ex) {
//some logging
}
return null;
}
The following updateDataBaseItem() method is common and called from other non transactional methods as well as the above method:
#Transactional
#Override
public DBItem updateDataBaseItem(Response response){
try {
DBItem dBItem = em.merge(response);
return dBItem;
} catch (Exception ex) {
//some logging
}
return null;
}
send() => spring detect #transaction with default parameters
actually Propagation setting is REQUIRED and the spring join the exist transaction or create new if none.
repository.updateResponse(..) => No transactions params the method execute within the same transaction already exist
updateDataBaseItem(..) => calling the method in same repository , spring will not recognize the #Transaction annotation because the use of proxy mode, so this method will be executed within the same transaction
a method within the target object calling another method of the target
object, will not lead to an actual transaction at runtime even if the
invoked method is marked with #Transactional
Spring 3 #AspectJ in use.
One target method below,
public void testParam(#Deprecated String param) {
}
Two advices below,
#Before("args(java.lang.String)")
public void checkStrParam() {
System.out.println("str param found");
}
#Before("#args(java.lang.Deprecated)")
public void checkDepParam() {
System.out.println("dep param found");
}
Invocation on the target method only executes the args advice. What does the #args advice lack of?