12

Spring Boot's Actuator library with production information endpoints is really useful for any server application. But the problem is I could not find a way to integrate into a traditional Spring Application (which is not a Spring BOOT application).

There must be some way to use the endpoints of actuator but I could not wire them up.

I have a JavaConfig class like below

@Configuration
@ComponentScan(basePackages = { "com.company.helper", "org.springframework.boot" })
@EnableWebMvc
@Import({ DbConfig.class })

public class AppConfig extends WebMvcConfigurerAdapter {

}

But this configuration throws an error during deployment.

Can this wiring be done without the Spring Boot application?

LostMohican
  • 3,082
  • 7
  • 29
  • 38
  • Well with copying part of the configuration and beans of Spring Boot you should be able to. But why not simply add Spring Boot to your application? You don't loose anything you only gain imho. – M. Deinum Nov 13 '14 at 18:57

3 Answers3

12

I have added information on how to add spring boot actuator in a non boot application in this blog post

http://givenwhenthen.blogspot.com/2015/09/adding-spring-boot-actuator-to-non.html

In the application's build.gradle, I added the following dependency

compile('org.springframework.boot:spring-boot-actuator:1.2.5.RELEASE'){
    exclude group: 'org.springframework.boot', module:'spring-boot-starter-logging'}

In the application's Spring Config class, I added the following things:

 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;  
 import org.springframework.boot.actuate.endpoint.BeansEndpoint;  
 import org.springframework.boot.actuate.endpoint.HealthEndpoint;  
 import org.springframework.boot.actuate.endpoint.InfoEndpoint;  
 import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;  
 import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;  
 import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;  
 import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;  
 import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;  

 @Configuration  
 @Import(EndpointAutoConfiguration.class)  
 public class MyAppSpringConfig {  

   @Bean  
   @Autowired  
   //Define the HandlerMapping similar to RequestHandlerMapping to expose the endpoint  
   public EndpointHandlerMapping endpointHandlerMapping(  
     Collection<? extends MvcEndpoint> endpoints  
   ){  
     return new EndpointHandlerMapping(endpoints);  
   }  

   @Bean  
   @Autowired  
   //define the HealthPoint endpoint  
   public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate){  
     return new HealthMvcEndpoint(delegate, false);  
   }  

   @Bean  
   @Autowired  
   //define the Info endpoint  
   public EndpointMvcAdapter infoMvcEndPoint(InfoEndpoint delegate){  
      return new EndpointMvcAdapter(delegate);  
   }  

   @Bean  
   @Autowired  
   //define the beans endpoint  
   public EndpointMvcAdapter beansEndPoint(BeansEndpoint delegate){  
     return new EndpointMvcAdapter(delegate);  
   }  

   @Bean  
   @Autowired  
   //define the mappings endpoint  
   public EndpointMvcAdapter requestMappingEndPoint(  
     RequestMappingEndpoint delegate  
   ){  
     return new EndpointMvcAdapter(delegate);  
  }  
}  

If you want to get rid of one additional dependency then please refer to the blogpost.

UPDATE

Also you need to make sure you have a bean defined for RequestMappingHandlerAdapter, if you do not have it the ServletDispatcher will not be able to fetch the adapter for the handler of your HealthMvcEndpoint.

if you dont have it just add it to your bean configuration file

xml configurations:

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <ref bean="jsonConverter"/>
            </list>
        </property>
    </bean>

    <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="supportedMediaTypes" value="application/json" />
        <property name="prettyPrint" value="true" />
    </bean>
Amer Qarabsa
  • 6,412
  • 3
  • 20
  • 43
Prateek Negi
  • 260
  • 3
  • 11
  • When I hit the health endpoint, I always get a status of UNKNOWN do you get valid health status information from this configuration? – mad_fox Dec 17 '15 at 21:40
  • Yes, We do get health status. You would have to add @ComponentScan("my.domain.package.of.health.indicators") for Spring to refer to your health indicators – Prateek Negi Jan 18 '16 at 13:02
  • If I use this, will all of the actuators be enabled by default? Or should it be configured in the application.properties? Also will the port default to 8080? – Loren Nov 10 '16 at 06:33
5

The project I'm working on uses Spring, but neither Spring-boot nor Spring-MVC. The following solution may not be as automagic as the actuator with boot, but it exposes the endpoints in a pretty succinct way.

Basically, all actuator endpoints are just beans, so you can create a new component and autowire in the endpoints however you see fit.

The only additional dependencies in my pom are spring-boot-actuator and spring-webmvc:

   <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-actuator</artifactId>
        <version>1.2.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId> 
        <version>4.1.4.RELEASE</version>
    </dependency>

Then all you need to do is create a single component class (maybe register it if you need to). Make sure to annotate with @EnableAutoConfiguration:

@Component
@EnableAutoConfiguration
@Path("/actuator/")
public class ActuatorResource {

private ObjectMapper mapper = new ObjectMapper();


@Autowired 
private DumpEndpoint dumpEndpoint;

@GET
@Produces("application/json")
@Path("/dump")
@Transactional(readOnly = true)
public String getDump() throws JsonProcessingException { 
    return mapper.writeValueAsString(dumpEndpoint.invoke());
}

@Autowired
private EnvironmentEndpoint envEndpoint;

@GET
@Produces("application/json")
@Path("/environment")
@Transactional(readOnly = true)
public String getEnvironment() throws JsonProcessingException {
    return mapper.writeValueAsString(envEndpoint.invoke());
}

}
dneronique
  • 51
  • 1
  • 2
  • please see this post: https://stackoverflow.com/questions/32383875/does-having-spring-boot-actuator-changes-the-context-root-of-the-spring-applicat – brain storm Sep 03 '15 at 19:24
2

In our project we used a little hack, that worked for us. To enable actuator we used dependencies from spring-boot in POM.

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-actuator</artifactId>
        <version>1.2.3.RELEASE</version>
        <type>jar</type>
    </dependency>
     <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>4.3.2.Final</version>
    </dependency>

and just used additional config class as follows:

@EnableConfigurationProperties
@Configuration
@EnableAutoConfiguration
@Import(EndpointAutoConfiguration.class)
public class SpringBootActuatorConfig {

}
RadekSohlich
  • 182
  • 1
  • 3
  • 11