Spring 3.1 configuration: environment not injected - java

I am using the following for the spring 3.1 configuration:
#Configuration
#EnableTransactionManagement
public class DataConfig {
#Inject
private Environment env;
#Inject
private DataSource dataSource;
// #Bean
public SpringLiquibase liquibase() {
SpringLiquibase b = new SpringLiquibase();
b.setDataSource(dataSource);
b.setChangeLog("classpath:META-INF/db-changelog-master.xml");
b.setContexts("test, production");
return b;
}
#Bean
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean b = new LocalContainerEntityManagerFactoryBean();
b.setDataSource(dataSource);
HibernateJpaVendorAdapter h = new HibernateJpaVendorAdapter();
h.setShowSql(env.getProperty("jpa.showSql", Boolean.class));
h.setDatabasePlatform(env.getProperty("jpa.database"));
b.setJpaVendorAdapter(h);
return (EntityManagerFactory) b;
}
#Bean
public PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor() {
PersistenceExceptionTranslationPostProcessor b = new PersistenceExceptionTranslationPostProcessor();
// b.setRepositoryAnnotationType(Service.class);
// do this to make the persistence bean post processor pick up our #Service class. Normally
// it only picks up #Repository
return b;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager b = new JpaTransactionManager();
b.setEntityManagerFactory(entityManagerFactory());
return b;
}
/**
* Allows repositories to access RDBMS data using the JDBC API.
*/
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource);
}
#Bean(destroyMethod = "close")
public DataSource dataSource() {
BasicDataSource db = new BasicDataSource();
if (env != null) {
db.setDriverClassName(env.getProperty("jdbc.driverClassName"));
db.setUsername(env.getProperty("jdbc.username"));
db.setPassword(env.getProperty("jdbc.password"));
} else {
throw new RuntimeException("environment not injected");
}
return db;
}
}
the issue is that the variable env is not injected and is always null.
I have not done anything about the Environment setup since I do not know if it's needed or how to. I looked at the greenhouse example and i did not find anything specifically for Environment. What should I do to make sure the env is injected?
The related files:
// CoreConfig.java
#Configuration
public class CoreConfig {
#Bean
LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
/**
* Properties to support the 'standard' mode of operation.
*/
#Configuration
#Profile("standard")
#PropertySource("classpath:META-INF/runtime.properties")
static class Standard {
}
}
// the Webconfig.java
#Configuration
#EnableWebMvc
#EnableAsync
// #EnableScheduling
#EnableLoadTimeWeaving
#ComponentScan(basePackages = "com.jfd", excludeFilters = { #Filter(Configuration.class) })
#Import({ CoreConfig.class, DataConfig.class, SecurityConfig.class })
#ImportResource({ "/WEB-INF/spring/applicationContext.xml" })
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/images/**").addResourceLocations(
"/images/");
}
#Bean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver b = new BeanNameViewResolver();
b.setOrder(1);
return b;
}
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver b = new InternalResourceViewResolver();
b.setSuffix(".jsp");
b.setPrefix("/WEB-INF/jsp/");
b.setOrder(2);
return b;
}
#Bean
public CookieLocaleResolver localeResolver() {
CookieLocaleResolver b = new CookieLocaleResolver();
b.setCookieMaxAge(100000);
b.setCookieName("cl");
return b;
}
// for messages
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource b = new ResourceBundleMessageSource();
b.setBasenames(new String[] { "com/jfd/core/CoreMessageResources",
"com/jfd/common/CommonMessageResources",
"com/jfd/app/AppMessageResources",
"com/jfd/app/HelpMessageResources" });
b.setUseCodeAsDefaultMessage(false);
return b;
}
#Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver b = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.put("org.springframework.web.servlet.PageNotFound", "p404");
mappings.put("org.springframework.dao.DataAccessException",
"dataAccessFailure");
mappings.put("org.springframework.transaction.TransactionException",
"dataAccessFailure");
b.setExceptionMappings(mappings);
return b;
}
/**
* ViewResolver configuration required to work with Tiles2-based views.
*/
#Bean
public ViewResolver viewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(TilesView.class);
return viewResolver;
}
/**
* Supports FileUploads.
*/
#Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(500000);
return multipartResolver;
}
// for configuration
#Bean
public CompositeConfigurationFactoryBean myconfigurations()
throws ConfigurationException {
CompositeConfigurationFactoryBean b = new CompositeConfigurationFactoryBean();
PropertiesConfiguration p = new PropertiesConfiguration(
"classpath:META-INF/app-config.properties");
p.setReloadingStrategy(new FileChangedReloadingStrategy());
b.setConfigurations(new org.apache.commons.configuration.Configuration[] { p });
b.setLocations(new ClassPathResource[] { new ClassPathResource(
"META-INF/default-config.properties") });
return b;
}
#Bean
org.apache.commons.configuration.Configuration configuration()
throws ConfigurationException {
return myconfigurations().getConfiguration();
}
// and the SecurityConfig.java
#Configuration
#ImportResource({ "/WEB-INF/spring/applicationContext-security.xml" })
public class SecurityConfig {
#Bean
public BouncyCastleProvider bcProvider() {
return new BouncyCastleProvider();
}
#Bean
public PasswordEncryptor jasyptPasswordEncryptor() {
ConfigurablePasswordEncryptor b = new ConfigurablePasswordEncryptor();
b.setAlgorithm("xxxxxx");
return b;
}
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder b = new org.jasypt.spring.security3.PasswordEncoder();
b.setPasswordEncryptor(jasyptPasswordEncryptor());
return b;
}
}
in the applicationcontext.xml, it only imported two xmls to config cache and cassandra, so it may not be important.

Not sure why, but using the #Resource annotation worked for me. #Autowired always returned null.

The problem is with the spring security for the remember me feature. if I take this line <code> <remember-me data-source-ref="dataSource" /> </code> out. everything works fine. if this line presents, it will try to load the db before anything else and env was never injected.

If you don't use full Java EE compatible server you have to include javax.inject.jar to your project classpath to add the support of #Inject. You can also try to use spring's native #Autowired annotation.

#jfd,
I don't immediately see anything wrong with your configuration that would cause a failure to inject the Environment.
If you start from scratch with an empty #Configuration class, and then #Inject the Environment, does it work for you?
If yes, then at what point does it begin to fail?
Would you be willing to reduce the example down to the smallest possible configuration that fails and submit it as a reproduction project? The instructions here make this as simple as possible: https://github.com/SpringSource/spring-framework-issues#readme
Thanks!

I've detect a similar error for my project as mentioned here.
I've also figure out, that a call of afterproperties is necessary to get the sessionFactory.
... and yes, I'm using Spring Security too (which may be the source of the problem).
My #Configuration annotated class uses #ComponentScan for packages containing Hibernate based DAOs and a #Bean annotated method for creating the SessionFactory used by the DAOs. At runtime, a exception is thrown, mentioned that 'sessionFactory' or 'hibernateTemplate' was not found. It seems that the DAOs are constructed before the SessionFactory was created. One workaround for me was to put the component scan directive back in a XML file () and replace #ComponentScan with #ImportResource of that file.
#Configuration
//#ComponentScan(basePackages = "de.webapp.daocustomer", excludeFilters = {#ComponentScan.Filter(Configuration.class), #ComponentScan.Filter(Controller.class)})
#ImportResource({"classpath*:componentScan.xml","classpath*:properties-config.xml","classpath*:security-context.xml"})
public class AppConfig
{
...
#Bean
public SessionFactory sessionFactory() throws Exception
{
AnnotationSessionFactoryBean bean = new AnnotationSessionFactoryBean();
bean.setDataSource(dataSource());
bean.setPackagesToScan(new String[] {"de.webapp"});
bean.setHibernateProperties(hibernateProps());
bean.afterPropertiesSet();
return bean.getObject();
}
Also interesting fact: if #ComponentScan is included, a breakpoint set in method sessionFactory() was never reached !

I also had the similar issue with spring-social-sample app.
After I converted field level #Inject to constructor level inject it worked.

Related

Configured two data source in spring data JPA but second data source still considering first DB URL

In this official example of configuring 2 data sources, they have used embedded DB.
So, with the help of Spring boot's How to documentations, Configure Two DataSources and Using Multiple EntityManagerFactories, I customized that to use MySQL DB.
You can find the complete code under this Github repository. Don't forget to checkout the "two_data_sources_in_separate_files" branch.
But with this modification, "secondDBTransactionManager" is not pointing second_db. Instead, it is inserting data into first_db.
FirstDataSource.java
#Configuration
#EnableJpaRepositories(entityManagerFactoryRef = "firstDBEntityManagerFactory", transactionManagerRef = "firstDBTransactionManager")
public class FirstDataSource {
#Bean
PlatformTransactionManager firstDBTransactionManager(DataSourceProperties firstDataSourceProperties) {
return new JpaTransactionManager(firstDBEntityManagerFactory(firstDataSourceProperties).getObject());
}
#Bean
#Primary
LocalContainerEntityManagerFactoryBean firstDBEntityManagerFactory(DataSourceProperties firstDataSourceProperties) {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(firstDS(firstDataSourceProperties));
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
factoryBean.setPackagesToScan(FirstDataSource.class.getPackage().getName());
return factoryBean;
}
#Bean
#Primary
#ConfigurationProperties("app.datasource.first")
public DataSourceProperties firstDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("app.datasource.first.configuration")
public HikariDataSource firstDS(DataSourceProperties firstDataSourceProperties) {
return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
}
SecondDataSource.java
#Configuration
#EnableJpaRepositories(entityManagerFactoryRef = "secondDBEntityManagerFactory", transactionManagerRef = "secondDBTransactionManager")
public class SecondDataSource {
#Bean
PlatformTransactionManager secondDBTransactionManager(DataSourceProperties secondDataSourceProperties) {
return new JpaTransactionManager(secondDBEntityManagerFactory(secondDataSourceProperties).getObject());
}
#Bean
#ConfigurationProperties("app.datasource.second")
public DataSourceProperties secondDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties("app.datasource.second.configuration")
public HikariDataSource secondDS(
#Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) {
return secondDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Bean
public LocalContainerEntityManagerFactoryBean secondDBEntityManagerFactory(DataSourceProperties secondDataSourceProperties) {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(secondDS(secondDataSourceProperties));
factoryBean.setJpaVendorAdapter(vendorAdapter);
factoryBean.setPackagesToScan(SecondDataSource.class.getPackage().getName());
return factoryBean;
}
}
FetchData.java
#Controller // This means that this class is a Controller
#RequestMapping(path = "/demo") // This means URL's start with /demo (after Application path)
public class FetchData {
#Autowired
private TableARepository tableARepository;
#Autowired
private TableCRepository tableCRepository;
#GetMapping(path = "/addDataToFirstDB")
public #ResponseBody void addDataToFirstDB() {
// This returns a JSON or XML with the users
Table_A tableA = new Table_A();
tableA.setTable_a_col_1("test1");
tableA.setTable_a_col_2("test2");
tableA.setTable_a_col_3("test3");
tableA.setTable_a_col_4("test4");
System.out.println("Inserting data into first DB.");
tableARepository.save(tableA);
}
#GetMapping(path = "/addDataToSecondDB")
#Transactional("secondDBTransactionManager")
public #ResponseBody void addDataToSecondDB() {
// This returns a JSON or XML with the users
Table_C tableC = new Table_C();
tableC.setTable_c_col_1("test1");
tableC.setTable_c_col_2("test2");
tableC.setTable_c_col_3("test3");
tableC.setTable_c_col_4("test4");
System.out.println("Inserting data into second DB.");
tableCRepository.save(tableC);
}
}
Observe that, I've used #Transactional("secondDBTransactionManager") for second REST API, "addDataToSecondDB".
It is easy to resolve issue if any Exception is thrown. There is no Exception here but the behavior is not as expected.

Spring Configuration creating two beans instead of one

I'm writing a RESTful web service using Spring MVC, using Java Configuration. My configuration file is below. My issue is this -- I discovered that 2 instances of "myService" bean is being created, instead of just one instance. I'm not sure why? How can I adjust the configuration to create only one?
Can anyone point me in the right direction? Thanks!
Here's my configuration class....
#Configuration
public class MyConfig {
#Bean(name = "dataSource")
public DriverManagerDataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// datasource set up
return dataSource;
}
#Autowired
#Bean(name = "sessionFactory")
public SessionFactory getSessionFactory(DriverManagerDataSource dataSource) {
LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(dataSource);
sessionBuilder.scanPackages("com.mypackages");
sessionBuilder.addProperties(getHibernateProperties());
return sessionBuilder.buildSessionFactory();
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.enable_lazy_load_no_trans", "true");
properties.put("hibernate.id.new_generator_mappings", "true");
return properties;
}
#Autowired
#Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);
return transactionManager;
}
#Bean
public MyMainBean MyMainBean() {
MyMainBean bean = new MyMainBean();
bean.setService(myService());
bean.setValidator(myValidator());
return bean;
}
#Bean(name = "myService")
public MyService myService() {
MyService s = new MyService();
s.setDao1(myDao1());
s.setDao2(myDao2());
s.setCopyUtil(copyUtil());
return s;
}
#Bean
public MyDao1 myDao1() {
return new MyDao1();
}
#Bean
public MyDao2 myDao2() {
return new MyDao2();
}
#Bean
public CopyUtil copyUtil() {
return new CopyUtil();
}
#Bean
public ReportValidator reportValidator() {
ReportValidator validator = new ReportValidator();
validator.setService(myService());
return validator;
}
#Bean
public XMLValidator xmlValidator() {
XMLValidator validator = new XMLValidator();
validator.setService(myService());
return validator;
}
}
Actually, Spring is smart when wiring beans and should only call the myService() function once, and then pass the result to the other myService() calls, resulting in only one bean of MyService.
Make sure you really are getting 2 instances of MyService, e.g. by adding a log in the constructor of the MyService class.
If you truly see more than one constructor log statement, make sure that you are not declaring other MyService beans in other #Configuration classes, or that you are not using any component annotation on the MyService class (i.e. don't use #Service, #Component, #Repository).
If you declare the class with #Service, it effectively instantiates the class and adds it to the context. When you declare it again with #Bean you end up with 2 instances, so don't mix them.
Also, you don't need to use those #Autowired annotations here, or even calls to other beans, because the following will also work:
#Configuration
public class DbConfiguration {
#Bean
public MovieDao dao() {
return new MovieDao();
}
#Bean
public MovieService service(MovieDao dao) {
return new MovieService(dao);
}
}
Spring will see that you need a MovieDao to build a MovieService and it will instantiate the dao first and pass it to the service bean. You don't even need to add #Service or similar annotations to your classes!
It really is that good, hope these tips help ;)

Spring Boot ConflictingBeanDefinitionException: Annotation-specified bean name for #Controller class

I keep getting the ConflictingBeanDefinitionException error in my Spring boot application. I am not entirely sure as to how to address it, I have several #Configuration annotated classes helping to set up Thymeleaf, Spring Security and Web. Why is the application trying to setup the homeController twice? (and where is it trying to do this?)
The error is:
org.springframework.beans.factory.BeanDefinitionStoreException:
Failed to parse configuration class [org.kemri.wellcome.hie.Application]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException:
Annotation-specified bean name 'homeController' for bean class [org.kemri.wellcome.hie.HomeController] conflicts with existing, non-compatible bean definition of same name and class [org.kemri.wellcome.hie.controller.HomeController]
My spring boot main application initializer:
#EnableScheduling
#EnableAspectJAutoProxy
#EnableCaching
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected final SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
My database config file:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="org.kemri.wellcome.hie.repositories")
#PropertySource("classpath:application.properties")
public class DatabaseConfig {
#Autowired
private Environment env;
#Autowired
private DataSource dataSource;
#Autowired
private LocalContainerEntityManagerFactoryBean entityManagerFactory;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.driverClassName"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory =
new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource);
// Classpath scanning of #Component, #Service, etc annotated class
entityManagerFactory.setPackagesToScan(
env.getProperty("spring.jpa.hibernate.entitymanager.packagesToScan"));
// Vendor adapter
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
// Hibernate properties
Properties additionalProperties = new Properties();
additionalProperties.put(
"hibernate.dialect",
env.getProperty("spring.jpa.hibernate.dialect"));
additionalProperties.put(
"hibernate.showsql",
env.getProperty("spring.jpa.hibernate.showsql"));
additionalProperties.put(
"hibernate.hbm2ddl.auto",
env.getProperty("spring.jpa.hibernate.hbm2ddl.auto"));
entityManagerFactory.setJpaProperties(additionalProperties);
return entityManagerFactory;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager =
new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
entityManagerFactory.getObject());
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
My Thymeleaf config file:
#Configuration
public class ThymeleafConfig {
#Bean
public ServletContextTemplateResolver templateResolver(){
ServletContextTemplateResolver thymeTemplateResolver = new ServletContextTemplateResolver();
thymeTemplateResolver.setPrefix("/WEB-INF/views/");
thymeTemplateResolver.setSuffix(".html");
thymeTemplateResolver.setTemplateMode("HTML5");
return thymeTemplateResolver;
}
#Bean
public SpringSecurityDialect springSecurityDialect(){
SpringSecurityDialect dialect = new SpringSecurityDialect();
return dialect;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.addTemplateResolver(templateResolver());
Set<IDialect> dialects = new HashSet<IDialect>();
dialects.add(springSecurityDialect());
engine.setAdditionalDialects(dialects);
return engine;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setViewClass(ThymeleafTilesView.class);
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
}
My Web config class:
#Configuration
#PropertySource("classpath:application.properties")
public class WebConfig extends WebMvcAutoConfigurationAdapter {
#Autowired
private Environment env;
#Bean
public JavaMailSenderImpl javaMailSenderImpl() {
JavaMailSenderImpl mailSenderImpl = new JavaMailSenderImpl();
mailSenderImpl.setHost(env.getProperty("smtp.host"));
mailSenderImpl.setPort(env.getProperty("smtp.port", Integer.class));
mailSenderImpl.setProtocol(env.getProperty("smtp.protocol"));
mailSenderImpl.setUsername(env.getProperty("smtp.username"));
mailSenderImpl.setPassword(env.getProperty("smtp.password"));
Properties javaMailProps = new Properties();
javaMailProps.put("mail.smtp.auth", true);
javaMailProps.put("mail.smtp.starttls.enable", true);
mailSenderImpl.setJavaMailProperties(javaMailProps);
return mailSenderImpl;
}
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}
My controller (where there is an error setting up the controller)
#Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
return "index.html";
}
}
What might be causing the ConflictingBeanDefinitionException error for my controller class?
I ran into the same problem but for a different reason.
This can also occur if you move your classes around in your project and fail to do a 'clean'.
I use gradle with spring-boot plugin. Now I usually run:
$> ./gradlew clean bootRun
I had the same problem on a Spring integration test when I ran it with InteliJ.
After a refactor, one of my controller class was actually duplicate in the /out/production/classes directory which is the default output directory for Intelij since version 2017.2.
Since the gradle output directory is different (It's build/classes), the gradle clean goal had no effect.
For me the solution was to manually remove /out/production/classes and re run my integration test.
For a possible durable solution not having 2 output directories see here
The solution, as I found out, is to disable double initialization by including a filter in the component scan. In my case:
#EnableScheduling
#EnableAspectJAutoProxy
#EnableCaching
#Configuration
#ComponentScan(basePackages = { "org.kemri.wellcome.hie" },
excludeFilters = {#Filter(value = Controller.class, type = FilterType.ANNOTATION)})
#EnableAutoConfiguration
#PropertySource("classpath:application.properties")
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I encountered this with mvn after changing several folder names and related package names. Than I applied maven clean and run spring boot again, all solved:
mvn clean
mvn spring-boot:run
It seems you have two entityManagerFactory, one you will autowire and one you resolve programmatically as Bean:
#Autowired
private LocalContainerEntityManagerFactoryBean entityManagerFactory;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
...
}
I think you just need your configured Factory in entityManagerFactory() method.
I was having the same problem with a generated .war file from spring-boot. the approved solution (Timothy Tuti's own solution) didn't quite work for me exactly as-is, but I tweaked it a little bit and it worked. I just added the following line to my Application.java:
#ComponentScan(basePackages = { "com.mypackage" })
For reference, here goes my full Application.java
package com.inmoment.devchallenge;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
#SpringBootApplication
#Configuration
#ComponentScan(basePackages = { "com.inmoment.devchallenge.controller" })
#EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
#Configuration
#EnableNeo4jRepositories(basePackages = "com.inmoment.devchallenge.repository")
static class ApplicationConfig extends Neo4jConfiguration {
public ApplicationConfig() {
setBasePackage("com.inmoment.devchallenge.repository");
}
#Bean
GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabase("accessingdataneo4j.db");
}
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
I solved my problem by adding a bean name on top of the class.
#Component("myBeanName1")
public class MyBean {
}
And initialize it with #Autowire in this way:
#Autowire
#Qualifier("myBeanName1")
MyBean myBean;
let's assume your package name - com.example.company and the class name is RestExceptionHandler. Then you need to add the full name with the package to be identical.
add annotation #Component("com.example.company.RestExceptionHandler")
It will identify your class without conflict.
I ran into same problem when one of dependencies(say module Y) of current module(say X) also had definition of same class. So I had to create a separate module(say Z) to store common classes and then add dependency on Z for both X and Y to use.

How to verify Spring + AspecJ Transactions?

I'm pretty sure transactions are not being applied in my Spring Boot application, even though I am using #EnableTransactionManagement(mode=AdviceMode.ASPECTJ) & have spring-aspects in my app.
Configuration
#Configuration
public class DatastoreConfig{
#Bean
public DataSource dataSource(){ ///... }
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
#Bean
public String databaseSchema(){
return "mySchema";
}
#Bean
public OpenSessionInViewInterceptor openSessionInViewInterceptor(SessionFactory sessionFactory){
OpenSessionInViewInterceptor result = new OpenSessionInViewInterceptor();
result.setSessionFactory(sessionFactory);
return result;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter vendorAdapter){
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan( Main.class.getPackage().getName() + ".model");
factory.setDataSource(dataSource);
factory.setJpaProperties(getJpaProperties());
factory.afterPropertiesSet();
return factory;
}
#Bean
public HibernateJpaVendorAdapter jpaVendorAdapter(){
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
vendorAdapter.setDatabasePlatform(PostgreSQL9Dialect.class.getName());
return vendorAdapter;
}
#Bean
public SessionFactory sessionFactory(HibernateEntityManagerFactory emf){
SessionFactoryImpl sf = (SessionFactoryImpl) emf.getSessionFactory();
return sf;
}
private Properties getJpaProperties() {
Properties props = new Properties();
// props.put("hibernate.hbm2ddl.auto", "validate");
props.put("hibernate.ejb.naming_strategy", ImprovedNamingStrategy.class.getName());
props.put("hibernate.enable_lazy_load_no_trans", "true");
props.put("jadira.usertype.autoRegisterUserTypes", "true");
return props;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) throws PropertyVetoException {
JpaTransactionManager transactionManager = new JpaTransactionManager(emf);
transactionManager.setRollbackOnCommitFailure(true);
transactionManager.setDataSource(dataSource());
return transactionManager;
}
#Bean
public NamedParameterJdbcTemplate namedParameterJdbcTemplate(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
Main class
#Configuration
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
#ComponentScan
#EnableAsync
#EnableScheduling
#EnableEntityLinks
#EnableAspectJAutoProxy
#EnableApiResources(apiPrefix = "")
#EnableJpaRepositories(transactionManagerRef = "transactionManager")
#EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
#EnableConfigurationProperties
#EnableAutoConfiguration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
#EnableWebSocketMessageBroker
#Slf4j
public class Main extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(
SpringApplicationBuilder application) {
return application.sources(Main.class);
}
public static void main(String[] args) {
Object[] sources = {
Main.class,
TomcatConfig.class // include for embedded tomcat...
};
ConfigurableApplicationContext result = SpringApplication.run(sources, args);
}
}
Attempt to verify
One of the ways I was trying to verify was by running
#Component
public class SomeService{
#Transactional
#Override
protected void tryItOut(){
// this is always false
boolean declarativeTransaction = TransactionSynchronizationManager.isActualTransactionActive();
// this throws an exception
TransactionStatus aspect = TransactionAspectSupport.currentTransactionStatus();
}
}
However, if I insert a call to transactionManager.getTransaction(new DefaultTransactionDefinition()); beforehand, declarativeTransaction is true.
Any advice on how to set up #Transactions / verify they're working?
Your configuration is contradicting itself.
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
Means you are using loadtime or compile time weaving of the aspects. Judging on your configuration you aren't using that. If I have to judge your configuration you are just using basic proxies no weaving.Just remove AdviceMode.ASPECTJ and it will work, you also don't need the #EnableAspectJAutoProxy as applying transactions has nothing to do with that it (it can work without it).
Also your configuration is doing a lot to basically not use Spring Boot. You have configured a lot of things that spring boot already does for you automatically, manually.
Spring Boot already configures the following for you (just by only specifying #EnableAutoConfiguration.
EntityManagerFactory
DataSource
JdbcTemplate
Transaction Setup
OpenEntityManagerInView
Spring Web Sockets
Hypermedia support
Spring Data JPA setup
You can remove a whole lot of your configuration and put some properties in application.properties.
If you really need the SessionFactory instead of doing the casting yourself I strongly suggest the use of the HibernateJpaSessionFactoryBean.
I would suspect the following main class to still work (with transaction enabled).
#Configuration
#ComponentScan
#EnableAsync
#EnableScheduling
#EnableAutoConfiguration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
#EnableWebSocketMessageBroker
#Slf4j
public class Main extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(
SpringApplicationBuilder application) {
return application.sources(Main.class);
}
public static void main(String[] args) {
Object[] sources = {
Main.class,
TomcatConfig.class // include for embedded tomcat...
};
ConfigurableApplicationContext result = SpringApplication.run(sources, args);
}
}
With this DatastoreConfig class
#Configuration
public class DatastoreConfig{
#Bean
public FactoryBean<SessionFactory> sessionFactory(EntityManagerFactory emf){
HibernateJpaSessionFactoryBean jpaSessionFactoryBean = new HibernateJpaSessionFactoryBean();
jpaSessionFactoryBean.setEntityManagerFactory(emf);
return jpaSessionFactoryBean;
}
}
And this application.properties
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
spring.jpa.properties.jadira.usertype.autoRegisterUserTypes=true
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
You probably need to add some spring.datasource. properties to setup your datasource correctly. Also judging from the fact that you had a OpenSessionInViewInterceptor you used that instead of the OpenEntityManagerInViewInterceptor (or filter). So you might even be able to remove that.
Not sure if that is al your configuration but you might be able to remove/trim down even more. I suggest a look at the Spring Boot reference guide to get a feeling for what is already automatically configured.

#Autowired bean not being found

I'm getting errors trying to inject resource dependencies into my unit testing.
My approach has been to write a TestConfig.java to replace the applicationContext.xml for production which manages the connections of the beans. So that I can run it with an in-memory database and just test components.
TestConfig.java
#Configuration
#EnableTransactionManagement
public class TestConfig {
#Bean
public DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.hsqldb.jdbcDriver");
ds.setUrl("jdbc:hsqldb:mem:testdb");
ds.setUsername("sa");
ds.setPassword("");
return ds;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
LocalContainerEntityManagerFactoryBean lcemfb
= new LocalContainerEntityManagerFactoryBean();
lcemfb.setDataSource(this.dataSource());
lcemfb.setPackagesToScan(new String[] {"com.dao","com.data"});
lcemfb.setPersistenceUnitName("MyTestPU");
HibernateJpaVendorAdapter va = new HibernateJpaVendorAdapter();
lcemfb.setJpaVendorAdapter(va);
Properties ps = new Properties();
ps.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
ps.put("hibernate.hbm2ddl.auto", "create");
lcemfb.setJpaProperties(ps);
lcemfb.afterPropertiesSet();
return lcemfb;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager tm = new JpaTransactionManager();
tm.setEntityManagerFactory(this.entityManagerFactoryBean().getObject());
return tm;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
#Bean
public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor()
{
return new AutowiredAnnotationBeanPostProcessor();
}
}
ProductsDaoTest.java
#ContextConfiguration(classes = { TestConfig.class })
#RunWith(SpringJUnit4ClassRunner.class)
public class ProductsDaoTest {
#Resource(name="com.dao.ProductsDao")
private ProductsDao testDao;
#Test
public void testSaveProduct() {
Product productA = new Product();
testDao.save(productA);
Set<Product> products = testDao.getAllProducts();
assertNotNull(products);
}
}
The error is Error creating bean with name 'com.dao.ProductsDaoTest': Injection of resource dependencies failed
So it can't find the ProductDao Bean which is a #Repository with a #Autowired sessionFactory.
So my guess is that because I'm not naming the beans using xml it can't find it, though I thought it should automatically pick it up from setPackagesToScan(). So is there a way to manually insert the Bean mapping so that it can be found?
Also more generally is this a reasonable way to go about testing Spring DAO configurations?
Regards,
Iain
I think you are trying to use wrong name of your DAO bean in #Resource annotation. Have you explicitly specify name of the ProductsDao bean using #Qualifier? If no, then as I remember by default the name of the bean will be productsDao. So you should inject your DAO like:
#Resource(name="productsDao")
private ProductsDao testDao;
If you have only one ProductDAO implementation then simply write:
#Autowired
private ProductsDao testDao;
or
#Inject
private ProductsDao testDao;
In case if you want to give specific name to DAO then use next construction:
#Respository
#Qualifier(name="specificName")
public class ProductDAO...
EDIT:
As Boris noted you should also specify which package to scan for defined beans (classes annotated with #Component, #Service, #Repository...). For this you should add #ComponentScan annotation to your configuration class definition.
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = {"package_to_scan"})
public class TestConfig {...}

Categories

Resources