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 la primavera de arranque y tratando de inicio con un tomcat embebido 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 puedo quitar el @ImportResource mi aplicación se inicia bien. Me puedo conectar a la instancia de tomcat. Puedo comprobar todos los de mi actuador extremos. El uso de JConsole, me puedo conectar a la aplicación puedo ver a mi origen de datos en el MBeans (Catalina -> Recursos> Contexto -> «/» -> localhost -> javax.sql.DataSource -> jdbc/mydatasource)

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

Sin embargo, cuando me @ImportResource lo que realmente está buscando mydatasource a través de JNDI, es no encontrarla.

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

El correspondiente parte de mi archivo xml importado

La ContextResource que estoy configuración anterior es con exactamente los mismos parámetros que yo estaba usando en el context.xml que se está implementado cuando la aplicación se implementa en un contenedor tomcat. A mi las habas y mi aplicación funcionan correctamente cuando se implementa en un contenedor tomcat.

Así que parece que tengo un contexto ahora, pero no parece que el nombre es correcto. He intentado varias combinaciones del nombre del recurso, pero parece que no puede generar un «borrador» enlazado 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
InformationsquelleAutor DaShaun | 2014-07-24

6 Comentarios

  1. 56

    Por defecto, JNDI está deshabilitado en Tomcat embebido que está causando el NoInitialContextException. Usted necesita llamar a Tomcat.enableNaming() para activarlo. La forma más fácil de hacerlo es con un TomcatEmbeddedServletContainer subclase:

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

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

    context.getNamingResources().addResource añade el recurso a la java:comp/env contexto para el nombre del recurso debe ser jdbc/mydatasource no java:comp/env/mydatasource.

    Tomcat utiliza el hilo contexto de la clase loader para determinar qué contexto JNDI una búsqueda se debe realizar en contra. Estás unión de los recursos en la aplicación web del contexto JNDI por lo que necesita para asegurarse de que la búsqueda se realiza cuando la aplicación web del cargador de clases es el hilo conductor del contexto de la clase loader. Usted debe ser capaz de lograr esto mediante el establecimiento de lookupOnStartup a false en el jndiObjectFactoryBean. Usted también tendrá que establecer expectedType a 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 va a crear un proxy para el origen de datos con el real JNDI de búsqueda que se realiza en el primer uso, en lugar de durante el contexto de la aplicación de inicio.

    El enfoque descrito anteriormente se ilustra en la esta Primavera de Inicio de la muestra.

    • Creo que tu sugerencia me tiene más cerca. Todavía soy incapaz de encontrar mis recursos, aunque están apareciendo en JConsole.
    • He actualizado mi respuesta, con algunos detalles más. Tenga en cuenta que usted está usando el nombre equivocado para enlazar los recursos en JNDI. También he mostrado cómo puede llamar a tomcat.enableNaming() en lugar de copiar el código.
    • La adición de la TomccatEmbeddedServletContainer bean pone «origen de datos» MBeans visible a través de JConsole, que es grande. Ahora me obtención de un contexto, pero el [comp] no se encuentra.
    • La adición de la JndiObjectFactoryBean pone el código anterior en un desbordamiento de pila del estado.
    • Gracias por toda la ayuda!
    • Estoy teniendo un tiempo difícil la aplicación de este, al menos cuando se utiliza Graisl 3.0. Hay una actualización de manera de realizar esto? La forma en que ustedes esquema ya no parece funcionar
    • Es el contexto JNDI crear aquí utilizable a lo largo de la JVM, incluso por los objetos que no crea la instancia de la Primavera? Me estoy poniendo un NameNotFound con el detalle, «no se puede encontrar[java:comp]»
    • Como he dicho en mi respuesta, «Tomcat utiliza el hilo contexto de la clase loader para determinar qué contexto JNDI una búsqueda se debe realizar en contra». Un JNDI de búsqueda sólo funcionará cuando el TCCL es Tomcat aplicación web del cargador de clases.
    • Para responder a mi propia pregunta, se puede utilizar en todo el JVM si se utiliza el «hack» que se describe aquí: stackoverflow.com/questions/27822619/…
    • En lugar de definir el origen de datos en el postProcessContext método, es posible el uso de una Primavera genera en función de la primavera.origen de datos.* propiedades de la aplicación.propiedades?
    • este no me funciona, me sale javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial tengo mi jndi recursos definidos como los frijoles en otros archivos.

  2. 12

    Después de todo tengo la respuesta gracias a wikisona, primero los granos:

    @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

    • Estoy recibiendo java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory cuando intente usar código anterior. Todo funciona bien, si defino tomcat db piscinas a través de application.properties. He utilizado la primavera de arranque de las dependencias, pero se ve que me falta algo para JNDI. Su embebidos tomcat versión 8.5.x.
    • He resuelto problema mediante la adición de la línea , resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory"); . Mi Pregunta o tengo que agregar por separado dbcp dependencia, pero que no parece una buena idea con la primavera de arranque.
    • Khan Mediante el Tomcat jdbc DS es una posibilidad razonable. Pero no creo que el uso de otro DS está mal. La primavera de Arranque no cubrir esta parte en un fuera de la caja de manera que no en todos los estándar en la Primavera de Arranque para usar directamente JNDI, pero a veces no queremos estar limitado a los Tomcat jdbc DS o no quieren usar.
  3. 9

    Recientemente tuve la necesidad de utilizar de JNDI con un Tomcat incluido en la Primavera de Arranque.

    Real de las respuestas que dan algunos interesantes consejos para resolver mi tarea pero no fue suficiente, ya que probablemente no se actualiza para la Primavera de Arranque 2.

    Aquí está mi contribución probado con Spring Boot 2.0.3.De LIBERACIÓN.

    Especificar un origen de datos disponibles en la ruta de clases en tiempo de ejecución

    Tienes varias opciones :

    • utilizando el DBCP 2 origen de datos (usted no desea utilizar el DBCP 1 que es obsoleta y menos eficiente).
    • utilizando el Tomcat origen de datos JDBC.
    • el uso de cualquier otro origen de datos : por ejemplo HikariCP.

    Si no se especifica ninguno de ellos, con la configuración por defecto de la creación de instancias de el origen de datos, se produce una excepción :

    Causada por: javax.nomenclatura.NamingException: no se Pudo crear el recurso de instancia de fábrica 
    en org.apache.nomenclatura.de la fábrica.ResourceFactory.getDefaultFactory(ResourceFactory.java:50) 
    en org.apache.nomenclatura.de la fábrica.FactoryBase.getObjectInstance(FactoryBase.java:90) 
    en javax.nomenclatura.spi.NamingManager.getObjectInstance(NamingManager.java:321) 
    en org.apache.nomenclatura.NamingContext.la búsqueda(NamingContext.java:839) 
    en org.apache.nomenclatura.NamingContext.la búsqueda(NamingContext.java:159) 
    en org.apache.nomenclatura.NamingContext.la búsqueda(NamingContext.java:827) 
    en org.apache.nomenclatura.NamingContext.la búsqueda(NamingContext.java:159) 
    en org.apache.nomenclatura.NamingContext.la búsqueda(NamingContext.java:827) 
    en org.apache.nomenclatura.NamingContext.la búsqueda(NamingContext.java:159) 
    en org.apache.nomenclatura.NamingContext.la búsqueda(NamingContext.java:827) 
    en org.apache.nomenclatura.NamingContext.la búsqueda(NamingContext.java:173) 
    en org.apache.nomenclatura.SelectorContext.la búsqueda(SelectorContext.java:163) 
    en javax.nomenclatura.InitialContext.la búsqueda(InitialContext.java:417) 
    en org.springframework.jndi.JndiTemplate.lambda$de búsqueda$0(JndiTemplate.java:156) 
    en org.springframework.jndi.JndiTemplate.ejecutar(JndiTemplate.java:91) 
    en org.springframework.jndi.JndiTemplate.la búsqueda(JndiTemplate.java:156) 
    en org.springframework.jndi.JndiTemplate.la búsqueda(JndiTemplate.java:178) 
    en org.springframework.jndi.JndiLocatorSupport.la búsqueda(JndiLocatorSupport.java:96) 
    en org.springframework.jndi.JndiObjectLocator.la búsqueda(JndiObjectLocator.java:114) 
    en org.springframework.jndi.JndiObjectTargetSource.getTarget(JndiObjectTargetSource.java:140) 
    ... 39 comunes marcos omite 
    Causado por: java.lang.ClassNotFoundException: org.apache.tomcat.el dbcp.dbcp2.BasicDataSourceFactory 
    en java.net.URLClassLoader.findClass(URLClassLoader.java:381) 
    en java.lang.Cargador de clases.loadClass(cargador de clases.java:424) 
    en el sol.misc.Lanzador De$AppClassLoader.loadClass(Lanzador.java:331) 
    en java.lang.Cargador de clases.loadClass(cargador de clases.java:357) 
    en java.lang.Clase.forName0(Native Method) 
    en java.lang.Clase.forName(de la Clase.java:264) 
    en org.apache.nomenclatura.de la fábrica.ResourceFactory.getDefaultFactory(ResourceFactory.java:47) 
    ... 58 comunes marcos omite 
    
    • Utilizar Apache origen de datos JDBC, no es necesario añadir ninguna dependencia, pero usted tiene que cambiar el valor predeterminado de fábrica en clase a org.apache.tomcat.jdbc.pool.DataSourceFactory.

      Usted puede hacerlo en la declaración de recurso :
      resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
      Voy a explicar a continuación donde agregar esta línea.

    • Utilizar el DBCP 2 el origen de datos de una dependencia se requiere:

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

    Por supuesto, adaptar el artefacto versión de acuerdo a su Muelle de Arranque de Tomcat versión incrustada.

    • Utilizar HikariCP, agregar la dependencia necesaria si no se encuentra ya en su configuración (puede ser que si usted confía en la persistencia de los arrancadores de la Primavera de Arranque), tales como :

      <dependency>
      <groupId>com.zaxxer</groupId>
      <artifactId>HikariCP</artifactId>
      <version>3.1.0</version>
      </dependency>

    y especificar la fábrica que va en la declaración de recurso:

    resource.setProperty("factory", "com.zaxxer.hikari.HikariDataSource");
    

    Origen de datos de configuración/declaración

    Usted tiene que personalizar el bean que crea el TomcatServletWebServerFactory instancia.

    Dos cosas que hacer :

    • la habilitación de la JNDI de nomenclatura que está deshabilitada de forma predeterminada

    • crear y agregar el recurso JNDI(s) en el contexto del servidor

    Por ejemplo con PostgreSQL y un DBCP 2 origen de datos, haga que :

    @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);          
    }
    };
    }
    

    Aquí las variantes de Tomcat JDBC y HikariCP origen de datos.

    En postProcessContext() conjunto de la fábrica de los bienes, como se explicó temprano para Tomcat JDBC ds :

        @Override 
    protected void postProcessContext(Context context) {
    ContextResource resource = new ContextResource();       
    //...
    resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
    //...
    context.getNamingResources()
    .addResource(resource);          
    }
    };
    

    y para HikariCP :

        @Override 
    protected void postProcessContext(Context context) {
    ContextResource resource = new ContextResource();       
    //...
    resource.setProperty("factory", "com.zaxxer.hikari.HikariDataSource");
    //...
    context.getNamingResources()
    .addResource(resource);          
    }
    };
    

    Mediante Inyección el origen de datos

    Ahora debería ser capaz de buscar el recurso JNDI en cualquier lugar mediante el uso de un estándar InitialContext ejemplo :

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

    También puede utilizar JndiObjectFactoryBean de la Primavera a la búsqueda del recurso :

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

    Para tomar ventaja de la DI contenedor también puede hacer que el DataSource una Primavera bean :

    @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 por lo que ahora puede inyectar en el origen de datos en cualquier Primavera frijoles tales como :

    @Autowired
    private DataSource jndiDataSource;
    

    Tenga en cuenta que muchos ejemplos en internet parecen deshabilitar la búsqueda de la JNDI de recursos en el arranque :

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

    Pero creo que es impotente como se invoca sólo después de afterPropertiesSet() que hace la búsqueda !

    • En primer lugar, muchas gracias. Esta no es solo una gran descripción de cómo llegar JNDI de trabajo w/primavera de arranque 2 incrustado tomcat – es también uno de los únicos recursos como lo he encontrado. x100 gracias. Una nota – necesitaba el setProxyInterface/setLookupOnState llamadas para evitar InstanceAlreadyExistsException excepciones.
    • Gracias por tus buenas palabras. Tengo que tomar alrededor de 1 día entero para hacer que funcione correctamente : generalmente legado soluciones no están bien documentados… Pero muy contento de que este post puede ayudar a otros con este raro requisito. Sobre tu último punto, es interesante, no me esta excepción por la eliminación de estas dos afirmaciones.
    • Para mí no funciona con Hikari, he tenido que cambiar la línea de fábrica, para resource.setProperty("factory", "com.zaxxer.hikari.HikariJNDIFactory"); (en lugar de HikariDataSource)
  4. 1

    Por favor nota en lugar de

    public TomcatEmbeddedServletContainerFactory tomcatFactory()
    

    He tenido que utilizar el método siguiente de la firma

    public EmbeddedServletContainerFactory embeddedServletContainerFactory() 
    
  5. 1

    En la primavera de arranque 2.1, he encontrado otra solución.
    Ampliar estándar de fábrica método de clase getTomcatWebServer. Y luego lo devuelve como un bean desde cualquier lugar.

    public class CustomTomcatServletWebServerFactory extends TomcatServletWebServerFactory {
    @Override
    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    System.setProperty("catalina.useNaming", "true");
    tomcat.enableNaming();
    return new TomcatWebServer(tomcat, getPort() >= 0);
    }
    }
    @Component
    public class TomcatConfiguration {
    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
    TomcatServletWebServerFactory factory = new CustomTomcatServletWebServerFactory();
    return factory;
    }
    

    La carga de recursos de context.xml no funciona bien. Tratará de averiguar.

  6. 0

    ¿Has probado @Lazy carga el origen de datos? Porque eres de iniciar su contenedor Tomcat embebido dentro del contexto de la Primavera, usted tiene que retrasar el comienzo de su DataSource (hasta el JNDI vars han sido el programa de instalación).

    N. B. 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();
    }

    Usted también puede necesitar para agregar el @Lazy anotación donde el origen de datos está siendo utilizada. por ejemplo,

    @Lazy
    @Autowired
    private DataSource dataSource;

Dejar respuesta

Please enter your comment!
Please enter your name here