Pass/inject object to adviced method in spring - java

Here is the situation. I have a starter which performs checks on every request. I have performed it by creating such an aspect:
#Around("execution(* (#org.springframework.web.bind.annotation.RestController *).*(..))")
public Object check(final ProceedingJoinPoint joinPoint) {
CheckResponse checkResponse = client.check();
if (checkResponse.getDate().before(new Date())) {
throw new CheckException("message");
}
try {
return joinPoint.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
Everything worked fine until I needed to use this CheckResponse in my rest controller.
How can I get it inside there? I thought about creating request scoped beans inside my aspect and injecting it into controller with spring but I can't figure out how to do it programmatically. I guess another way can be using reflection but I am inclined to more elegant ways. Maybe you have any ideas?
P.S. The controller method has its own parameters such as #RequestParam, and using joinPoint.proceed() can override those parameters. I am trying to make this thing as much universal as possible.

Related

#Transactionnal annotation in spring boot does it really create a transaction?

I want to call different methods that interact with my database in one method.
something like this :
#Autowired
EnteteService es; // service for jpaRepository
#Autowired
SystemOracleServiceJpa so; // service using jdbcTemplate
#Autowired
DetailService ds; // service for jpaRepository
#Transactional
public void configure(EnteteAR entete) throws ConfigurationException {
try{
this.es.save(entete); // first methode
List<DetailAR> details = this.so.getStructure(entete.getTableName());
if(details.size()>0){
this.ds.saveAllDetails(details); // second
this.so.CreateTable(details, entete.getTableName(), "DEM");//third
this.so.createPkIdxDem(entete.getTableName()); // fourth
this.so.CreateTable(details, entete.getTableName(), "BACK"); // fifth
}
else{
throw new ConfigurationException("Configuration error");
}
}catch(Exception e){
throw new ConfigurationException(e.getMessage());
}
}
I want to commit only if no errors appears in all this methods inside my main method "configure".
I was thinking that #transactionnal annotation work for this, but that commit after each method inside.
Exemple :
if this.es.save work and this.ds.saveAllDetails dont, I find data of es.save on database :(
Someone can help my please ?
thank with advance for your reading and your potential help.
#Transactional will automatically invoke a rollback if an unchecked exception is thrown from the executed method.
ConfigurationException in your case is a checked exception and hence it does not work.
You can make it work by modifying your annotation to
#Transactional(rollbackOn = ConfigurationException.class)
public void configure(EnteteAR entete) throws ConfigurationException {
try{ ....

Quarkus asynchronous

I try to develop a scenario here my code must be asynchronous using quarkus framework, bellow a snippet of my code:
#Inject
ThreadContext threadContext;
#Inject
ManagedExecutor managedExecutor
#Transactional
#ActivateRequestContext
private void asyncMethod(DataAccessAuthorisationEntity dataAccess) {
dataAccess.setStatus(IN_PROGRESS);//!!!!!!!!!
dataAccessAuthorisationRepository.persist(dataAccess);
threadContext.withContextCapture(CompletableFuture.completedFuture("T")).runAsync(()->{
logger.info("[][][] for dataAccess id we begin the treatement "+dataAccess.getId());
boolean exit = false;
PortfolioEntity portfolioEntity = portfolioRepository.findById(dataAccess.getPortfolioId());
System.out.println("");
try {
logger.info("[BEGIN][copyFileAfterSharing] for data access id= "+dataAccess.getId());
String portfolioId = portfolioEntity.getExternalId() + "_" + portfolioEntity.getExternalIdType().getCode();
fileService.copyFileOnAnotherServer(new CopyObject(portfolioId, dataAccess.getStartPoint().toString(),
dataAccess.getEmitterOrganisationId(), dataAccess.getRecipientOrganisationId()));
} catch (Exception e) {
dataAccess.setStatus(PENDING);//!!!!!!!!!
dataAccessAuthorisationRepository.persist(dataAccess);
logger.info("[ERROR][copyFileAfterSharing][BELLOW STACKTRACE] for data access id= "+dataAccess.getId());
e.printStackTrace();
exit= true;
}
},managedExecutor);
}
but I get always when I my execution pass by the exception catch and When I call dataAccessAuthorisationRepository.persist(dataAccess) I get:
Transaction is not active, consider adding #Transactional to your
method to automatically activate one.
because I update my entity dataAccess twice time in the same transaction
Quarkus creates a proxy wrapper around your instance that is injected. When you call a method of a manged bean you call actually this proxy object, that hanldes annotations. If you call a mehtod via "this." the Bean-container/proxy will not detect this call as the call does not go thorugh it. You can't use annotations on calls with "this.".

JavaEE Globally Catch Runtime exception

I want to "globally" catch a Runtime exception, now this sounds silly right but let me explain.
I have created a interceptor that I use on all my ejbs which require authorization to use, this interceptor is called every time a method is called.
See here the code:
#Secure
#Interceptor
public class SecurityInterceptor {
#EJB
private SessionManager sessionManager;
#AroundInvoke
private Object securityCheck(InvocationContext ctx) throws Exception {
System.out.println("hello");
List<UserGroup> allowedRoles = (List<UserGroup>) ctx.getContextData().get("rolesAllowed");
sessionManager.isAuthorized(allowedRoles);
return ctx.proceed();
}
}
Now I don't want in my Named Bean to surround the body of every method that uses one of these secured beans with a try and catch block, like this:
public List<Contracts> getContracts() {
List<Contracts> contracts = new ArrayList<>();
try {
contracts = contractEntityManager.getAll();
} catch (EJBTransactionRolledbackException e) {
Throwable throwable = ExceptionUtils.getRootCause(e);
if (throwable instanceof NotAuthenticatedException) {
System.out.println("Not Authenticated");
}
else if (throwable instanceof UnAuthorizedException) {
System.out.println("Not Authorized");
}
}
return contracts;
}
So is there some way to globally catch a runtime exception and the redirect the user to the login page if he is unauthenticated and to a error page when he is unauthorized.
Maybe my design is just generally bad and I would need to complety rethink this idea.
Please let me know.
Thank you
So, in the end, it is the in the web tier that you want to catch the exceptions. This is easy:
If you are only on servlets, specify the error-page element in web.xml for each exception you wish to handle, e.g. as follows:
<error-page>
<exception-type>fully.quallified.NotAuthenticatedException</exception-type>
<location>/where/to/redirect/eg/login</location>
</error-page>
You declare the exception you want to handle globally and the URL you want to handle it, it may be a servlet, a JSP or any other resource.
JAX-RS offers a similar mechanism with the javax.ws.rs.ext.ExceptionMapper:
#Provider
public class NotAuthenticatedExceptionMapper implements ExceptionMapper<NotAuthenticatedException> {
...
}

#transactional rollback not working

Below is what I did, I need to implement rollback, using #transactional annotation, but not working as expected, what else need to be done for proper rollback to happen ?? I want that when the code is executed result in db should be "testingOne" , currently it is set to "notRollBacked". Can you please point my mistake.
public Response deleteUser(Request argVO)throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setUserName("testingOne");
UsersLocalServiceUtil.updateUsers(users);
try
{
testRollbackFunction();
}
catch (Exception ex)
{
}
return new Response();
}
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
private void testRollbackFunction() throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setUserName("notRollbacked");
UsersLocalServiceUtil.updateUsers(users);
throw new PortalException();
}
****************Edit 1*************
I did what was mentioned in answers:
I did taken bean from context
and written a class/bean as
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
public class RollBack
{
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
public void thisWillRollBack() throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setBarringReason("notRollbacked");
UsersLocalServiceUtil.updateUsers(users);
throw new PortalException();
}
}
spring xml file bean refrence set as :
<bean id="rollBackBean" class="com.alepo.RollBack">
</bean>
public Response myMethod(Request argVO)throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setBarringReason("testingOne");
UsersLocalServiceUtil.updateUsers(users);
try
{
Test test = new Test();
Object obj = test.getBean();
RollBack rollBack = (RollBack)obj;
rollBack.thisWillRollBack();
}
catch (Exception ex)
{
ex.printStackTrace();
}
return new Response();
}
#################EDIT 4
now calling rollback function as :
RollBack rollBack = (RollBack)PortalBeanLocatorUtil.getBeanLocator().locate("rollBackBean");
rollBack.thisWillRollBack();
No Test class in picture now ...no new anywhere ...
still NOT WORKING .......
If you have a #Transactional annotation on method, Spring wraps the call to this method with aspect handling the transaction.
So:
1) Only public methodes can be wrapped in aspect
2) You call wrapped code only if you call the method on a bean taken from / injected by Spring container.
In your case:
1) The code isn't wrapped in transactional aspect because it is not public method
2) Event if it was, it is called directly from within the class, so you wouldn't call wrapped version anyway.
So the solution is to make separate bean with #Transactional method, inject it into and call it from Response class.
Of course you need <tx:annotation-driven/> in your spring-xml or instruct Spring otherwise to process #Transactional annotations (see the reference).
The issue is you are outside the application context. You are creating a new instance of a class, NEW is bad in Spring, very bad. Get an instance of Test from the application context, not by creating a new instance unless you start your application context in Test. Try to Autowire test in your class you mention above or inject it from Spring and then let me know, but the code you are showing above will never work with transaction management.

Capturing parameters of a method at runtime in Java

Our application uses several back-end services and we maintain wrappers which contain the methods to make the actual service calls. If any exception occurs in any of those methods while invoking a service, we throw a custom exception encapsulating the original exception as shown below.
interface IServiceA {
public void submit(String user, String attributes);
}
public class ServiceAWrapper implements IserviceA {
private ActualService getActualService() {
.....
}
public void submit(String user, String attributes) {
try {
Request request = new Request();
request.setUser(user);
request.setAttributes(attributes);
getActualService().call(request);
} catch(ServiceException1 e) {
throw new MyException(e, reason1);
} catch(ServiceException2 e) {
throw new MyException(e, reason2);
}
}
}
I would like to know if there's any framework that would allow me to
capture (and probably log) all the
parameters passed to my wrapper
methods at run-time; if the methods
are called.
capture the actual exception
object(MyException instance in above
example), if any thrown; so that I
could append the passed parameters
to the object at run-time.
I am currently exploring AspectJ to see if it can address my requirements, but I am not sure if it can be used to capture the parameters passed to methods at runtime and also to capture exception objects, if any occur.
Thanks.
With AspectJ, you can use around advice to execute advice instead of the code at the join point. You can then execute the actual join-point from within the advice by calling proceed. This would allow you to capture the input parameters, log them, and proceed to call the actual method.
Within the same advice you could capture any logs throw from the method, and inspect or log them before passing it back up to higher levels.
Matt B's answer is right. Specifically, you can do something like this:
aspect MonitorServiceCalls {
private final Logger LOG = LoggerFactory.getLog("ServiceCallLog");
Object around() throws MyException: call(public * *(..) throws MyException)
&& target(IServiceA+) {
MethodSignature msig = (MethodSignature)thisJoinPoint;
String fullMethName = msig.getMethod().toString();
try {
Object result = proceed();
LOG.info("Successful call to {} with arguments {}",
fullMethName,
thisJoinPoint.getArgs());
return result;
} catch(MyException e) {
LOG.warn("MyException thrown from {}: {}", msig.getMethod(), e);
throw e;
}
}
}
AspectJ is the right option. You will be able to get hold of the parameters by way of a JoinPoint object that will be passed to your advise methods. You can also get hold of the exception either by implementing an after throwing advise or an around advise.

Categories

Resources