3

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.

Hasan Can Saral
  • 2,950
  • 5
  • 43
  • 78
  • I might be able to explain it if I can see the full picture: Maven build, Sporing configuration, aspect and application code. I need to see real package names and full code, not fake packages and incomplete methods with a lot of "...". Would you mind posting an [MCVE](http://stackoverflow.com/help/mcve) reproducing your problem on GitHub? – kriegaex Sep 29 '18 at 12:33

1 Answers1

2

Actual problem

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.

1. Working example with your samples (not actual after question edit)

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.


2. Issue explanation

2.1. Intro

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():

  1. 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()
    
  2. 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

2.2. Assumption

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):

  • After serverSocket.accept() in case MyServiceAspect is located in the same package and
  • Before - in case MyServiceAspect is located in some other place

3. Debugging

I'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:

  1. Run Java with specific arguments like it is described in this question >>
  2. And connect to the specified debug port using IDE (steps for Eclipse are described in the same question)
marme1ad
  • 1,253
  • 8
  • 8
  • __P.S.__: @hasan-can-saral, in case this will not help, could you please provide some additional information / code samples. – marme1ad Sep 30 '18 at 00:03
  • Thanks for the answer, but I'm looking for a solution to my specific problem, whereas your answer is mostly `ServerThread`? So this is highly irrelevant. I'm trying to replicate the issue and post it to GitHub. Did you see the update? Apart from that there are no issues with my socket implementation. – Hasan Can Saral Oct 01 '18 at 07:07
  • @HasanCanSaral, thank you for the update, but it is still not enough information, it is not clear how you instantiate `serverSocket` on your side from the line `Socket socket = serverSocket.accept();` Let me please wait for the MCVE then. – marme1ad Oct 01 '18 at 08:46
  • As far I've used all your code samples within my example, which works independent of `MyServiceAspect`'s package. – marme1ad Oct 01 '18 at 09:00
  • Updated the question one more time. I am initializing `ServerSocket` in `afterPropertiesSet` method of `InitializingBean`. – Hasan Can Saral Oct 01 '18 at 09:03
  • @HasanCanSaral, so you might/will have a race condition here because of `serverSocket.accept()` - it will block the thread until socket listener will be created. As I wrote earlier please add `serverSocket.setSoTimeout(1000); // 1 seconds timeout` - it will wait for a second to instantiate connection with a socket listener, then will go to the `ServerThread` logic where listener instantiated, then will try to accept once again. – marme1ad Oct 01 '18 at 09:31
  • Also you can simply check if `serverSocket.accept()` is the reason of the blockage without debugging, just by adding two outputs around that line: `System.out.println(1); Socket socket = serverSocket.accept(); System.out.println(2);` – marme1ad Oct 01 '18 at 09:35
  • I have checked, it is not. I think it has more to do with Spring black magic rather than `ServerSocket`. Moving the class into another package makes it work. If it were due to a race condition or a timeout (which I cannot btw, since this is a server and it would exit if there were no connections within 1s) moving packages wouldn't help. – Hasan Can Saral Oct 01 '18 at 09:37
  • Sorry, so both outputs (`1` and `2`) are shown, correct? – marme1ad Oct 01 '18 at 09:40
  • Correct, regardless of the aspect working or not. I honestly think that this has nothing to do with `ServerSocket`. – Hasan Can Saral Oct 01 '18 at 09:57
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/181060/discussion-between-marme1ad-and-hasan-can-saral). – marme1ad Oct 01 '18 at 09:59