springboot example data annotation jndi spring-boot embedded-tomcat-7

example - Cómo crear contexto JNDI en Spring Boot con Embedded Tomcat Container



spring data jndi datasource (5)

¿Has probado @Lazy cargando el origen de datos? Debido a que está inicializando su contenedor integrado Tomcat dentro del contexto de Spring, debe retrasar la inicialización de su DataSource (hasta que se hayan configurado los vars JNDI).

NB ¡ No he tenido la oportunidad de probar este código todavía!

@Lazy @Bean(destroyMethod="") public DataSource jndiDataSource() throws IllegalArgumentException, NamingException { JndiObjectFactoryBean bean = new JndiObjectFactoryBean(); bean.setJndiName("java:comp/env/jdbc/myDataSource"); bean.setProxyInterface(DataSource.class); //bean.setLookupOnStartup(false); bean.afterPropertiesSet(); return (DataSource)bean.getObject(); }

También es posible que necesite agregar la anotación @Lazy siempre que se @Lazy el DataSource. p.ej

@Lazy @Autowired private DataSource dataSource;

import org.apache.catalina.Context; import org.apache.catalina.deploy.ContextResource; import org.apache.catalina.startup.Tomcat; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; @Configuration @EnableAutoConfiguration @ComponentScan @ImportResource("classpath:applicationContext.xml") public class Application { public static void main(String[] args) throws Exception { new SpringApplicationBuilder() .showBanner(false) .sources(Application.class) .run(args); } @Bean public TomcatEmbeddedServletContainerFactory tomcatFactory() { return new TomcatEmbeddedServletContainerFactory() { @Override protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer( Tomcat tomcat) { tomcat.enableNaming(); return super.getTomcatEmbeddedServletContainer(tomcat); } }; } @Bean public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer() { return new EmbeddedServletContainerCustomizer() { @Override public void customize(ConfigurableEmbeddedServletContainer container) { if (container instanceof TomcatEmbeddedServletContainerFactory) { TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory = (TomcatEmbeddedServletContainerFactory) container; tomcatEmbeddedServletContainerFactory.addContextCustomizers(new TomcatContextCustomizer() { @Override public void customize(Context context) { ContextResource mydatasource = new ContextResource(); mydatasource.setName("jdbc/mydatasource"); mydatasource.setAuth("Container"); mydatasource.setType("javax.sql.DataSource"); mydatasource.setScope("Sharable"); mydatasource.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver"); mydatasource.setProperty("url", "jdbc:oracle:thin:@mydomain.com:1522:myid"); mydatasource.setProperty("username", "myusername"); mydatasource.setProperty("password", "mypassword"); context.getNamingResources().addResource(mydatasource); } }); } } }; }

}

Estoy usando el arranque de primavera e intento iniciar con un tomcat integrado que crea un contexto JNDI para mis fuentes de datos:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>1.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>1.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-oracle</artifactId> <version>1.0.0.RELEASE</version> </dependency>

Si elimino @ImportResource, mi aplicación se iniciará correctamente. Puedo conectarme a la instancia de tomcat. Puedo verificar todos los puntos finales de mi actuador. Utilizando JConsole, puedo conectarme a la aplicación. Puedo ver mi fuente de datos en los MBeans (Catalina -> Recurso -> Contexto -> "/" -> localhost -> javax.sql.DataSource -> jdbc / mydatasource)

También tengo MBeans que aparecen, a través de JConsole, aquí (Tomcat -> DataSource -> / -> localhost -> javax.sql.DataSource -> jdbc / mydatasource)

Sin embargo, cuando yo @ImportResource busca realmente mydatasource a través de JNDI, no lo encuentra.

<bean id="myDS" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:comp/env/jdbc/mydatasource"/> </bean>

La parte relevante de mi archivo xml importado

El ContextResource que configuro arriba está con exactamente los mismos parámetros que estaba usando en el contexto.xml que se implementa cuando la aplicación se implementa en un contenedor de tomcat. Mis beans importados y mi aplicación funcionan correctamente cuando se implementan en un contenedor de tomcat.

Entonces parece que tengo un contexto ahora, pero no parece que el nombre sea correcto. Intenté varias combinaciones del nombre del recurso, pero parece que no puedo generar un límite "comp" en este contexto.

Caused by: javax.naming.NameNotFoundException: Name [comp/env/jdbc/mydatasource] is not bound in this Context. Unable to find [comp]. at org.apache.naming.NamingContext.lookup(NamingContext.java:819) at org.apache.naming.NamingContext.lookup(NamingContext.java:167) at org.apache.naming.SelectorContext.lookup(SelectorContext.java:156) at javax.naming.InitialContext.lookup(InitialContext.java:392) at org.springframework.jndi.JndiTemplate$1.doInContext(JndiTemplate.java:155) at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:87) at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:152) at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:179) at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:95) at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:106) at org.springframework.jndi.JndiObjectFactoryBean.lookupWithFallback(JndiObjectFactoryBean.java:231) at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:217) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549) ... 30 more


De forma predeterminada, JNDI está deshabilitado en Tomcat integrado que causa la NoInitialContextException . Tomcat.enableNaming() llamar a Tomcat.enableNaming() para habilitarlo. La forma más fácil de hacerlo es con una subclase TomcatEmbeddedServletContainer :

@Bean public TomcatEmbeddedServletContainerFactory tomcatFactory() { return new TomcatEmbeddedServletContainerFactory() { @Override protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer( Tomcat tomcat) { tomcat.enableNaming(); return super.getTomcatEmbeddedServletContainer(tomcat); } }; }

Si postProcessContext este enfoque, también puede registrar DataSource en JNDI anulando el método postProcessContext en su subclase TomcatEmbeddedServletContainerFactory .

context.getNamingResources().addResource agrega el recurso al contexto java:comp/env para que el nombre del recurso sea jdbc/mydatasource no java:comp/env/mydatasource .

Tomcat utiliza el cargador de clases de contexto de subprocesos para determinar en qué contexto JNDI se debe realizar una búsqueda. Está vinculando el recurso al contexto JNDI de la aplicación web, por lo que debe asegurarse de que la búsqueda se realice cuando el cargador de clases de la aplicación web es el cargador de clases de contexto de subprocesos. Debería poder lograr esto al establecer lookupOnStartup en false en jndiObjectFactoryBean . También deberá establecer expectedType en javax.sql.DataSource :

<bean class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:comp/env/jdbc/mydatasource"/> <property name="expectedType" value="javax.sql.DataSource"/> <property name="lookupOnStartup" value="false"/> </bean>

Esto creará un proxy para el DataSource con la búsqueda JNDI real que se realiza en el primer uso en lugar de durante el inicio del contexto de la aplicación.

El enfoque descrito anteriormente se ilustra en esta muestra Spring Boot .


Después de todo, recibí la respuesta gracias a wikisona, primero los frijoles:

@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) { ContextResource resource = new ContextResource(); resource.setName("jdbc/myDataSource"); resource.setType(DataSource.class.getName()); resource.setProperty("driverClassName", "your.db.Driver"); resource.setProperty("url", "jdbc:yourDb"); context.getNamingResources().addResource(resource); } }; } @Bean(destroyMethod="") public DataSource jndiDataSource() throws IllegalArgumentException, NamingException { JndiObjectFactoryBean bean = new JndiObjectFactoryBean(); bean.setJndiName("java:comp/env/jdbc/myDataSource"); bean.setProxyInterface(DataSource.class); bean.setLookupOnStartup(false); bean.afterPropertiesSet(); return (DataSource)bean.getObject(); }

el código completo está aquí: https://github.com/wilkinsona/spring-boot-sample-tomcat-jndi


Recientemente tuve el requisito de usar JNDI con un Tomcat integrado en Spring Boot.
Las respuestas reales dan alguna pista para resolver el problema, pero no fueron suficientes, probablemente no se actualizaron.

Aquí está mi contribución probada con Spring Boot 2.0.3.RELEASE.

Pom.xml

Según el origen de datos que haya especificado, es posible que deba proporcionar la biblioteca Tomcat-DBCP.
Por ejemplo, con la configuración predeterminada, la creación de instancias de la fuente de datos lanzó una excepción:

Caused by: javax.naming.NamingException: Could not create resource factory instance at org.apache.naming.factory.ResourceFactory.getDefaultFactory(ResourceFactory.java:50) at org.apache.naming.factory.FactoryBase.getObjectInstance(FactoryBase.java:90) at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:321) at org.apache.naming.NamingContext.lookup(NamingContext.java:839) at org.apache.naming.NamingContext.lookup(NamingContext.java:159) at org.apache.naming.NamingContext.lookup(NamingContext.java:827) at org.apache.naming.NamingContext.lookup(NamingContext.java:159) at org.apache.naming.NamingContext.lookup(NamingContext.java:827) at org.apache.naming.NamingContext.lookup(NamingContext.java:159) at org.apache.naming.NamingContext.lookup(NamingContext.java:827) at org.apache.naming.NamingContext.lookup(NamingContext.java:173) at org.apache.naming.SelectorContext.lookup(SelectorContext.java:163) at javax.naming.InitialContext.lookup(InitialContext.java:417) at org.springframework.jndi.JndiTemplate.lambda$lookup$0(JndiTemplate.java:156) at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:91) at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:156) at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:178) at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:96) at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:114) at org.springframework.jndi.JndiObjectTargetSource.getTarget(JndiObjectTargetSource.java:140) ... 39 common frames omitted Caused by: java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:264) at org.apache.naming.factory.ResourceFactory.getDefaultFactory(ResourceFactory.java:47) ... 58 common frames omitted

Como solución alternativa puede agregar esta dependencia:

<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-dbcp</artifactId> <version>8.5.4</version> </dependency>

Por supuesto, adapte la versión del artefacto según su versión de Tomcat.

Configuración de primavera

TomcatServletWebServerFactory personalizar el bean que crea la instancia de TomcatServletWebServerFactory .
Dos cosas para hacer:

  • habilitando el nombre JNDI que está deshabilitado de forma predeterminada

  • crear y agregar los recursos JNDI en el contexto del servidor

Por ejemplo, con un origen de datos de base de datos PostgreSQL se ve así:

@Bean public TomcatServletWebServerFactory tomcatFactory() { return new TomcatServletWebServerFactory() { @Override protected TomcatWebServer getTomcatWebServer(org.apache.catalina.startup.Tomcat tomcat) { tomcat.enableNaming(); return super.getTomcatWebServer(tomcat); } @Override protected void postProcessContext(Context context) { // context ContextResource resource = new ContextResource(); resource.setName("jdbc/myJndiResource"); resource.setType(DataSource.class.getName()); resource.setProperty("driverClassName", "org.postgresql.Driver"); resource.setProperty("url", "jdbc:postgresql://hostname:port/dbname"); resource.setProperty("username", "username"); resource.setProperty("password", "password"); context.getNamingResources() .addResource(resource); } }; }

Ahora debería poder buscar el recurso JNDI en cualquier lugar utilizando una instancia estándar de InitialContext :

InitialContext initialContext = new InitialContext(); DataSource datasource = (DataSource) initialContext.lookup("java:comp/env/jdbc/myJndiResource");

También puede usar JndiObjectFactoryBean of Spring para buscar el recurso:

JndiObjectFactoryBean bean = new JndiObjectFactoryBean(); bean.setJndiName("java:comp/env/jdbc/myJndiResource"); bean.afterPropertiesSet(); DataSource object = (DataSource) bean.getObject();

Para aprovechar el contenedor DI también puede hacer que DataSource un bean Spring:

@Bean(destroyMethod = "") public DataSource jndiDataSource() throws IllegalArgumentException, NamingException { JndiObjectFactoryBean bean = new JndiObjectFactoryBean(); bean.setJndiName("java:comp/env/jdbc/myJndiResource"); bean.afterPropertiesSet(); return (DataSource) bean.getObject(); }

Y entonces ahora puede inyectar DataSource en cualquier grano de Spring como por ejemplo:

@Autowired private DataSource jndiDataSource;

Tenga en cuenta que muchos ejemplos en Internet parecen desactivar la búsqueda del recurso JNDI en el inicio:

bean.setJndiName("java:comp/env/jdbc/myJndiResource"); bean.setProxyInterface(DataSource.class); bean.setLookupOnStartup(false); bean.afterPropertiesSet();

Pero creo que es impotente, ya que invoca justo después de afterPropertiesSet() que hace la búsqueda!


Tenga en cuenta en lugar de

public TomcatEmbeddedServletContainerFactory tomcatFactory()

Tuve que usar la siguiente firma de método

public EmbeddedServletContainerFactory embeddedServletContainerFactory()