Spring security Ldap authentication Exception : Not an instance of DirContext - spring-boot

I'm trying to connect to a Ldap server (host by the company, don't have much info about it), using Spring Security, I have this bean:
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
DefaultDirObjectFactory factory = new DefaultDirObjectFactory();
LdapContextSource ldapContextSource = new LdapContextSource();
ldapContextSource.setAnonymousReadOnly(true);
ldapContextSource.setUrl("ldap://ldap.company.domain.com:xxxx/dc=company,dc=com");
ldapContextSource.setDirObjectFactory(factory.getClass());
auth.ldapAuthentication().userSearchFilter("uid={0}").contextSource(ldapContextSource);
}
But I got this error:
Caused by: org.springframework.ldap.NotContextException: Not an
instance of DirContext; nested exception is
javax.naming.NotContextException: Not an instance of DirContext
at backend-1.0.0-RC1.war//org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:209)
at backend-1.0.0-RC1.war//org.springframework.ldap.core.LdapTemplate.executeWithContext(LdapTemplate.java:824)
at backend-1.0.0-RC1.war//org.springframework.ldap.core.LdapTemplate.executeReadOnly(LdapTemplate.java:807)
at backend-1.0.0-RC1.war//org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntry(SpringSecurityLdapTemplate.java:260)
at backend-1.0.0-RC1.war//org.springframework.security.ldap.search.FilterBasedLdapUserSearch.searchForUser(FilterBasedLdapUserSearch.java:100)
at backend-1.0.0-RC1.war//org.springframework.security.ldap.authentication.BindAuthenticator.authenticate(BindAuthenticator.java:86)
at backend-1.0.0-RC1.war//org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:174)
... 101 more Caused by: javax.naming.NotContextException: Not an instance of DirContext
at java.naming/javax.naming.directory.InitialDirContext.getURLOrDefaultInitDirCtx(InitialDirContext.java:154)
at java.naming/javax.naming.directory.InitialDirContext.search(InitialDirContext.java:326)
at java.naming/javax.naming.directory.InitialDirContext.search(InitialDirContext.java:326)
at backend-1.0.0-RC1.war//org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntryInternal(SpringSecurityLdapTemplate.java:271)
at backend-1.0.0-RC1.war//org.springframework.security.ldap.SpringSecurityLdapTemplate.lambda$searchForSingleEntry$3(SpringSecurityLdapTemplate.java:260)
at backend-1.0.0-RC1.war//org.springframework.ldap.core.LdapTemplate.executeWithContext(LdapTemplate.java:821)
... 106 more
when doing:
AuthenticationManager.authenticate(authenticateToken)
Because it is working on another projet, using jndi, I know that Ldap info are correct.
Edit:
I tried to add:
Map<String, Object> baseEnvironmentProperties = new HashMap<String, Object>();
baseEnvironmentProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
But still get the same error.
Do you have any idea why I got this error?

I have found a working solution using a LdapAuthenticationProvider:
#Bean
public AuthenticationProvider ldapAuthenticationProvider() throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(List.of("ldap://ldap.company.domain.com:xxx"),"dc=company,dc=com");
contextSource.setAnonymousReadOnly(true);
contextSource.afterPropertiesSet();
LdapUserSearch ldapUserSearch = new FilterBasedLdapUserSearch("", "uid={0}", contextSource);
BindAuthenticator bindAuthenticator = new BindAuthenticator(contextSource);
bindAuthenticator.setUserSearch(ldapUserSearch);
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(bindAuthenticator);
return ldapAuthenticationProvider;
}

Related

SpringBootTest, Testcontainers, container start up - Mapped port can only be obtained after the container is started

I am using docker/testcontainers to run a postgresql db for testing. I have effectively done this for unit testing that is just testing the database access. However, I have now brought springboot testing into the mix so I can test with an embedded web service and I am having problems.
The issue seems to be that the dataSource bean is being requested before the container starts.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [com/myproject/integrationtests/IntegrationDataService.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
Caused by: java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
Here is my SpringBootTest:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {IntegrationDataService.class, TestApplication.class},
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringBootTestControllerTesterIT
{
#Autowired
private MyController myController;
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Test
public void testRestControllerHello()
{
String url = "http://localhost:" + port + "/mycontroller/hello";
ResponseEntity<String> result =
restTemplate.getForEntity(url, String.class);
assertEquals(result.getStatusCode(), HttpStatus.OK);
assertEquals(result.getBody(), "hello");
}
}
Here is my spring boot application referenced from the test:
#SpringBootApplication
public class TestApplication
{
public static void main(String[] args)
{
SpringApplication.run(TestApplication.class, args);
}
}
Here is the IntegrationDataService class which is intended to startup the container and provide the sessionfactory/datasource for everything else
#Testcontainers
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#EnableTransactionManagement
#Configuration
public class IntegrationDataService
{
#Container
public static PostgreSQLContainer postgreSQLContainer = (PostgreSQLContainer) new PostgreSQLContainer("postgres:9.6")
.withDatabaseName("test")
.withUsername("sa")
.withPassword("sa")
.withInitScript("db/postgresql/schema.sql");
#Bean
public Properties hibernateProperties()
{
Properties hibernateProp = new Properties();
hibernateProp.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
hibernateProp.put("hibernate.format_sql", true);
hibernateProp.put("hibernate.use_sql_comments", true);
// hibernateProp.put("hibernate.show_sql", true);
hibernateProp.put("hibernate.max_fetch_depth", 3);
hibernateProp.put("hibernate.jdbc.batch_size", 10);
hibernateProp.put("hibernate.jdbc.fetch_size", 50);
hibernateProp.put("hibernate.id.new_generator_mappings", false);
// hibernateProp.put("hibernate.hbm2ddl.auto", "create-drop");
// hibernateProp.put("hibernate.jdbc.lob.non_contextual_creation", true);
return hibernateProp;
}
#Bean
public SessionFactory sessionFactory() throws IOException
{
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setHibernateProperties(hibernateProperties());
sessionFactoryBean.setPackagesToScan("com.myproject.model.entities");
sessionFactoryBean.afterPropertiesSet();
return sessionFactoryBean.getObject();
}
#Bean
public PlatformTransactionManager transactionManager() throws IOException
{
return new HibernateTransactionManager(sessionFactory());
}
#Bean
public DataSource dataSource()
{
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(postgreSQLContainer.getDriverClassName());
dataSource.setUrl(postgreSQLContainer.getJdbcUrl());
dataSource.setUsername(postgreSQLContainer.getUsername());
dataSource.setPassword(postgreSQLContainer.getPassword());
return dataSource;
}
}
The error occurs on requesting the datasource bean from the sessionFactory from one of the Dao classes before the container starts up.
What the heck am I doing wrong?
Thanks!!!
The reason for your java.lang.IllegalStateException: Mapped port can only be obtained after the container is started exception is that when the Spring Context now gets created during your test with #SpringBootTest it tries to connect to the database on application startup.
As you only launch your PostgreSQL inside your IntegrationDataService class, there is a timing issue as you can't obtain the JDBC URL or create a connection on application startup as this bean is not yet properly created.
In general, you should NOT use any test-related code inside your IntegrationDataService class. Starting/stopping the database should be done inside your test setup.
This ensures to first start the database container, wait until it's up- and running, and only then launch the actual test and create the Spring Context.
I've summarized the required setup mechanism for JUnit 4/5 with Testcontainers and Spring Boot, that help you get the setup right.
In the end, this can look like the following
// JUnit 5 example with Spring Boot >= 2.2.6
#Testcontainers
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationIT {
#Container
public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer()
.withPassword("inmemory")
.withUsername("inmemory");
#DynamicPropertySource
static void postgresqlProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
registry.add("spring.datasource.password", postgreSQLContainer::getPassword);
registry.add("spring.datasource.username", postgreSQLContainer::getUsername);
}
#Test
public void contextLoads() {
}
}

Access denied to resource: type=<jms>, application=JMSModule, destinationType=queue, resource=test, action=receive

I'm try to receive a weblogic server jms in spring boot . But I have encountered this problem after launch application successfully.
[]org.springframework.jms.listener.DefaultMessageListenerContainer:handleListenerSetupFailure(892): Setup of JMS message listener invoker failed for destination 'jms/test' - trying to recover. Cause: Access denied to resource: type=<jms>, application=JMSModule, destinationType=queue, resource=ns-alert-test, action=receive
And I found it can be connect successfully in thread of [main] when launching.
It looks like the username and password is missed when thread changed
[main] org.springframework.jndi.JndiObjectFactoryBean:lookup(112): Located object with JNDI name [jms/rtsConnectionFactory]
And I'm try to use wlfullclient.jar and wlclient.jar and wlthint3client.jar in my project . But the problem still exists. Can you give me some suggest of this problem . Below is my code
Config:
#Autowired
private JmsErrorHandler jmsErrorHandler;
#Autowired
private JMSPropertiesConfig jmsPropertiesConfig;
#Bean
public JndiTemplate jndiTemplate(){
JndiTemplate jndiTemplate =new JndiTemplate();
Properties properties = new Properties();
properties.setProperty("java.naming.factory.initial","weblogic.jndi.WLInitialContextFactory");
properties.setProperty("java.naming.provider.url", jmsPropertiesConfig.getUrl());
if(jmsPropertiesConfig.getUname()!=null){
properties.setProperty("username", jmsPropertiesConfig.getUname());
}
if(jmsPropertiesConfig.getUcert()!=null){
properties.setProperty("password", jmsPropertiesConfig.getUcert());
}
jndiTemplate.setEnvironment(properties);
return jndiTemplate;
}
#Bean
public JndiDestinationResolver jmsDestionationProvider() {
JndiDestinationResolver destinationResolver = new JndiDestinationResolver();
destinationResolver.setJndiTemplate(jndiTemplate());
return destinationResolver;
}
#Bean
public JndiObjectFactoryBean connectionFactory(){
JndiObjectFactoryBean cf = new JndiObjectFactoryBean();
cf.setJndiTemplate(jndiTemplate());
cf.setJndiName(jmsPropertiesConfig.getFactory());
return cf;
}
#Bean
public JmsTemplate jmsTemplate(){
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory((ConnectionFactory) connectionFactory().getObject());
template.setSessionAcknowledgeModeName("AUTO_ACKNOWLEDGE");
template.setSessionTransacted(true);
template.setDestinationResolver(jmsDestionationProvider());
return template;
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory((ConnectionFactory) connectionFactory().getObject());
factory.setDestinationResolver(jmsDestionationProvider());
factory.setErrorHandler(jmsErrorHandler);
factory.setSessionAcknowledgeMode(0);
return factory;
}
Receive:
#JmsListener(destination = "jms/test")
public void receiveApplicationNotification(String input) throws Exception {
log.info("Receiving message from jms.external.ExampleQueue.queue "+input);
}
Seems to be a bug in Weblogic.
Can you please try applying below patch..
Bug 22550927 - WEBLOGIC JMS CONNECTION IS NOT THREAD-SAFE

Spring Batch not deserialising dates

I'm adding an object in the JobExecution context of spring batch, which contains an Instant field.
It's getting serialised as follows:
{
"startFrom": {
"nano": 0,
"epochSecond": 1541116800
}
}
However, Spring Batch doesn't seem to be able to deserialise it.
Caused by: java.lang.IllegalArgumentException: Unable to deserialize the execution context
at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao$ExecutionContextRowMapper.mapRow(JdbcExecutionContextDao.java:325)
at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao$ExecutionContextRowMapper.mapRow(JdbcExecutionContextDao.java:309)
at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:93)
at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:60)
at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:667)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:605)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:657)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:688)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:700)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:756)
at org.springframework.batch.core.repository.dao.JdbcExecutionContextDao.getExecutionContext(JdbcExecutionContextDao.java:112)
at org.springframework.batch.core.explore.support.SimpleJobExplorer.getJobExecutionDependencies(SimpleJobExplorer.java:202)
at org.springframework.batch.core.explore.support.SimpleJobExplorer.getJobExecutions(SimpleJobExplorer.java:83)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at org.springframework.aop.framework.ReflectiveMethod
When doing some research I see that Jackson has a JavaTimeModule to serialise/deserialise Instant and other date classes.
However, in the Jackson2ExecutionContextStringSerializer class, it creates the ObjectMapper as follows, not registering the right module:
public Jackson2ExecutionContextStringSerializer() {
this.objectMapper = new ObjectMapper();
this.objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
this.objectMapper.enableDefaultTyping();
this.objectMapper.registerModule(new JobParametersModule());
}
Is there a reason for Spring Batch not to use the autowired ObjectMapper? Or a reason for them not to register the JavaTimeModule?
Is there maybe a workaround this issue?
Thanks!
Edit:
I've found how to overwrite this object mapper:
#Bean
public JobRepository createJobRepository() throws Exception {
ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()).findAndRegisterModules();
Jackson2ExecutionContextStringSerializer defaultSerializer = new Jackson2ExecutionContextStringSerializer();
defaultSerializer.setObjectMapper(objectMapper);
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.setSerializer(defaultSerializer);
factory.afterPropertiesSet();
return factory.getObject();
}
However, even with this, the issue persists.
The following worked for me. I am extending Mahmoud Ben Hassine's answer above which didn't work for Nicolas Widart. (I don't have enough reputation to just comment).
#Bean
public BatchConfigurer configurer(DataSource dataSource, PlatformTransactionManager transactionManager,
ObjectMapper objectMapper) {
return new DefaultBatchConfigurer(dataSource) {
final Jackson2ExecutionContextStringSerializer serializer = new Jackson2ExecutionContextStringSerializer();
#Override
protected JobRepository createJobRepository() throws Exception {
serializer.setObjectMapper(objectMapper);
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.setSerializer(serializer);
factory.afterPropertiesSet();
return factory.getObject();
}
#Override
protected JobExplorer createJobExplorer() throws Exception {
serializer.setObjectMapper(objectMapper);
JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
jobExplorerFactoryBean.setSerializer(serializer);
jobExplorerFactoryBean.setDataSource(dataSource);
jobExplorerFactoryBean.afterPropertiesSet();
return jobExplorerFactoryBean.getObject();
}
};
}
(I have defined an ObjectMapper bean in another #Configuration class.) The important thing here is to also override the createJobExplorer() method and set the correct ExecutionContextSerializer there, as this method will call getTarget() on the JobExplorerFactoryBean, which will set a vanilla ExecutionContextSerializer if there is none set and which causes the error you described.
The code you showed is the default initialization of the serializer. You can override it by providing a custom object mapper that you pre-configure with the modules you need. Here is an example:
#Bean
public JobRepository jobRepository() throws Exception {
ObjectMapper objectMapper = null; // configure the object mapper as required
Jackson2ExecutionContextStringSerializer serializer = new Jackson2ExecutionContextStringSerializer();
serializer.setObjectMapper(objectMapper);
JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
jobRepositoryFactoryBean.setSerializer(serializer);
// set other properties on the jobRepositoryFactoryBean
jobRepositoryFactoryBean.afterPropertiesSet();
return jobRepositoryFactoryBean.getObject();
}
Hope this helps.
UPDATE
Spring batch now has a fix that allows you to customise your ObjectMapper while also getting all of the default setup that Jackson2ExecutionContextStringSerialier does internally after this issue was addressed: https://jira.spring.io/browse/BATCH-2828.
The fix is in 4.2.0 which at time of writing is currently at RC1.
Original answer
I've noticed similar problems. I need to customise ObjectMapper to add KotlinModule for dealing with kotlin data classes. Unfortunately, the way Jackson2ExecutionContextStringSerializer is currently written is not very extensible. As you have seen from the default ObjectMapper that was created there are some defaults that were added, including a fix for deserialisation issues with job parameters: https://jira.spring.io/browse/BATCH-2680.
The JobParametersModule they setup is a private class and as such we cannot setup ObjectMapper with the same features and add to it.
I have raised an issue for this in Jira: https://jira.spring.io/browse/BATCH-2828
The dirty workaround until this is fixed is to copy and paste the source code for JobParmetersModule so you can register the same in your override.

With Spring boot and integration DSL, getting error ClassNotFoundException integration.history.TrackableComponent

Trying a very basic JMS receiver using Spring Boot, Integration and DSL. I have worked on XML based on Spring Integration, but am new to Spring Boot and DSL.
This is a code sample that I have so far
#SpringBootApplication
#IntegrationComponentScan
#EnableJms
public class JmsReceiver {
static String mailboxDestination = "RETRY.QUEUE";
#Configuration
#EnableJms
#IntegrationComponentScan
#EnableIntegration
public class MessageReceiver {
#Bean
public IntegrationFlow jmsMessageDrivenFlow() {
return IntegrationFlows
.from(Jms.messageDriverChannelAdapter(this.connectionFactory())
.destination(mailboxDestination))
.transform((String s) -> s.toUpperCase())
.get();
}
//for sneding message
#Bean
ConnectionFactory connectionFactory() {
ActiveMQConnectionFactory acFac = new ActiveMQConnectionFactory();
acFac.setBrokerURL("tcp://crsvcdevlnx01:61616");
acFac.setUserName("admin");
acFac.setPassword("admin");
return new CachingConnectionFactory(acFac);
}
}
//Message send code
public static void main(String args[]) throws Throwable {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(JmsReceiver.class);
JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
System.out.println("Sending a new mesage.");
MessageCreator messageCreator = new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage("ping!");
}
};
jmsTemplate.send(mailboxDestination, messageCreator);
context.close();
}
}
And, I get this error when running with Gradle.
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.integration.dsl.IntegrationFlow]: Factory method 'inboundFlow' threw exception; nested exception is java.lang.NoClassDefFoundError: org/springframework/integration/history/TrackableComponent
reflect.NativeMethodAccessorImpl.invoke0(Native Method)
.
.
.
Caused by: java.lang.ClassNotFoundException: org.springframework.integration.history.TrackableComponent
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
My gradle dependencies:
compile "org.springframework.boot:spring-boot-starter-jersey",
"org.springframework.boot:spring-boot-starter-actuator",
"org.springframework.boot:spring-boot-configuration-processor",
"org.springframework.boot:spring-boot-starter-integration",
"org.springframework.integration:spring-integration-jms",
"org.springframework.integration:spring-integration-java-dsl:1.1.1.RELEASE",
"org.springframework.integration:spring-integration-flow:1.0.0.RELEASE",
"org.springframework.integration:spring-integration-core:4.2.2.RELEASE",
"org.springframework.integration:spring-integration-java-dsl:1.1.0.RELEASE",
"org.springframework.integration:spring-integration-flow:1.0.0.RELEASE",
"org.apache.activemq:activemq-spring:5.11.2",
UPDATE.. SOLVED: Thanks much. Changed two things:
Cleaned up gradle dependencies based on your advice. New ones looks like this:
compile "org.springframework.boot:spring-boot-starter-jersey",
"org.springframework.boot:spring-boot-starter-actuator",
"org.springframework.boot:spring-boot-configuration-processor",
"org.springframework.boot:spring-boot-starter-integration",
"org.springframework.integration:spring-integration-jms",
"org.springframework.integration:spring-integration-java-dsl:1.1.0.RELEASE",
"org.apache.activemq:activemq-spring:5.11.2"
Code was throwing constructor error about not being able to instantiate <init> in the inner class. Changed the Inner class to static. New Code:
#SpringBootApplication
#IntegrationComponentScan
#EnableJms
public class JmsReceiver {
static String lsamsErrorQueue = "Queue.LSAMS.retryMessage";
static String fatalErrorsQueue = "Queue.LSAMS.ManualCheck";
//receiver
#EnableJms
#EnableIntegration
#Configuration
public static class MessageReceiver {
#Bean
public IntegrationFlow jmsMessageDrivenFlow() {
return IntegrationFlows
.from(Jms.messageDriverChannelAdapter(this.connectionFactory())
.destination(lsamsErrorQueue))
//call LSAMS REST service with the payload received
.transform((String s) -> s.toUpperCase())
.handle(Jms.outboundGateway(this.connectionFactory())
.requestDestination(fatalErrorsQueue))
.get();
}
#Bean
ConnectionFactory connectionFactory() {
ActiveMQConnectionFactory acFac = new ActiveMQConnectionFactory();
acFac.setBrokerURL("tcp://crsvcdevlnx01:61616");
acFac.setUserName("admin");
acFac.setPassword("admin");
return new CachingConnectionFactory(acFac);
}
}
//Message send code
public static void main(String args[]) throws Throwable {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(JmsReceiver.class);
JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
System.out.println("Sending a new mesage.");
MessageCreator messageCreator = new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage("ping!");
}
};
jmsTemplate.send(lsamsErrorQueue, messageCreator);
context.close();
}
}
Well, that fully looks like you have a version mess in your classpath.
First of all you shouldn't mix the same artifacts manually, like you have with spring-integration-java-dsl and spring-integration-flow. BTW, do you really need the last one?.. I mean is there some reason to keep spring-integration-flow? This project is about Modular Flows.
From other side you don't need to specify spring-integration-core if you are based on the Spring Boot (spring-boot-starter-integration in your case).
And yes: the TrackableComponent has been moved to the org.springframework.integration.support.management since Spring Integration 4.2 (https://jira.spring.io/browse/INT-3799).
From here it looks like you use the older Spring Integration version somehow:
- or Spring Boot 1.2.x
- or it is really side-effect of transitive dependency from spring-integration-flow...

Spring Boot Using Embedded Tomcat with JNDI

I am using Spring Boot with Embedded Tomcat and attempting to use JNDI but getting the following error:
javax.naming.NameNotFoundException: Name [jdbc/dataSource]
Any tips would be greatly appreciated.
Here is my code:
#Configuration
public class TomcatJndiConfiguration{
#Value("${database.driver}")
private String driverClassName;
#Value("${database.url}")
private String databaseUrl;
#Value("${database.username}")
private String databaseUsername;
#Value("${database.password}")
private String databasePassword;
#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/dataSource");
resource.setType(DataSource.class.getName());
resource.setProperty("driverClassName", driverClassName);
resource.setProperty("url", databaseUrl);
resource.setProperty("password", databaseUsername);
resource.setProperty("username", databasePassword);
context.getNamingResources().addResource(resource);
}
};
}
#Bean
public DataSource dataSource() throws IllegalArgumentException, NamingException {
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName("jdbc/dataSource");
bean.setLookupOnStartup(true);
bean.setProxyInterface(DataSource.class);
bean.setResourceRef(true);
bean.afterPropertiesSet();
return (DataSource) bean.getObject();
}
Stacktrace is:
Caused by: javax.naming.NameNotFoundException: Name [jdbc/dataSource] is not bound in this Context. Unable to find [jdbc].
at org.apache.naming.NamingContext.lookup(NamingContext.java:818)
at org.apache.naming.NamingContext.lookup(NamingContext.java:166)
at org.apache.naming.SelectorContext.lookup(SelectorContext.java:157)
at javax.naming.InitialContext.lookup(InitialContext.java:417)
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.JndiObjectTargetSource.afterPropertiesSet(JndiObjectTargetSource.java:97)
at org.springframework.jndi.JndiObjectFactoryBean$JndiObjectProxyFactory.createJndiObjectProxy(JndiObjectFactoryBean.java:318)
at org.springframework.jndi.JndiObjectFactoryBean$JndiObjectProxyFactory.access$000(JndiObjectFactoryBean.java:307)
at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:200)
at com.kronos.daas.configuration.TomcatJndiConfiguration.dataSource(TomcatJndiConfiguration.java:72)
You need to set lookupOnStartup to false on the JndiObjectFactoryBean.
Alternatively, if you need the lookup to work during startup, then this answer may be of interest.
Edit: you've also set the JNDI name on your JndiObjectFactory bean incorrectly. It needs to be java:comp/env/jdbc/myDataSource not jdbc/dataSource.
You use a different name when you're looking up the resource versus when you registered it as the registration automatically places the resource beneath java:comp/env/.
If you are using spring boot, no need for all of that class.
It is already configured in #EnableAutoConfiguration or
#SpringBootApplication
Just put the following in your application.properties file or equivalent in application.yml file
spring.datasource.driverClassName=JDBCDriverClassName
spring.datasource.url=databaseUrl
spring.datasource.username=databaseUsername
spring.datasource.password=databasePassword
spring.datasource.jndi-name=java:jdbc/dataSource

Resources