Spring AOP doesn`t work with class comprising #Transactional method - java

I develop web-app, with a need to store heavy-weight files and use Apache FTP Server for this purpose. When a new user register his account, the folder named as his username must be created on remote server. To establish connection, before UserCreatingServiceImpl.createUser() method will be performed, I use Spring AOP:
#Component
#Aspect
public class RemoteServerConnectionEstablisher {
private static boolean connectionEstablished = false;
#Autowired
private RemoteServerConnector serverConnector;
#Pointcut("execution(* com.storehouse.business.services.impl.UserCreatingServiceImpl.createUser(..)) ||"
+ " execution (* com.storehouse.business.services.impl.ItemCreatingServiceImpl.createItem(..)) ||"
+ "execution (* com.storehouse.business.services.impl.FileDownloadingServiceImpl.downloadFile(..))")
public void pointcut() {
}
#Before("pointcut()")
public void establishConnection(JoinPoint jp) {
if (!connectionEstablished) {
if (serverConnector.connectToRemoteServer()) {
connectionEstablished = true;
}
}
}
#After("pointcut()")
public void disconnect(JoinPoint jp) {
if (connectionEstablished) {
if (serverConnector.disconnect()) {
connectionEstablished = false;
}
}
}
}
Here is the service class with createUser() method:
#Service
public class UserCreatingServiceImpl implements UserCreatingService {
#Autowired
private UserService userService;
#Autowired
private FTPClient ftpClient;
#Override
public boolean createUser(UserDto userDto) {
try {
ftpClient.makeDirectory(userDto.getUsername());
UserMapper userMapper = new UserMapper();
userService.persistUser(userMapper.dtoToEntity(userDto));
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
#Transactional
public void checkIfUsernameExist(String username) {
}
}
Everything had worked fine, until I added #Transactional method to service -class:
#Transactional
public void checkIfUsernameExist(String username) {
}
Now methods of Aspect-class don`t invoke. Could you explain the reason. Thanks in advance for help.

The issue lies in your pointcut expression.
execution(* com.storehouse.business.services.impl.UserCreatingServiceImpl.createUser(..))
You are intercepting the execution of the createUser method on the UserCreatingServiceImpl. This works when you don't add something that creates a proxy for your implementation. As you will be directly calling this method.
However as you have added #Transactional a proxy is created and the method call is now done on the UserCreatingService as that is the interface that is left due do the created proxy. By default spring uses JDK Dynamic proxies, which are interface based.
To solve do one of these things
Rewrite your pointcut to operate on the interface instead of implementing class
Use class based instead of interface based proxies
Use compile or load time weaving
Rewrite pointcut
Use execution(* com.storehouse.business.services.UserCreatingService+.createUser(..)) instead of what you have now. This will use the interface instead of the concrete class.
Use class based proxies
Assuming you use #EnableAspectJAutoProxy add proxyTargetClass=true to it, leading to #EnableAspectJAutoProxy(proxyTargetClass=true). This will create class based proxies and should make the original pointcut work.
Use compile or load time weaving
Instead of using proxies you also could change the way your code is build/loaded. For compile time weaving you would have to modify your build to use the AspectJ compiler to apply the aspects at compilation time, then you don't need the proxies any more.
Or instead of #EnableAspectJAutoProxy you could add #EnableLoadTimeWeaving which, if you use a recent servlet container, would weave the aspects as soon as a class is being loaded.
Both would eliminate the need for proxies (at least for this part) and would make the original pointcuts work.

Related

Dagger 2 runtime parameter in a chain of dependencies

Decided to give Dagger 2 a try while reorganizing a certain project, however there are still some things not entirely clear to me. What would be the best approach to supporting a chain of dependencies that require a runtime parameter at it's root?
A quick simplified example: let's say there's a base class called "SpecificDatabaseService", which requires a runtime parameter like "login". Then there's a "SampleDao" class, that requires a DatabaseService [amongst some other dependencies]. Finally 2 classes named "FirstProcessor" and "SecondProcessor" would both require a SampleDao, like so:
public class SpecificDatabaseService implements DatabaseService {
public SpecificDatabaseService(String login) { // setup }
}
public class SampleDao {
public SampleDao(DatabaseService service) { // setup }
}
public class FirstProcessor {
public FirstProcessor(SampleDao dao) { // setup }
}
public class SecondProcessor {
public SecondProcessor(SampleDao dao) { // setup }
}
Next thing I'm creating SpecificDatabaseServiceModule and a DatabaseServiceComponent in order to do some additional stuff to DatabaseService later.
#Module
interface SpecificDatabaseServiceModule {
#Binds
DatabaseService bindDatabaseService(SpecificDatabaseService databaseService);
}
#Singleton
#Component(modules = { SpecificDatabaseServiceModule.class })
interface DatabaseServiceComponent {
DatabaseService getDatabaseService();
#Component.Factory
interface Factory {
DatabaseServiceComponent create(#BindsInstance #Named("login") String login);
}
}
Now when I need to create a FirstProcessor, someone has to provide a "login" for FirstProcessorComponent once again (same as with SecondProcessor). And if there's a class, which uses a FirstProcessor as a dependency, then again it needs a "login" to be provided. As the chain grows, it becomes more and more difficult to use Dagger.
Is there an easier way or approach of passing the "login" value once and just sharing module/component. For instance FirstProcessorComponent depends on DatabaseServiceComponent and if Dagger know how to construct DatabaseService, it's able to construct FirstProcessor as well. Hasn't been unable to get it working up till now.

Java: calling public transactional method from a private method

I have two classes
public class MyTest {
#Autowired
private MyService myService;
private void test() {
myService.writeToDb();
}
}
#Service
public class MyService {
#Transactional
public void writeToDb() {
// do db related stuff
}
}
I want to know if calling a method test() (which is a private method) from MyTest class would create a transaction.
P.S
I'm using Spring Boot. And Java 17.
It will work, whether you call the method of another object from a public or private method inside yours is an implementation detail. From callee's point of view, it's the same, it is not even aware of the caller's context.
Spring AOP uses the Proxy pattern to handle those scenarios. It means you are not directly receiving a MyService bean, but a MyServiceSpringCreatedProxy (not the actual name, check in debug mode and you'll see), which is actually handling transactions around methods.
So as long as the call passes through the Spring's proxy, the #Transactional will be accounted for as expected. Bear in mind that it doesn't mean a new transaction is open, it depends if another already exists and your configuration.
However, any self call (to a public or a private method) would not pass through the proxy and then #Transactional would not be working.
#Service
public class MyService {
// can be private, public or whatever
public void callRelatedStuff() {
//self call, no transactional work done
writeToDb();
}
#Transactional
public void writeToDb() {
// do db related stuff
}
}

Aspect method of a parent class of a RestController does not trigger advice logic

I have some construct I'd really love to use an aspect on. I want to let my RestController inherit from a class which yields special logging methods which
log to the standard logback output
fires a http request to a service which does stuff with the log
message as well (done by the aspect)
I created an annotation with which I mark the method I want to aspect so the pointcut cant filter it. Special case is that this method is declared within the parent class of the RestController.
The aspect is not running even tho IntelliJ is marking the method as being used by the aspect, which tells me the pointcut has to be working?
Please see my code and check what I've might missed out to get it to work.
ApplicationClass
#SpringBootApplication
#ComponentScan("com.xetra.experimental")
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopTryoutApplication {
public static void main(String[] args) {
SpringApplication.run(AopTryoutApplication.class, args);
}
}
RestController
#RestController
public class Endpoint extends SimpleLogger {
#GetMapping("/endpoint")
public void doStuff(){
log("foo");
}
}
Parent class for RestController
public class SimpleLogger implements EndpointLogger{
#AspectAnnotation
public void log(String msg) {
System.out.println(msg);
}
}
Interface for parent class (heard that aspected methods need interfaces)
public interface EndpointLogger {
void log(String msg);
}
Annotation my aspect should pointcut to
#Inherited
public #interface AspectAnnotation {
}
Spring AOP aspect
#Component
#Aspect
public class TestAspect {
#Pointcut("#annotation(com.xetra.experimental.aoptryout.AspectAnnotation)")
public void methods() {
}
#Before("methods()")
public void beforeMethodExecution(JoinPoint jp) {
System.out.println("Aspect ran!!!!");
}
}
Due to the proxy-based nature of Spring’s AOP framework, calls within the target object are by definition not intercepted.
You can find more here.
The call to log method is not intercepted since it's made from the doStuff method belonging to the same target object.
Now, any call to log method will be intercepted as long it's made externally from another object (not the same target object).
Questions
So if I use SimpleLogger as a component and not a parent class within the Endpoint it will work aye?
Yes, you are right!
Is there any way to get this to work anyway? Like using AspectJ and not Spring AOP?
You can use AspectJ's source weaving to make it work. Here, is a working example.

When and how to instantiate a Spring Bean in my Rest Api

First of all, I'm a relative noob to Spring Boot, so keep that in mind.
I've got a REST api in which I'm trying to minimize database calls for the same object and I've determined that using a Spring Bean scoped to the Request is what I want. Assuming that is correct, here is what I'm trying to do:
1) Controller takes in a validated PhotoImportCommandDto command
PhotoCommandController
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> importPhoto(#Valid #RequestBody PhotoImportCommandDto command){
...
}
2) PhotoImportCommandDto is validated. Note the custom #UserExistsConstraint which validates that the user exists in the database by calling a service method.
PhotoImportCommandDto
#Component
public class PhotoImportCommandDto extends BaseCommand {
#NotNull(message = "userId must not be null!")
#UserExistsConstraint
private Long userId;
...
}
What I would like to do is somehow set a Spring Bean of the user that is validated in the #UserExistsConstraint and reference it in various methods that might be called throughout a Http request, but I'm not really sure how to do that. Since I've never really created my own Spring Beans, I don't know how to proceed. I've read various guides like this, but am still lost in how to implement it in my code.
Any help/examples would be much appreciated.
You can use the #Bean annotation.
#Configuration
public class MyConfiguration {
#Bean({"validUser"})
public User validUser() {
User user;
//instantiate user either from DB or anywhere else
return user;
}
then you can obtain the validUser.
#Component
public class PhotoImportCommandDto extends BaseCommand {
#Autowired
#Qualifier("validUser")
private User validUser;
...
}
I don't really know how to make annotations in Java. Anyway, in Spring, checking where the User exists in the DataBase or not is one line of code:
userRepository.findOne(user) == null
That is accomplished by the Spring Data JPA project:
Create a JPA Entity User.
Set the spring.datasource.url and login/password in the
resources/application.properties.
Create this interface:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
Note, Spring implements it behind the scences.
Inject this interface into your RestController (or any other Spring bean):
private UserRepository userRepository ;
**constructor**(UserRepository ur){
userRepository = ur;
}
Note, a Spring Bean is any class annotated #Component (this includes stereotype annotations like Controller, Repository - just look up the contents of an annotation, it may use #Component internally) or returned from a method which is annotated #Bean (can only be on the Component or Configuration class). A Component is injected by searching the classpath, Bean is injected more naturally.
Also note, injecting is specifying #Autowired annotation on field or constructor, on a factory method, or on a setter. The documentation recommends that you inject required dependencies into constructor and non-required into the setter.
Also note, if you're injecting into a constructor and it is clean by the arguments, you may omit #Autowired annotation, Spring will figure it out.
Call its method findOne.
So, you can do one of the following:
Inject the userRepository into the #RestController constructor (as shown above). I would do that.
Inject the userRepository into the #Service (internally #Component) class that will do this sorts of thing for you. Maybe you can play with it to create an annotation.
p.s. Use #PostMapping instead of #RequestMapping(method = RequestMethod.POST)
p.p.s. If ever in doubt, go to the official documentation page and just press CTRL-F: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ Note the current word, that will always take you to the latest version.
p.p.p.s Each Spring project has its own .io webpage as well as quick Get Started Guides where you can quickly see the sample project with explanations expecting you to know nothing.
Hope that helps! :)
Don't forget to mark the answer as accepted if you wish
Using Jose's input, I took a bit of a different route.
Here's what I did:
I created a ValidatedUser class:
#RequestScope
#Component
public class ValidatedUser {
private UserEntity user;
public UserEntity getUser() {
return user;
}
public void setUser(UserEntity user) {
this.user = user;
}
}
and I also created a wrapper class HttpRequestScopeConfig to capture all variables to use over the course of an Http Request to the api.
#Component
public class HttpRequestScopeConfig {
#Autowired
private ValidatedUser validatedUser;
...
public UserEntity getValidatedUser() {
return validatedUser.getUser();
}
public void setValidatedUser(UserEntity validatedUser) {
this.validatedUser.setUser(validatedUser);
}
...
}
In my UserExistsConstraintValidator (which is the impl of #UserExistsConstraint, I set the validatedUser in the httpRequestScopeConfig:
public class UserExistsConstraintValidator implements ConstraintValidator<UserExistsConstraint, Long> {
//private Log log = LogFactory.getLog(EmailExistsConstraintValidator.class);
#Autowired
private UserCommandService svc;
#Autowired
private HttpRequestScopeConfig httpRequestScope;
#Override
public void initialize(UserExistsConstraint userId) {
}
#Override
public boolean isValid(Long userIdField, ConstraintValidatorContext context) {
try {
UserEntity user = svc.findUserOfAnyStatus((Long) userIdField);
if (user != null) {
httpRequestScope.setValidatedUser(user);
return true;
}
} catch (Exception e) {
//log.error(e);
}
return false;
}
}
Now, I can access these variables throughout the rest of my service layers by autowiring HttpRequestScopeConfig where necessary.

Hibernate Validator: Intercept Invalid Values

I'd like to set up my beans to use both Hibernate Validator (for validation) and Google Guice (for DI and method interception).
Ideally, I'd like to have a setup where any method that "fails" validation will cause a method interceptor to be called:
public class Widget {
#NotNull
public Fizz getFizz() {
return fizz;
}
}
public class FailedWidgetInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
// This gets executed if Widget's getFizz() returns null...
}
}
But it looks like Hibernate Validator only allows you to determine pass/fail status by explicitly passing an object T to a ClassValidator<T>'s getInvalidValues() method.
So I need a place to make such a call! The only viable solution I can think of is to create my own annotation (which I've never done before!) which might look like this:
#NotNull
public #interface AutoValidatingNotNull {
// ...??
}
And then in Guice Module:
public class WidgetModule implements Module {
public void configure(Binder binder) {
binder.bindInterceptor(
any(),
annotatedWith(AutoValidatingNotNull.class),
new ValidatingWidgetInterceptor()
);
}
}
public class ValidatingWidgetInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
ClassValidator<Widget> widgetValidator = new ClassValidator<Widget>();
InvalidValue[] badVals = widgetValidator.getInvalidValues(widget);
if(badVals.length > 0)
handleFailedValidationAndThrowRuntimeExceptionOrSomething();
}
}
Finally, to change getFizz():
#AutoValidatingNotNull
public Fizz getFizz() {
return fizz;
}
For one, this only almost works: inside the interceptor's invoke method, how do I get my hands on the widget instance (the one we wish to validate)?. Is there a way to pass the widget instance via annotations?
Edit:
Doesn't look like I can pass Object into annotations (as parameters)...
Second, this is kind of nasty. Perhaps I'm overlooking something that Hibernate Validator offers that takes care of all this for me? Is there a better way to go? Thanks in advance!
It seems like you're still using the Hibernate Validator 3.x API around ClassValidator et al.
I recommend to upgrade to 4.2 where an API for method validation was introduced which exactly does what you describe.
An example for the required glue code to integrate that API with Google Guice can be found in this project which I created a while ago on GitHub.

Categories

Resources