I have a java app with redis, and it throws the exception.
Here are classes.
Main class:
public class App {
public static void main( String[] args ) {
ManipulatingData manData = new ManipulatingData();
manData.addData();
}
}
ApplicationConfig:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.mycompany.springredisdatabook;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
#Configuration
public class ApplicationConfig {
#Bean
public JedisConnectionFactory connectionFactory() {
JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
connectionFactory.setHostName("localhost");
connectionFactory.setPort(6379);
return connectionFactory;
}
#Bean
public StringRedisTemplate redisTemplate() {
StringRedisTemplate redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(connectionFactory());
return redisTemplate;
}
#Bean
public RedisTemplate<String, Long> longTemplate() {
StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer();
RedisTemplate<String, Long> redisTemplate = new RedisTemplate<String, Long>();
redisTemplate.setConnectionFactory(connectionFactory());
redisTemplate.setKeySerializer(STRING_SERIALIZER);
redisTemplate.setValueSerializer(LongSerializer.INSTANCE);
return redisTemplate;
}
}
ManipulatingData:
package com.mycompany.springredisdatabook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
public class ManipulatingData {
public ManipulatingData() {}
#Autowired
StringRedisTemplate redisTemplate;
public void addData() {
double start = System.currentTimeMillis();
for (int i=1; i<=1000; i++) {
redisTemplate.opsForSet().add("k" + i, "v" + i);
}
double end = System.currentTimeMillis();
System.out.println("Add data time: " + (end-start));
}
public String getData (String key) {
return redisTemplate.opsForValue().get(key);
}
public void deleteData(String key) {
redisTemplate.opsForValue().getOperations().delete(key);
}
}
The Exception:
Exception in thread "main" java.lang.NullPointerException
at com.mycompany.springredisdatabook.ManipulatingData.addData(ManipulatingData.java:25)
at com.mycompany.springredisdatabook.App.main(App.java:11)
Java Result: 1
So, what is it? I have no idea. I'm using spring, by the way
your redisTemplate instance is null. check your spring.xml for the configuration of redisTemplate and possibilities which causes it to be null
Related
I'm at a loss. I upgraded an application from Spring Boot 2.1 to 2.6 and from Wicket 8.0 to 9.6. I had two issues with circular-references that I fixed but now I get an infinite loop if I want to start the application with H2 database because of authenticate. And I'm not sure what is happening there.
So this is the part of the StackTrace that keeps repeating. IntelliJ cuts off the beginning, not sure what to do about that:
at com.xyz.ufa.app.TestUFSession.<init>(TestUFSession.java:15)
at jdk.internal.reflect.GeneratedConstructorAccessor102.newInstance(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at org.apache.wicket.authroles.authentication.AuthenticatedWebApplication.newSession(AuthenticatedWebApplication.java:108)
at org.apache.wicket.Application.fetchCreateAndSetSession(Application.java:1527)
at org.apache.wicket.Session.get(Session.java:194)
at org.apache.wicket.protocol.http.WebSession.get(WebSession.java:41)
at com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.security.SecureWebSession.authenticate(SecureWebSession.java:48)
at org.apache.wicket.authroles.authentication.AuthenticatedWebSession.signIn(AuthenticatedWebSession.java:66)
at com.xyz.ufa.app.TestUFSession.<init>(TestUFSession.java:15)
Here is the TestUFSession.class
import com.xyz.ufa.frontend.config.UFSession;
import org.apache.wicket.request.Request;
/**
* Helper session to login autmatically.
*
*/
public class TestUFSession extends UFSession {
public TestUFSession(Request request) {
super(request);
signIn("admin", "admin"); // this calls authenticate
}
}
And here the UFSession class
import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.security.SecureWebSession;
import java.util.Locale;
import lombok.Getter;
import org.apache.wicket.request.Request;
#Getter
public class UFSession extends SecureWebSession {
private String username;
private Locale locale;
public UFSession(Request request) {
super(request);
locale = request.getLocale();
}
#Override
public void signOut() {
username = null;
super.signOut();
}
}
And here the WebSecurityConfiguration class
import com.xyz.uf.common.ApplicationProfile;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.search.LdapUserSearch;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.LdapUserDetailsService;
#Slf4j
#Configuration
#Import(LdapProperties.class)
#ComponentScan(basePackages = {"com.xyz.ufa.security.userinfo"})
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final String AUTHENTICATION_PROVIDER_BEAN_NAME = "authenticationProvider";
#Autowired
private LdapProperties ldapProperties;
#Bean(name = "authenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws AuthenticationServiceException {
try {
return super.authenticationManagerBean();
} catch (Exception e) {
log.error("Error in authenticationManagerBean", e);
throw new AuthenticationServiceException(e.getMessage(), e);
}
}
#Override
protected void configure(HttpSecurity httpSecurity) throws AuthenticationServiceException {
try {
httpSecurity
.csrf().disable()
.authorizeRequests()
.antMatchers("/*").permitAll()
.antMatchers("/restservice/**").hasAuthority(UFARole.TECHNICAL_ADMIN)
.and().httpBasic()
.and().logout().permitAll();
httpSecurity.headers().frameOptions().disable();
} catch (Exception e) {
throw new AuthenticationServiceException(String.format("Could not configure %s with csrf disabled and matching Pattern /*.", httpSecurity), e);
}
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
try {
AbstractLdapAuthenticationProvider authentProvider = (AbstractLdapAuthenticationProvider) getApplicationContext().getBean(AUTHENTICATION_PROVIDER_BEAN_NAME);
authentProvider.setAuthoritiesMapper(authoritiesMapper());
DaoAuthenticationConfigurer<AuthenticationManagerBuilder, LdapUserDetailsService> configurer = authenticationManagerBuilder
.authenticationProvider(authentProvider)
.userDetailsService(ldapUserDetailsService());
passwordEncoder.ifPresent(configurer::passwordEncoder);
} catch (Exception e) {
throw new AuthenticationServiceException("Could not configure authentication manager ", e);
}
}
#Bean
public LdapUserDetailsService ldapUserDetailsService() {
LdapUserDetailsService userDetailsService = new LdapUserDetailsService(userSearch(), ldapAuthoritiesPopulator());
return userDetailsService;
}
#Bean
public LdapUserSearch userSearch() {
return new FilterBasedLdapUserSearch(ldapProperties.getUserSearchBase(), ldapProperties.getUserSearchFilter(), contextSource());
}
#Bean
public GrantedAuthoritiesMapper authoritiesMapper() {
return new GrantAuthoritiesMapperWithEnvTag(ldapProperties.getEnv());
}
#Bean
public LdapContextSource contextSource() {
LdapContextSource ldapContextSource = new LdapContextSource();
ldapContextSource.setUrl(ldapProperties.getServerUrl());
ldapContextSource.setAnonymousReadOnly(true);
return ldapContextSource;
}
#Bean
public LdapAuthoritiesPopulator ldapAuthoritiesPopulator() {
DefaultLdapAuthoritiesPopulator ldapAuthoritiesPopulator = new DefaultLdapAuthoritiesPopulator(contextSource(), ldapProperties.getGroupSearchBase());
ldapAuthoritiesPopulator.setGroupSearchFilter(ldapProperties.getGroupSearchFilter());
ldapAuthoritiesPopulator.setGroupRoleAttribute(ldapProperties.getGroupRoleAttribute());
ldapAuthoritiesPopulator.setRolePrefix("");
ldapAuthoritiesPopulator.setConvertToUpperCase(false);
return ldapAuthoritiesPopulator;
}
#Bean(name = AUTHENTICATION_PROVIDER_BEAN_NAME)
#Profile(value = { ApplicationProfile.Values.TEST, ApplicationProfile.Values.PROD })
public AuthenticationProvider activeDirectory() {
ActiveDirectoryLdapAuthenticationProvider authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider("HRE.LOC", ldapProperties.getServerUrl());
authenticationProvider.setSearchFilter(ldapProperties.getUserSearchFilter());
return authenticationProvider;
}
#Bean(name = AUTHENTICATION_PROVIDER_BEAN_NAME)
#Profile(value = { ApplicationProfile.Values.DEFAULT, ApplicationProfile.Values.DEV })
public AuthenticationProvider defaultAuthenticationProvider() {
PasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator(contextSource());
authenticator.setPasswordAttributeName("userPassword");
passwordEncoder.ifPresent(authenticator::setPasswordEncoder);
authenticator.setUserSearch(userSearch());
LdapAuthenticationProvider authenticationProvider = new LdapAuthenticationProvider(authenticator, ldapAuthoritiesPopulator());
return authenticationProvider;
}
/**
* This bean is optional and not available for some profiles. Password encoder is only required for embedded LDAP, for productive Active Directory it is not used
*/
#Bean("passwordEncoder")
#Profile(value = { ApplicationProfile.Values.DEFAULT, ApplicationProfile.Values.DEV })
public static PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Autowired(required = false)
#Qualifier("passwordEncoder")
private Optional<PasswordEncoder> passwordEncoder;
}
Profile used for test is default.
Anyone any ideas?
Edit: So Martin helped me to understand the problem, but I'm too dumb to fix it. TestUFApplication is instantiated here via ReflectionTestUtils
ยดยดยดยด
#SpringBootApplication
public class TestApplicationWithH2Database {
#Autowired
private UFyWicketWebApplication webApplication;
#Value("${test.autologin.active}")
private boolean testAutologinActive;
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(Application.class)
.run(args);
}
#PostConstruct
public void PostConstruct() {
if (testAutologinActive) {
ReflectionTestUtils.setField(webApplication, "sessionClass", TestUFSession.class);
}
}
}
I tried to make signIn() a method in TestUFSession and call it like that
TestUFSession testUFSession = (TestUFSession) ReflectionTestUtils.getField(webApplication, "sessionClass");
testUFSession.signIn();
but got a ClassCastException
You will need to call signIn("admin", "admin"); after instantiating the TestUFSession.
From the stacktrace we can see that com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.security.SecureWebSession.authenticate(SecureWebSession.java:48) tries to lookup the WebSession via its static #get() method and that leads to the infinite loop.
when I using this code to acknowlege redis(v6.2.5) stream items in java 11 spring boot project:
#Override
public void onMessage(MapRecord<String, String, String> message) {
try {
log.debug("receive message from redis:" + JSON.toJSONString(body));
this.stringRedisTemplate.opsForStream().acknowledge(groupName, message);
} catch (Exception e) {
log.error("handle redis stream message error", e);
}
}
the stream element still exists in redis after running this code to acknowlege items, am I doing the wrong? what should I do to acknowlege the redis stream elements? This is the full code that consume element from redis streams:
package com.dolphin.soa.post.common.mq;
import com.alibaba.fastjson.JSON;
import com.dolphin.soa.post.service.IArticleService;
import com.dolphin.soa.post.service.ISubRelationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.stream.StreamListener;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* #author dolphin
*/
#Component
#Slf4j
public class StreamMessageListener implements StreamListener<String, MapRecord<String, String, String>> {
#Value("${dolphin.redis.stream.group}")
private String groupName;
#Autowired
private StreamMessageHandler streamMessageHandler;
private final StringRedisTemplate stringRedisTemplate;
private final RedisTemplate<String, Object> articleRedisTemplate;
private final RedisTemplate<String, Long> redisLongTemplate;
private final ISubRelationService subRelationService;
private final IArticleService articleService;
public StreamMessageListener(StringRedisTemplate stringRedisTemplate,
#Qualifier("redisObjectTemplate") RedisTemplate<String, Object> articleRedisTemplate,
ISubRelationService subRelationService,
#Qualifier("redisLongTemplate") RedisTemplate<String, Long> redisLongTemplate,
IArticleService articleService) {
this.stringRedisTemplate = stringRedisTemplate;
this.articleRedisTemplate = articleRedisTemplate;
this.subRelationService = subRelationService;
this.redisLongTemplate = redisLongTemplate;
this.articleService = articleService;
}
#Override
public void onMessage(MapRecord<String, String, String> message) {
try {
Map<String, String> body = message.getValue();
log.debug("receive message from redis:" + JSON.toJSONString(body));
streamMessageHandler.handleArticle(body);
this.stringRedisTemplate.opsForStream().acknowledge(groupName, message);
} catch (Exception e) {
log.error("handle redis stream message error", e);
}
}
}
I have two database structures, example:
1- MAIN_DATABASE: USER PASSWORD
2: CUSTOMER DATABASE: CUSTOMER_A CUSTOMER_B CUSTOMER_C
I want to access the main database and after validating the data, redirect to the customer database.
I currently use spring and configure it in applicationContext.xml
Example:
<bean id = "encryptionPassword" class = "utils.EncryptionPasswordSpring" />
<bean id = "dataSource" class = "com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method = "close">
<property name = "driverClass" value = "com.mysql.jdbc.Driver" />
<property name = "user" value = "user" />
<property name = "password" value = "123456" />
<property name = "jdbcUrl" value = "jdbc:mysql://localhost/testdb?useSSL = false" />
</bean>
Any example, suggestion? Thanks.
The below is my code for dynamic datasource with mybatis. one is main ds. another is read ds. Hope it useful to you.
use AbstractRoutingDataSource to define a DynamicDataSource
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
#Override
protected DataSource determineTargetDataSource() {
return super.determineTargetDataSource();
}
/**
*/
#Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceKey();
}
/**
* #param defaultDataSource
*/
public void setDefaultDataSource(Object defaultDataSource) {
super.setDefaultTargetDataSource(defaultDataSource);
}
/**
* #param dataSources
*/
public void setDataSources(Map<Object, Object> dataSources) {
super.setTargetDataSources(dataSources);
DynamicDataSourceContextHolder.addDataSourceKeys(dataSources.keySet());
}
}
use ThreadLocal to switch datasource in context
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
/**
make main as default ds
*/
#Override
protected String initialValue() {
return "main";
}
};
/**
*
*/
private static List<Object> dataSourceKeys = Collections.synchronizedList(new ArrayList<>());
/**
* switch ds
*
* #param key
*/
public static void setDataSourceKey(String key) {
contextHolder.set(key);
}
/**
* get ds
*
* #return
*/
public static String getDataSourceKey() {
return contextHolder.get();
}
/**
* reset ds
*/
public static void clearDataSourceKey() {
contextHolder.remove();
}
/**
* judge if ds existed
*
* #param key
* #return
*/
public static boolean containDataSourceKey(String key) {
return dataSourceKeys.contains(key);
}
/**
* add ds
*
* #param keys
* #return
*/
public static boolean addDataSourceKeys(Collection<? extends Object> keys) {
return dataSourceKeys.addAll(keys);
}
}
inject different datasource via application.yml or application.properties
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
#Configuration
public class DataSourceConfig {
#Primary
#Bean
#ConfigurationProperties("spring.datasource.main")
public DataSource main() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties("spring.datasource.read")
public DataSource read() {
return DataSourceBuilder.create().build();
}
#Bean
public DataSource dynamicDataSource(
#Qualifier("main") DataSource main,
#Qualifier("read") DataSource read
) {
Map<Object, Object> targetDataSources = new HashMap<>(2);
targetDataSources.put("main", main);
targetDataSources.put("read", read);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setDefaultTargetDataSource(main); //default
dynamicDataSource.setDataSources(targetDataSources);
return dynamicDataSource;
}
#Bean
public SqlSessionFactory sqlSessionFactory(
#Qualifier("dynamicDataSource") DataSource dynamicDataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dynamicDataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mappings/**/*.xml"));
return bean.getObject();
}
#Bean(name = "sqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(
#Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory)
throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
#Bean
public PlatformTransactionManager transactionManager(
#Qualifier("dynamicDataSource") DataSource dynamicDataSource
) {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
define an AOP to control which Dao method use which datasource. Dao is interface to access DB via mybatis.
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
#Aspect
#Order(-1)
#Component
public class DynamicDataSourceAspect {
private final static Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
private final String[] QUERY_PREFIX = {
"select","get","find","query","quickGet"
};
#Pointcut("execution( * com.biz.dao..*.*(..))")
public void daoAspect() {
}
#Before("daoAspect()")
public void beforeDao(JoinPoint point) {
boolean isQueryMethod = isQueryMethod(point.getSignature().getName());
if (isQueryMethod) {
switchDataSource("read");
}
}
#After("daoAspect()")
public void afterDao(JoinPoint point) {
restoreDataSource();
}
//===============================private method
private void switchDataSource(String key) {
if (!DynamicDataSourceContextHolder.containDataSourceKey(key)) {
logger.debug("======>DataSource [{}] doesn't exist, use default DataSource [{}] " + key);
} else {
// switch ds
DynamicDataSourceContextHolder.setDataSourceKey(key);
logger.debug("======>Switch DataSource to " + DynamicDataSourceContextHolder.getDataSourceKey());
}
}
private void restoreDataSource() {
// reset to default ds
DynamicDataSourceContextHolder.clearDataSourceKey();
}
private boolean isQueryMethod(String methodName) {
for (String prefix : QUERY_PREFIX) {
if (methodName.startsWith(prefix)) {
return true;
}
}
return false;
}
}
Configure two beans with two different set of configs in app.props
For one configuration you can use this (beanName = dataSource1):
#Configuration
#EnableRetry
public class DataSourceConfiguration {
#Value("${datasource1.username}")
private String username;
#Value("${datasource1.password}")
private String password;
#Value("${datasource1.url}")
private String connection;
#Bean(name = "dataSource1")
#Primary
public DataSource mainDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl(connection);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
These are my classes and configurations,
ServiceImpl.java
#Override
#Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public InsertUpdateSuccessBean insertNewSiteEntry(SiteEntry newSiteBean) throws SQLException {
dao.insert1();
dao.insert2();
except();
return new InsertUpdateSuccessBean(true,LoggerConstants.NEW_SITE_ENTRY_SUCCESS);
}
private void except()throws SQLException{
throw new SQLException();
}
DAOImpl.java
#Repository
public class DAOImpl implements DAO
{
#Autowired
private DataSourceTransactionManager transManager;
#Autowired
private CommonDAOUtils commonUtils;
private static final Logger logger = Logger.getLogger(HunterDAOImpl.class) ;
#Override
public Integer insert1() throws SQLException {
Integer insertNumRows = 0;
Connection connectionObject = transManager.getDataSource().getConnection();
PreparedStatement psObject = connectionObject.prepareStatement(
SQLQuery );
insertNumRows = psObject.executeUpdate();
commonUtils.closer(null,psObject,connectionObject);
// Function to close open connection resultsets statement objects
return insertNumRows;
}
#Override
public Integer insert2() throws SQLException {
Integer insertNumRows = 0;
Connection connectionObject = transManager.getDataSource().getConnection();
PreparedStatement psObject = connectionObject.prepareStatement(
SQLQuery );
insertNumRows = psObject.executeUpdate();
commonUtils.closer(null,psObject,connectionObject);
// Function to close open connection resultsets statement objects
return insertNumRows;
}
}
AppConfig.java
import com.interceptors.AppInterceptor;
import com.utils.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Component
#Configuration
#EnableWebMvc
#Configurable
#ComponentScan(basePackages = {Constants.BASE_PACKAGE})
#EnableAspectJAutoProxy(proxyTargetClass = true)
#PropertySource(Constants.DB_PROPERTIES_PATH)
#EnableTransactionManagement
public class AppConfig extends WebMvcConfigurerAdapter implements EnvironmentAware{
#Autowired
Environment environmentObject;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
PropertySourcesPlaceholderConfigurer placeHolder =new PropertySourcesPlaceholderConfigurer();
placeHolder.setLocation(new ClassPathResource(Constants.PROP_FILE_NAME));
return placeHolder;
}
#Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
internalResourceViewResolver.setPrefix(Constants.ROOT_PATH);
internalResourceViewResolver.setSuffix(Constants.JSP_DOT_EXTENSION);
return internalResourceViewResolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public DriverManagerDataSource dataSource(){
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(environmentObject.getProperty(Constants.JDBC_DRIVERCLASS_NAME_PROP_FILE));
driverManagerDataSource.setUrl(environmentObject.getProperty(Constants.JDBC_URL_PROP_FILE));
driverManagerDataSource.setUsername(environmentObject.getProperty(Constants.JDBC_USERNAME_PROP_FILE));
driverManagerDataSource.setPassword(new String(Constants.PASSWORD));
return driverManagerDataSource;
}
#Bean
public AutowiredAnnotationBeanPostProcessor postProcessorBean(){
return new AutowiredAnnotationBeanPostProcessor();
}
private ClientHttpRequestFactory getClientHttpRequestFactory() {
int timeout = 5000;
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setConnectTimeout(timeout);
return clientHttpRequestFactory;
}
#Bean
public DataSourceTransactionManager transactionManager(){
final DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource());
transactionManager.setRollbackOnCommitFailure(true);
return transactionManager;
}
}
And I also have an around advice for my project:
#Aspect
#Component
public class AroundAdvice
{
private static final Logger logger = Logger.getLogger(AroundAdvice.class) ;
// Add pointcuts here - package names here for around advice
#Around("execution(* com.beans.*.*(..)) || " +
"execution(* com.config.*.*(..)) || " +
"execution(* com.controller.*.*(..)) || " +
"execution(* com.dao.*.*(..)) || " +
"execution(* com.service.*.*(..)) || " +
"execution(* com.utils.*.*(..)) || "+
"execution(* com.interceptors.*.*(..))")
public Object aroundAdviceMethod(ProceedingJoinPoint proceedingObject)throws Throwable{
MethodSignature methodSignObject = (MethodSignature) proceedingObject.getSignature();
logger.debug(Constants.EXECUTING_METH_STR + methodSignObject.getMethod());
Object value = null;
try {
value = proceedingObject.proceed();
} catch (Exception e) {
e.printStackTrace();
logger.error(Constants.EXCEPTION_ASPECT_STR+methodSignObject.getMethod());
logger.error(Constants.EXCEPTION_MESSAGE,e);
throw e;
}
logger.debug(Constants.RETURN_STR+value);
return value;
}
}
On executing this flow, the inserts are successful, however when the exception is thrown, it is not rolling back. However, my logger reads that rolling back is initialized and done as follows,
14:11:51 DEBUG DataSourceTransactionManager:851 - Initiating transaction rollback
14:11:51 DEBUG DataSourceTransactionManager:325 - Rolling back JDBC transaction on Connection [org.postgresql.jdbc4.Jdbc4Connection#3b467b21]
14:11:51 DEBUG DataSourceTransactionManager:368 - Releasing JDBC Connection [org.postgresql.jdbc4.Jdbc4Connection#3b467b21] after transaction
Please let me know if I am missing something
The problem is your dao. You are opening connections yourself and therefor bypass all transaction management. Your dao is to complex just use a JdbcTemplate instead of your current code.
#Repository
public class DAOImpl implements DAO {
private static final Logger logger = Logger.getLogger(HunterDAOImpl.class) ;
private final JdbcTemplate jdbc;
public DAOImpl(DataSource dataSource) {
this.jdbc = new JdbcTemplate(dataSource);
}
#Override
public Integer insert1() throws SQLException {
return jdbc.update(SQLQuery);
}
#Override
public Integer insert2() throws SQLException {
return jdbc.update(SQLQuery);
}
}
This will do exactly the same as your code, with one main difference it will use the Connection opened when the transaction was started. Your sample used 3 separate connections and thus 3 individual transactions instead of 1 single transaction.
I am newbie to Spring Integration. I am working on solution, but I am stuck on a specific issue while using inbound file adapter ( FileReadingMessageSource ).
I have to read files from different directories and process them and save the files in different directories. As I understand, the directory name is fixed at the start of the flow.
Can some one help me on changing the directory name for different requests.
I attempted the following. First of all, I am not sure whether it is correct way to about and although it worked for only one directory. I think Poller was waiting for more files and never came back to read another directory.
#SpringBootApplication
#EnableIntegration
#IntegrationComponentScan
public class SiSampleFileProcessor {
#Autowired
MyFileProcessor myFileProcessor;
#Value("${si.outdir}")
String outDir;
#Autowired
Environment env;
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext ctx = new SpringApplication(SiSampleFileProcessor.class).run(args);
FileProcessingService gateway = ctx.getBean(FileProcessingService.class);
boolean process = true;
while (process) {
System.out.println("Please enter the input Directory: ");
String inDir = new Scanner(System.in).nextLine();
if ( inDir.isEmpty() || inDir.equals("exit") ) {
process=false;
} else {
System.out.println("Processing... " + inDir);
gateway.processFilesin(inDir);
}
}
ctx.close();
}
#MessagingGateway(defaultRequestChannel="requestChannel")
public interface FileProcessingService {
String processFilesin( String inputDir );
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller() {
return Pollers.fixedDelay(1000).get();
}
#Bean
public MessageChannel requestChannel() {
return new DirectChannel();
}
#ServiceActivator(inputChannel = "requestChannel")
#Bean
GenericHandler<String> fileReader() {
return new GenericHandler<String>() {
#Override
public Object handle(String p, Map<String, Object> map) {
FileReadingMessageSource fileSource = new FileReadingMessageSource();
fileSource.setDirectory(new File(p));
Message<File> msg;
while( (msg = fileSource.receive()) != null ) {
fileInChannel().send(msg);
}
return null; // Not sure what to return!
}
};
}
#Bean
public MessageChannel fileInChannel() {
return MessageChannels.queue("fileIn").get();
}
#Bean
public IntegrationFlow fileProcessingFlow() {
return IntegrationFlows.from(fileInChannel())
.handle(myFileProcessor)
.handle(Files.outboundAdapter(new File(outDir)).autoCreateDirectory(true).get())
.get();
}
}
EDIT: Based on Gary's response replaced some methods as
#MessagingGateway(defaultRequestChannel="requestChannel")
public interface FileProcessingService {
boolean processFilesin( String inputDir );
}
#ServiceActivator(inputChannel = "requestChannel")
public boolean fileReader(String inDir) {
FileReadingMessageSource fileSource = new FileReadingMessageSource();
fileSource.setDirectory(new File(inDir));
fileSource.afterPropertiesSet();
fileSource.start();
Message<File> msg;
while ((msg = fileSource.receive()) != null) {
fileInChannel().send(msg);
}
fileSource.stop();
System.out.println("Sent all files in directory: " + inDir);
return true;
}
Now it is working as expected.
You can use this code
FileProcessor.java
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
#Component
public class FileProcessor {
private static final String HEADER_FILE_NAME = "file_name";
private static final String MSG = "%s received. Content: %s";
public void process(Message<String> msg) {
String fileName = (String) msg.getHeaders().get(HEADER_FILE_NAME);
String content = msg.getPayload();
//System.out.println(String.format(MSG, fileName, content));
System.out.println(content);
}
}
LastModifiedFileFilter.java
package com.example.demo;
import org.springframework.integration.file.filters.AbstractFileListFilter;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
public class LastModifiedFileFilter extends AbstractFileListFilter<File> {
private final Map<String, Long> files = new HashMap<>();
private final Object monitor = new Object();
#Override
protected boolean accept(File file) {
synchronized (this.monitor) {
Long previousModifiedTime = files.put(file.getName(), file.lastModified());
return previousModifiedTime == null || previousModifiedTime != file.lastModified();
}
}
}
Main Class= DemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import org.apache.commons.io.FileUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.Aggregator;
import org.springframework.integration.annotation.InboundChannelAdapter;
import org.springframework.integration.annotation.Poller;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.channel.MessageChannels;
import org.springframework.integration.dsl.core.Pollers;
import org.springframework.integration.file.FileReadingMessageSource;
import org.springframework.integration.file.filters.CompositeFileListFilter;
import org.springframework.integration.file.filters.SimplePatternFileListFilter;
import org.springframework.integration.file.transformer.FileToStringTransformer;
import org.springframework.integration.scheduling.PollerMetadata;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.stereotype.Component;
#SpringBootApplication
#Configuration
public class DemoApplication {
private static final String DIRECTORY = "E:/usmandata/logs/input/";
public static void main(String[] args) throws IOException, InterruptedException {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public IntegrationFlow processFileFlow() {
return IntegrationFlows
.from("fileInputChannel")
.transform(fileToStringTransformer())
.handle("fileProcessor", "process").get();
}
#Bean
public MessageChannel fileInputChannel() {
return new DirectChannel();
}
#Bean
#InboundChannelAdapter(value = "fileInputChannel", poller = #Poller(fixedDelay = "1000"))
public MessageSource<File> fileReadingMessageSource() {
CompositeFileListFilter<File> filters =new CompositeFileListFilter<>();
filters.addFilter(new SimplePatternFileListFilter("*.log"));
filters.addFilter(new LastModifiedFileFilter());
FileReadingMessageSource source = new FileReadingMessageSource();
source.setAutoCreateDirectory(true);
source.setDirectory(new File(DIRECTORY));
source.setFilter(filters);
return source;
}
#Bean
public FileToStringTransformer fileToStringTransformer() {
return new FileToStringTransformer();
}
#Bean
public FileProcessor fileProcessor() {
return new FileProcessor();
}
}
The FileReadingMessageSource uses a DirectoryScanner internally; it is normally set up by Spring after the properties are injected. Since you are managing the object outside of Spring, you need to call Spring bean initialization and lifecycle methods afterPropertiesSet() , start() and stop().
Call stop() when the receive returns null.
> return null; // Not sure what to return!
If you return nothing, your calling thread will hang in the gateway waiting for a response. You could change the gateway to return void or, since your gateway is expecting a String, just return some value.
However, your calling code is not looking at the result anyway.
> gateway.processFilesin(inDir);
Also, remove the #Bean from the #ServiceActivator; with that style, the bean type must be MessageHandler.