Cloud Foundry Support

Spring Boot’s actuator module includes additional support that is activated when you deploy to a compatible Cloud Foundry instance. The /cloudfoundryapplication path provides an alternative secured route to all @Endpoint beans.

The extended support lets Cloud Foundry management UIs (such as the web application that you can use to view deployed applications) be augmented with Spring Boot actuator information. For example, an application status page can include full health information instead of the typical “running” or “stopped” status.

The /cloudfoundryapplication path is not directly accessible to regular users. To use the endpoint, you must pass a valid UAA token with the request.

Disabling Extended Cloud Foundry Actuator Support

If you want to fully disable the /cloudfoundryapplication endpoints, you can add the following setting to your application.properties file:

  • Properties

  • YAML

management.cloudfoundry.enabled=false
management:
  cloudfoundry:
    enabled: false

Cloud Foundry Self-signed Certificates

By default, the security verification for /cloudfoundryapplication endpoints makes SSL calls to various Cloud Foundry services. If your Cloud Foundry UAA or Cloud Controller services use self-signed certificates, you need to set the following property:

  • Properties

  • YAML

management.cloudfoundry.skip-ssl-validation=true
management:
  cloudfoundry:
    skip-ssl-validation: true

Custom Context Path

If the server’s context-path has been configured to anything other than /, the Cloud Foundry endpoints are not available at the root of the application. For example, if server.servlet.context-path=/app, Cloud Foundry endpoints are available at /app/cloudfoundryapplication/*.

If you expect the Cloud Foundry endpoints to always be available at /cloudfoundryapplication/*, regardless of the server’s context-path, you need to explicitly configure that in your application. The configuration differs, depending on the web server in use. For Tomcat, you can add the following configuration:

  • Java

  • Kotlin

import java.io.IOException;
import java.util.Collections;

import jakarta.servlet.GenericServlet;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletContainerInitializer;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.apache.catalina.Host;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyCloudFoundryConfiguration {

	@Bean
	public TomcatServletWebServerFactory servletWebServerFactory() {
		return new TomcatServletWebServerFactory() {

			@Override
			protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
				super.prepareContext(host, initializers);
				StandardContext child = new StandardContext();
				child.addLifecycleListener(new Tomcat.FixContextListener());
				child.setPath("/cloudfoundryapplication");
				ServletContainerInitializer initializer = getServletContextInitializer(getContextPath());
				child.addServletContainerInitializer(initializer, Collections.emptySet());
				child.setCrossContext(true);
				host.addChild(child);
			}

		};
	}

	private ServletContainerInitializer getServletContextInitializer(String contextPath) {
		return (classes, context) -> {
			Servlet servlet = new GenericServlet() {

				@Override
				public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
					ServletContext context = req.getServletContext().getContext(contextPath);
					context.getRequestDispatcher("/cloudfoundryapplication").forward(req, res);
				}

			};
			context.addServlet("cloudfoundry", servlet).addMapping("/*");
		};
	}

}
import jakarta.servlet.GenericServlet
import jakarta.servlet.Servlet
import jakarta.servlet.ServletContainerInitializer
import jakarta.servlet.ServletContext
import jakarta.servlet.ServletException
import jakarta.servlet.ServletRequest
import jakarta.servlet.ServletResponse
import org.apache.catalina.Host
import org.apache.catalina.core.StandardContext
import org.apache.catalina.startup.Tomcat.FixContextListener
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.servlet.ServletContextInitializer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.io.IOException
import java.util.Collections.emptySet

@Configuration(proxyBeanMethods = false)
class MyCloudFoundryConfiguration {

	@Bean
	fun servletWebServerFactory(): TomcatServletWebServerFactory {
		return object : TomcatServletWebServerFactory() {

			override fun prepareContext(host: Host, initializers: Array<ServletContextInitializer>) {
				super.prepareContext(host, initializers)
				val child = StandardContext()
				child.addLifecycleListener(FixContextListener())
				child.path = "/cloudfoundryapplication"
				val initializer = getServletContextInitializer(contextPath)
				child.addServletContainerInitializer(initializer, emptySet())
				child.crossContext = true
				host.addChild(child)
			}

		}
	}

	private fun getServletContextInitializer(contextPath: String): ServletContainerInitializer {
		return ServletContainerInitializer { classes: Set<Class<*>?>?, context: ServletContext ->
			val servlet: Servlet = object : GenericServlet() {

				@Throws(ServletException::class, IOException::class)
				override fun service(req: ServletRequest, res: ServletResponse) {
					val servletContext = req.servletContext.getContext(contextPath)
					servletContext.getRequestDispatcher("/cloudfoundryapplication").forward(req, res)
				}

			}
			context.addServlet("cloudfoundry", servlet).addMapping("/*")
		}
	}
}