Simulant
Simulant

Reputation: 20112

How to deploy multiple Spring boot applications with external configurations on the same Tomcat?

When I want to deploy multiple Spring-Boot applications on the same tomcat I face multiple problems.

1) Both applications have to run independent while sharing the same tomcat. When deploying two Spring-Boot application on the same tomcat I get the following Exception:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-03-06 17:31:01 ERROR o.s.boot.SpringApplication - Application run failed
org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [HikariDataSource (HikariPool-2)] with key 'dataSource'; nested exception is javax.management.InstanceAlreadyExistsException: com.zaxxer.hikari:name=dataSource,type=HikariDataSource
    at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:625)
    at org.springframework.jmx.export.MBeanExporter.lambda$registerBeans$2(MBeanExporter.java:551)
    at java.util.HashMap.forEach(HashMap.java:1289)
    at org.springframework.jmx.export.MBeanExporter.registerBeans(MBeanExporter.java:551)
    at org.springframework.jmx.export.MBeanExporter.afterSingletonsInstantiated(MBeanExporter.java:434)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:863)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
    at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:157)
    at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:137)
    at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:91)
    at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:171)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5267)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:754)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:730)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
    at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:629)
    at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1839)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
    at java.util.concurrent.FutureTask.run(FutureTask.java)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: javax.management.InstanceAlreadyExistsException: com.zaxxer.hikari:name=dataSource,type=HikariDataSource
    at com.sun.jmx.mbeanserver.Repository.addMBean(Repository.java:437)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerWithRepository(DefaultMBeanServerInterceptor.java:1898)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerDynamicMBean(DefaultMBeanServerInterceptor.java:966)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerObject(DefaultMBeanServerInterceptor.java:900)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerMBean(DefaultMBeanServerInterceptor.java:324)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.registerMBean(JmxMBeanServer.java:522)
    at org.springframework.jmx.support.MBeanRegistrationSupport.doRegister(MBeanRegistrationSupport.java:137)
    at org.springframework.jmx.export.MBeanExporter.registerBeanInstance(MBeanExporter.java:671)
    at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:615)
    ... 28 common frames omitted

2) I need to provide external configurations for both applications and it needs to be clear that each application uses the right configuration.

Upvotes: 2

Views: 4635

Answers (1)

Simulant
Simulant

Reputation: 20112

1) When two Spring-Boot applications run on the same tomcat you will normally face the issue that the instantiation of a datasource might fail, because an instance with the same name already exists. This is the exception you described in your question.

This can be solved by adding a unique name to each Spring-Boot application e.g. in the

application.yml

spring:
  application:
    name: application-name-1
  jmx:
    default-domain: application-name-1

2) Providing an external configuration for each Spring-Boot application can be done by tomcat context configurations for every application individually. Assuming that all applications are deployed as .war e.g. app1.war, app2.war we need to configure the context for both applications as the following:

create the following files (and directories if missing)

tomcat-base-dir
  /conf
    /catalina
      /localhost #must be the same as specified in the Host tag in the server.xml
        app1.xml #must have the same name as the .war application file
        app2.xml

Content for the app1.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- docBase must contain be the name of the application to be configured -->
<Context docBase="app1.war"> 
    <Parameter name="spring.config.location" value="${catalina.base}/conf/app1.yml" />
</Context>

This will configure the application app1.war to use the file app1.yml for configuration. Do the same for app2. The actual configuration file app1.yml can be located at any path specified in the value.

Upvotes: 3

Related Questions