Una forma sencilla de poder configurar nuestro Tomcat embebido en Spring Boot y aprovecharlo todo lo posible.
Índice de contenidos
- 1. Introducción
- 2. Entorno
- 3. Creando la clase para la configuración
- 4. Configurando nuestro contexto de ejemplo
- 5. Una pequeña mejora
- 6. Conclusiones
- 7. Referencias
1. Introducción
Spring Boot nos permite «olvidarnos» de ciertas cuestiones de configuración que con Spring pueden ser algo tediosas o que simplemente queremos aprovechar todo el potencial de las anotaciones, ya que nos quita el tener que estar trabajando con archivos XML de configuración, aunque esto no quita el hecho de que podamos seguir usándolo de la manera antigua.
Todo esto surge con mi necesidad de levantar un contexto de una versión muy antigua de Spring en un Spring Boot actual, y ya de paso aprovechar el Tomcat embebido y no tenerlo por separado.
Como ejemplo intentaremos configurar un contexto tal como podríamos hacer en el «server.xml» de un Tomcat cualquiera, pero lo haremos completamente en Java.
Nuestro reto será configurar una variable de entorno que será un path que use un bean ajeno y un recurso que será la configuración de nuestro data source.
2. Entorno
El tutorial está escrito usando el siguiente entorno:
- Hardware: portátil MacBook Pro 15′ (2 Ghz Intel Core I7, 8GB DDR3).
- Sistema operativo: Mac OS Sierra 10.12.5
- Entorno de desarrollo: Eclipse Neon 3
- Spring Boot 1.5.4 (Tomcat 8.5 embebido)
3. Creando nuestra clase para la configuración
En primer lugar crearemos una clase que nos servirá de configuración y se llamará AppConfiguration.java. El nombre puede ser el que queramos, ya que no afecta a Spring Boot en ningún sentido, podría llamarse TomcatConfiguration o como se desee. En ella configuraremos el data source y Tomcat.
Nuestra clase de configuración quedaría tal que:
@Configuration @EnableTransactionManagement public class AppConfiguration { }
La anotación «@Configuration» es la que le permite a Spring Boot entender que esta clase aporta configuración que puede ser distinta a la que él autoconfigura en un principio.
La anotación «@EnableTransactionManagement» es relevante con respecto al data source.
4. Configurando nuestro contexto de ejemplo
Una vez tenemos creado un punto de partida procederemos a configurar el data source y Tomcat creando un par de beans, uno de ellos será el data source y el otro nos permitirá crear la configuración del Tomcat embebido.
Debe tenerse muy en cuenta que, dependiendo de las características del proyecto y de cómo debe configurarse Tomcat según su versión, el siguiente código puede variar bastante del ejemplo a vuestro proyecto, ya que al fin y al cabo es configuración y hay un amplio abanico de posibilidades y casos.
Nuestra clase de configuración tomaría la siguiente forma:
@Configuration @EnableTransactionManagement public class AppConfig { // La anotación Primary hace que este bean tenga prioridad sobre otros iguales, no es // necesario. Además, el data source es muy peculiar y no condiciona nuestra configuración // de Tomcat. @Primary @Bean public DataSource dataSource() { // Configuración de JNDI que necesita un bean del proyecto ya que requiere unos // argumentos. final JndiObjectFactoryBean bean = new JndiObjectFactoryBean(); final Properties jndiEnvironment = new Properties(); jndiEnvironment.setProperty("name", "java:comp/env/customDirExample"); bean.setJndiEnvironment(jndiEnvironment); bean.setJndiName("java:comp/env/jdbc/exampleDB"); bean.setProxyInterface(DataSource.class); // Como comentaba anteriormente, el caso del lookup fue necesario por peculiaridades del // proyecto. bean.setLookupOnStartup(false); bean.afterPropertiesSet(); // Devuelve el data source con los cambios necesarios. return (DataSource) bean.getObject(); } // Este bean nos permite configurar Tomcat, dependiendo de las necesidades puede variar // bastante la implementación, por lo cual recomiendo encarecidamente revisar la documentación // de Spring Boot e ir incrementalmente añadiendo configuración, según sea necesario. @Bean public TomcatEmbeddedServletContainerFactory tomcatFactory() { return new TomcatEmbeddedServletContainerFactory() { @Override protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) { tomcat.enableNaming(); return super.getTomcatEmbeddedServletContainer(tomcat); } @Override protected void postProcessContext(Context context) { // Nos permite cambiar el contexto una vez levantado automáticamente por Spring // Boot. // Creamos la variable que empleará uno de los beans del proyecto. ContextEnvironment environment = new ContextEnvironment(); environment.setName("customDir"); environment.setType("java.lang.String"); environment.setValue("/Users/username/..."); // Configuramos el data source. ContextResource resource = new ContextResource(); resource.setName("jdbc/exampleDB"); resource.setAuth("Container"); resource.setType("javax.sql.DataSource"); resource.setProperty("maxTotal", "100"); resource.setProperty("maxIdle", "30"); resource.setProperty("maxWaitMillis", "1000"); resource.setProperty("driverClassName", "com.mysql.jdbc.Driver"); resource.setProperty("username", "root"); resource.setProperty("password", "root"); resource.setProperty("url", "jdbc:mysql://localhost:3306/exampledb?autoReconnect=true&useSSL=false"); resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory"); // Una vez creada la variable de entorno y el recurso del data source los añadimos // al contexto. context.getNamingResources().addEnvironment(environment); context.getNamingResources().addResource(resource); } }; } }
Como puede verse, la implementación está plagada de peculiaridades que no tienen por qué ser así en todos los proyectos, pero ya que nos basamos en un caso real es necesario tenerlo en cuenta.
Lo que realmente configura Tomcat son unas pocas líneas y realmente simples; es como la etiqueta «Context» de un server.xml pero en código Java.
Dependiendo de nuestras necesidades podemos sobreescribir el método que más nos convenga. En este ejemplo sobreescribimos el método postProcessContext, pero hay otros para configurar el contexto como configureContext. También podemos cambiar el baseDir entre otras cosas, aunque la mayor parte depende de sus los componentes del contexto como ContextEnvironment o ContextResource.
5. Una pequeña mejora
Una vez que tenemos bien configurado nuestro Tomcat hay algo que podemos mejorar. En lugar de escribir directamente la configuración del datasource y otros parámetros, ¿por qué no llevárnoslo todo a un YML?, ¡aprovechemos que Spring Boot puede tomar configuración de un YML!.
Para tomar la configuración de un YML necesitamos primero crear una carpeta en la raíz del proyecto y la llamaremos «config», dentro de esta crearemos el archivo «application.yml» con la siguiente configuración:
server: port: 8008 spring: datasource: url: jdbc:mysql://localhost:3306/exampledb?autoReconnect=true&useSSL=false name: exampledb username: root password: root type: javax.sql.DataSource driver-class-name: com.mysql.jdbc.Driver validation-query: SELECT 1 FROM ExampleTable
Mediante el YML le estamos diciendo a Spring Boot que queremos que su Tomcat use el puerto 8008 con la configuración del datasource que le estamos poniendo.
Todas las propiedades que podemos decirle a Spring Boot que configure las podemos encontrar aquí.
Pero Spring Boot aún no sabe nada de nuestro archivo de configuración, para que lo reconozca y lo use hay que ir a nuestro Eclipse y añadírselo en el classpath. Para esto configuramos el classpath de «Ejecutar como» o «Run as» -> «Run configurations». En el perfil que empleamos para ejecutar nuestra aplicación vamos a la pestaña classpath, hacemos click sobre «User Entries» o «Entradas de Usuario» y le damos al botón «Avanzado», seleccionamos añadir una carpeta y le decimos que añada nuestra carpeta config.
Una vez añadida la carpeta config al classpath no debería haber ningún problema, pero tenemos que acceder a estos valores desde el código con la anotación @Value, añadiendo la siguiente pieza de código al principio de nuestra clase de configuración:
@Value("${spring.datasource.driver-class-name}") private String DB_DRIVER; @Value("${spring.datasource.password}") private String DB_PASSWORD; @Value("${spring.datasource.url}") private String DB_URL; @Value("${spring.datasource.username}") private String DB_USERNAME;
Una vez conseguidos los valores que necesitemos procedemos a reemplazar las string que antes metíamos directamente por nuestros parámetros obtenidos del YML. Ahora mejor, ¿verdad?
6. Conclusiones
Como hemos visto, configurar nuestro Tomcat embebido es realmente sencillo, el verdadero reto es ir configurando poco a poco nuestro Spring Boot y Tomcat para satisfacer las necesidades de nuestro proyecto. Para ello recomiendo paciencia, tener cerca la documentación de Spring Boot y un motor de búsqueda.
Configurar el Tomcat embebido nos permite incluir la configuración general de nuestra aplicación y dejar en archivos externos sólo lo que queramos parametrizar simplificando, en algunos casos, enormemente la instalación de nuestra aplicación Spring Boot en cualquier sistema.
7. Referencias
- Spring Boot Docs. Documentación de Spring Boot.