Invoke request to localhost immediately after serv

2019-08-02 12:06发布

问题:

I'm looking to have my java web application call itself at a localhost URL immediately after the servlet container (be it tomcat, jetty, ...) begins accepting requests.

I'm using the java spring framework but I believe that's a "side issue" since it's really the servlet container's status that I need to be aware of.

As I understand it, spring initializes the application context with all its beans first, then maps the URLs and initializes the DispatcherServlet to handle the handling/filtering of requests.

I'm looking to find the "moment" when I can safely use a RestTemplate to call the server itself. Everything I've tried seems to be "too early" as it has resulted in java.net.ConnectException: Connection refused--except for when I invoke it manually from a web browser via a controller endpoint--which succeeds.

I've tried using:

  1. javax.servlet.ServletContextListener as per How to invoke a method on a servlet or controller after the web container has successfully started
  2. org.springframework.context.ApplicationListener<ContextRefreshedEvent>
  3. a hacky custom servlet's init method with a "load-on-startup" after the DispatcherServlet

At some point in time, the servlet container must have to take over from spring's setup and "flip the switch" to "on". Also, I'd like to do this in a servlet container "agnostic" way so that I don't have specific tomcat/jetty code.

Here's the resttemplate exception. I'm running the app on port 9090 and my contextPath is 'openid-connect-provider'. 'foo' is a very simple GET endpoint that works, as I mentioned, when invoked after the servlet container is started.

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:9090/openid-connect-provider/foo": Connection refused; nested exception is java.net.ConnectException: Connection refused
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:582)
...

The only other thing I can say is that these errors occur in my logs/console just before Jetty tells me that it's started:

[INFO] Started ServerConnector@1bcba9c7{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
[INFO] Started @13749ms
[INFO] Started Jetty Server

[update] A bit more background. I'm implementing an OAuth2 Authorization Server (AS). My user credentials are in a separate database that I need to access through a separate service (which is an oauth2 resource server or RS). I need my AS to call the RS to authenticate users but I want to protect my RS with tokens granted by my AS. So, i need to setup my AS as a oauth2 client of itself so that it can call the RS securely. To do that, I want to dynamically register the application (with itself) to get client_id/client_secret credentials generated so that it can call my RS. Ideally, I'd want my user info to be in the same service and not to have to do this but this is a 6-month interim step.

回答1:

Can you try this little trick at the end of your ServletContextListener#contextStarted event

new Thread(() -> {
        try {
            Thread.currentThread().sleep(500);
            // Your client call.
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();

You can adjust the sleep time.



回答2:

This is what I've decided to do (for now). Wait for the first request in order to guarantee that the servlet container is accepting requests. Then do the call once and disable it from doing it again until next startup.

public class PostStartInitilizationListener implements HttpSessionListener {

  private static volatile boolean isInitialized;

  @Override
  public void sessionCreated(HttpSessionEvent event) {
    if (!isInitialized) { // because we don't want to do this on every session creation
      synchronized(this) { // prevent other threads from making the request as well
        isInitialized = true; // to immediately prevent any other sessionCreated events from making the request
        // the request
        ResponseEntity<String> entity = new RestTemplate().getForEntity("http://localhost:9090/openid-connect-provider/foo", String.class);
      }
    }
  }
}