12

The setup is following - I have a timed task that would send verification emails so users:

@Scheduled(cron = " 0 0-59/1 * * * * ")
public void sendVerificationEmails() {
    //...
}

And in those emails I need to include a link leading back to the same webapp. However I can not find any references of how to get app's base url without servlet context.

BONUS

It would also help if I could set up thymeleaf template resolver here to work with those links, but for that I need a WebContext which requires an instance of HttpServletRequest.

Ben
  • 3,989
  • 9
  • 48
  • 84
  • 1
    what about properties file. BTW why exactly you dont want to use servlet context? – kuhajeyan Nov 03 '16 at 12:27
  • http://stackoverflow.com/questions/20405474/spring-boot-context-root – Vasu Nov 03 '16 at 12:27
  • @kuhajeyan because it's not a web request, a different scope. What about properties file? – Ben Nov 03 '16 at 12:29
  • See my answer here, https://stackoverflow.com/a/53896961/3307037. It's neat and frees you from all the concatenating , which is not portable and error prone – Enoobong Dec 22 '18 at 15:32

2 Answers2

8

Suppose your app is using embedded tomcat server, then url to your app may be found as follows:

@Inject
private EmbeddedWebApplicationContext appContext;

public String getBaseUrl() throws UnknownHostException {
    Connector connector = ((TomcatEmbeddedServletContainer) appContext.getEmbeddedServletContainer()).getTomcat().getConnector();
    String scheme = connector.getScheme();
    String ip = InetAddress.getLocalHost().getHostAddress();
    int port = connector.getPort();
    String contextPath = appContext.getServletContext().getContextPath();
    return scheme + "://" + ip + ":" + port + contextPath;
}

Here is an example for embedded jetty server:

public String getBaseUrl() throws UnknownHostException {
    ServerConnector connector = (ServerConnector) ((JettyEmbeddedServletContainer) appContext.getEmbeddedServletContainer()).getServer().getConnectors()[0];
    String scheme = connector.getDefaultProtocol().toLowerCase().contains("ssl") ? "https" : "http";
    String ip = InetAddress.getLocalHost().getHostAddress();
    int port = connector.getLocalPort();

    String contextPath = appContext.getServletContext().getContextPath();
    return scheme + "://" + ip + ":" + port + contextPath;
}
yglodt
  • 13,807
  • 14
  • 91
  • 127
eparvan
  • 1,639
  • 1
  • 15
  • 26
  • I'm not asking how to send emails or use thymeleaf templates, that's not relevant to the question and in fact, if you'd read the question carefully, you would realize that I already use thymeleaf templates and send emails. The real question is if the app is aware of it's base url, and how to get it - and your solution is to provide it myself, which does not help at all (and is a crude practice from deployment side). Besides @{} links in thymeleaf wont work if you dont make the the context HTTP-aware with `WebContext` – Ben Nov 04 '16 at 16:01
  • You're right, I completely misunderstood your question, perhaps my edited answer you'll find more useful. – eparvan Nov 04 '16 at 19:58
  • Will this work when deployed to a different web servlet container? – Ben Nov 06 '16 at 11:58
  • I've tested only with tomcat and jetty, see my edited answer. – eparvan Nov 06 '16 at 14:08
  • It's messy because of deployment-dependency. Nevertheless I think this is a correct answer. – Ben Nov 06 '16 at 14:17
6

In my setup I have a @Configuration class setting up the context path (hardcoded) and @Value injecting the port number from application.properties. The context path could also be extracted to a properties file and injected the same way, you would use:

@Value("${server.port}")
private String serverPort;
@Value("${server.contextPath}")
private String contextPath;

You can also implement ServletContextAware in your component in order to get a hook to the ServletContext which would also give you the context path.

I am guessing you would like to ship a complete url (including the full server name) in your emails, but you can't really be sure that the e-mail receivers are accessing your application server directly by hostname, i.e. it could be behind a web server, a proxy etc. You could of course add a server name which you know can be accessed from outside as a property as well.

Kristoffer
  • 251
  • 2
  • 10
  • 3
    In new versions of SpringBoot, it's `@Value("${server.context-path}")`, but's it's a good solution, thx. – Karl.S Feb 15 '18 at 23:15