Please see the updates below.
I have a Spring Boot application where I accept TCP/IP connections:
public MyClass implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
try (ServerSocket serverSocket = new ServerSocket(port)) {
while (true) {
Socket socket = serverSocket.accept();
new ServerThread(socket).start();
}
}
}
...
private class ServerThread extends Thread {
@Override
public void run() {
try (InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream()) {
// Read line from input and call a method from service:
service.myMethod(lineConvertedToMyObject);
} catch {
...
}
}
}
}
Now this works fine, as it is. But when I introduce AspectJ to myMethod
:
@Aspect
@Component
public class MyServiceAspect {
private static final Logger logger = LoggerFactory.getLogger(MyServiceAspect.class);
@Around(value = "execution(* com.package.to.MyService.myMethod(..))")
public MyObject rules(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
MyObject obj = (MyObject) joinPoint.proceed();
logger.debug("Took {} milliseconds", System.currentTimeMillis() - startTime);
return obj;
}
}
service.myMethod
is not called and the thread is blocked. What am I missing?
Update:
So here's the deal: MyService
, MyServiceImpl
and MyServiceAspect
are all in the same package. Moving MyServiceAspect
into another package made it work.
Does this ring a bell for anyone? Happy to award the bounty to anyone explaining this behavior. Thanks!
Update 2:
Yet another solution: Adding @DependsOn(value = {"myServiceAspect"})
on top of MyServiceImpl
again resolves the issue, still wondering why though.
As it was described by Alexander Paderin >> in his answer to the related question >> infinite loop in the afterPropertiesSet()
was the thread blocker, since control wasn't return back to Spring in this case.
Code samples you've provided do not contain issues directly, AspectJ declaration is fine.
First of all, please let me share working example: spring-aspectj-sockets. It is based on Spring 5.1.0 and AspectJ 1.9.1 (currently latest versions) and uses your samples, works independent of the location/package of MyServiceAspect
.
The most possible thread blocker in your samples is a call to ServerSocket.accept()
, javadocs for this method says:
Listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made.
There are 2 correct ways of handling accept()
:
To initialize connection first, e.g.:
serverSocket = new ServerSocket(18080);
clientSocket = new Socket("127.0.0.1", 18080); // initializing connection
Socket socket = serverSocket.accept(); // then calling accept()
Set up timeout to wait for acceptance:
serverSocket = new ServerSocket(18080);
serverSocket.setSoTimeout(5000); // 5 seconds timeout
Socket socket = serverSocket.accept(); // then calling accept()
NOTE: If within 5 seconds there will be no connections, accept()
will throw exception, but will not block the thread
I assume that you are using 1-st approach and somewhere you have a line which initializes the connection, i.e. clientSocket = new Socket("127.0.0.1", 18080);
.
But it is called (e.g. if static declarations are used):
serverSocket.accept()
in case MyServiceAspect
is located in the same package andMyServiceAspect
is located in some other placeI'm not sure if this is needed, have doubts because of bounty's description, let me cover this quickly just in case.
You can debug your application using Remote Debugging - it will cover aspects, child threads, services, etc. - you will only need to: