2014-07-24 18 views
33
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); 

        } 
       }); 
      } 
     } 
    }; 
} 

}Come creare contesto JNDI nella primavera del bagagliaio con integrato Tomcat container

sto usando avvio primavera e cercando di avvio con un Tomcat incorporato che crea un contesto JNDI per le mie origini dati:

<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> 

Se rimuovo @ImportResource, l'applicazione si avvia correttamente. Posso connettermi all'istanza di tomcat. Posso controllare tutti gli endpoint del mio attuatore. Usando JConsole, posso connettermi all'applicazione Posso vedere la mia origine dati negli MBean (Catalina -> Risorsa -> Contesto -> "/" -> localhost -> javax.sql.DataSource -> jdbc/mydatasource)

Ho anche MBean che mostrano, tramite JConsole, qui (Tomcat -> DataSource ->/-> localhost -> javax.sql.DataSource -> jdbc/mydatasource)

Tuttavia, quando I @ImportResource cosa sta effettivamente cercando mydatasource via JNDI, non lo trova.

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

La parte rilevante del mio file XML importato

Il ContextResource che sto configurazione sopra è con gli stessi esatti parametri che stavo usando in context.xml che sta ottenendo distribuiti quando il l'applicazione viene distribuita in un contenitore Tomcat. I miei fagioli importati e la mia applicazione funzionano correttamente quando vengono distribuiti in un contenitore Tomcat.

Quindi sembra che ora abbia un contesto, ma non sembra che la denominazione sia corretta. Ho provato varie combinazioni del nome della risorsa, ma non riesco a generare un "comp" legato in questo contesto.

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 

risposta

47

Per impostazione predefinita, JNDI è disattivato nel incorporato Tomcat che sta causando il NoInitialContextException. È necessario chiamare Tomcat.enableNaming() per abilitarlo. Il modo più semplice per farlo è con un TomcatEmbeddedServletContainer sottoclasse:

@Bean 
public TomcatEmbeddedServletContainerFactory tomcatFactory() { 
    return new TomcatEmbeddedServletContainerFactory() { 

     @Override 
     protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
       Tomcat tomcat) { 
      tomcat.enableNaming(); 
      return super.getTomcatEmbeddedServletContainer(tomcat); 
     } 
    }; 
} 

Se si prende questo approccio, è anche possibile registrare il DataSource in JNDI sovrascrivendo il metodo postProcessContext nel vostro TomcatEmbeddedServletContainerFactory sottoclasse.

context.getNamingResources().addResource aggiunge la risorsa alla java:comp/env contesto così il nome della risorsa non deve essere jdbc/mydatasourcejava:comp/env/mydatasource.

Tomcat utilizza il programma di caricamento classi contesto thread per determinare a quale contesto JNDI deve essere eseguita una ricerca. Stai vincolando la risorsa nel contesto JNDI dell'app Web, quindi devi assicurarti che la ricerca venga eseguita quando il caricatore di classe dell'app Web è il caricatore di classe del contesto thread. Dovresti essere in grado di ottenere ciò impostando lookupOnStartup su false su jndiObjectFactoryBean. Avrete anche bisogno di impostare expectedType-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> 

Questo creerà un proxy per il DataSource con la ricerca JNDI reale in corso di esecuzione al primo utilizzo piuttosto che durante contesto l'avvio dell'applicazione.

L'approccio sopra descritto è illustrato in this Spring Boot sample.

+0

Penso che il tuo suggerimento mi abbia avvicinato. Non riesco ancora a trovare le mie risorse anche se si presentano in JConsole. – DaShaun

+0

Ho aggiornato la mia risposta con qualche altro dettaglio. Si noti che si stava utilizzando il nome sbagliato per associare la risorsa a JNDI. Ho anche mostrato come chiamare tomcat.enableNaming() anziché copiare il codice. –

+0

L'aggiunta del bean TomccatEmbeddedServletContainer consente di visualizzare gli MBean "DataSource" tramite JConsole, è grandioso. Ora trovo un contesto, ma il [comp] non viene trovato. – DaShaun

10

Dopo tutto ho avuto la risposta grazie a wikisona, primi i fagioli:

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

il codice completo è qui: https://github.com/wilkinsona/spring-boot-sample-tomcat-jndi

+1

Ricevo 'java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory' quando provo a utilizzare il codice precedente. Tutto funziona correttamente se definisco i pool db tomcat tramite 'application.properties'. Ho usato le dipendenze del boot primaverile ma sembra che manchi qualcosa per JNDI. È per la versione di tomcat incorporata 8.5.x. –

+1

Ho risolto il problema aggiungendo la riga, 'resource.setProperty (" factory "," org.apache.tomcat.jdbc.pool.DataSourceFactory ");'. [La mia domanda SO] (http://stackoverflow.com/questions/39284947/springboot-jndi-datasource-throws-java-lang-classnotfoundexception-org-apache-t) o ho bisogno di aggiungere dipendenza dbcp separata ma che doesn ' t una buona idea con avvio a molla. –

0

Si prega di notare, invece di

public TomcatEmbeddedServletContainerFactory tomcatFactory() 

ho dovuto utilizzare il seguente metodo firma

public EmbeddedServletContainerFactory embeddedServletContainerFactory() 
0

Hai provato @Lazy caricando l'origine dati? Poiché stai inizializzando il tuo contenitore Tomcat incorporato nel contesto Spring, devi ritardare l'inizializzazione del tuo DataSource (fino a quando i vars JNDI sono stati impostati).

N.B. Non ho ancora avuto la possibilità di provare questo codice!

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

Potrebbe anche essere necessario aggiungere il @Lazy annotazioni ovunque si utilizza il DataSource. per esempio.

@Lazy 
@Autowired 
private DataSource dataSource;