I have Spring Boot web application. It exposes REST API on port 8080. It also exposes management port 8081 with Spring Boot Management endpoints (http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-monitoring.html).
I do not have any custom Tomcat configuration to achieve that. I just have property management.port=8081
in my application.properties
file.
I have JavaMelody configured as described in
https://github.com/javamelody/javamelody/wiki/UserGuideAdvanced#spring-boot-app
(I have my custom JavaMelodyConfiguration
class, with org.springframework.boot.web.servlet.FilterRegistrationBean
that registers net.bull.javamelody.MonitoringFilter
).
@Bean
public FilterRegistrationBean javaMelody() {
final FilterRegistrationBean javaMelody = new FilterRegistrationBean();
javaMelody.setFilter(new MonitoringFilter());
javaMelody.setAsyncSupported(true);
javaMelody.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
javaMelody.addUrlPatterns("/*");
return javaMelody;
}
With this configuration, Javamelody is exposed on port 8080 (business port). I would like to move it to 8081(management port). How to change that?
I use Spring Boot 1.4.2.RELEASE, javamelody 1.62.0
EmbeddedTomcatConfiguration.java
package ...
import java.util.ArrayList;
import java.util.List;
import org.apache.catalina.connector.Connector;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class EmbeddedTomcatConfiguration {
@Value("${server.additionalPorts}")
private String additionalPorts;
@Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
Connector[] additionalConnectors = this.additionalConnector();
if (additionalConnectors != null && additionalConnectors.length > 0) {
tomcat.addAdditionalTomcatConnectors(additionalConnectors);
}
return tomcat;
}
private Connector[] additionalConnector() {
if (StringUtils.isBlank(this.additionalPorts)) {
return null;
}
String[] ports = this.additionalPorts.split(",");
List<Connector> result = new ArrayList<>();
for (String port : ports) {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(Integer.valueOf(port));
result.add(connector);
}
return result.toArray(new Connector[] {});
}
}
application.yml
server:
port: ${appPort:8800}
additionalPorts: 8880,8881
Application.java
@SpringBootApplication
@ComponentScan(...)
@Import(EmbeddedTomcatConfiguration.class)
public Application {
public static void main(String[] args) {
SpringApplication.run(Application .class, args);
}
}
and my suggestion for limiting accessing javamelody from a specific port would be to extend the javamelody filter and just chain the request if it comes from a specific port otherwise send back a 404.
From the logs:
INFO TomcatEmbeddedServletContainer:185 - Tomcat started on port(s): 8800 (http) 8880 (http) 8881 (http)
This approach BTW exposes other endpoints on these ports.
To solve this and limiting javamelody filter (/monitoring) to a specific port, you would need to write a filter that verifies path (servlet and filter path) being requested from allowable ports keeping in mind that the ordering of these filters is important.
Based on this answer and partial source code that I had already available when I answered this question, I had published a blog post about this topic at http://tech.asimio.net/2016/12/15/Configuring-Tomcat-to-Listen-on-Multiple-ports-using-Spring-Boot.html
You can use the ReportServlet through a MvcEndpoint. Something like this:
import net.bull.javamelody.MonitoringFilter;
import net.bull.javamelody.ReportServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* We configure the Java Melody {@link MonitoringFilter} normally, but disables all access to the UI. Instead,
* we create a {@link ReportServlet}, and expose it through a {@link MvcEndpoint} in {@link #javaMelodyReportEndpoint()}.
*/
@Configuration
public class JavaMelodyConfiguration {
private final ServletConfig servletConfig;
@Autowired
public JavaMelodyConfiguration(ServletConfig servletConfig) {
this.servletConfig = servletConfig;
}
@Bean
MvcEndpoint javaMelodyReportEndpoint() {
ReportServlet reportServlet = new ReportServlet();
// We initialize the servlet with the servlet configuration from the server that runs on server.port, as
// it currently only uses it to access the Collector instance, and some system information.
reportServlet.init(servletConfig);
return new MvcEndpoint() {
@Override
public String getPath() {
return "/monitoring";
}
@Override
public boolean isSensitive() {
return false;
}
@Override
public Class<? extends Endpoint> getEndpointType() {
return null;
}
@GetMapping
public void report(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException, IOException {
reportServlet.service(httpRequest, httpResponse);
}
};
}
@Bean
FilterRegistrationBean javaMelodyFilterRegistration() {
FilterRegistrationBean javaMelody = new FilterRegistrationBean();
javaMelody.setFilter(monitoringFilter());
javaMelody.setName("javamelody");
return javaMelody;
}
@Bean
MonitoringFilter monitoringFilter() {
return new MonitoringFilter() {
@Override
protected boolean isAllowed(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException {
// We allow no access to the report (/monitoring) from this filter, access is done through the
// MvcEndpoint above, using the management port.
return false;
}
};
}
}
(I also posted this here: https://github.com/javamelody/javamelody/issues/601)