org.apache.catalina.core.StandardContext.startInternal One or more listeners failed to start
I would like to write a detailed answer on the issue and the steps I have followed to solve the problem.
A. There was not enough logging info. I looked up the POM
and found this,
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> <exclusions> <!-- Exclude Commons Logging in favor of SLF4j --> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency>
I have excluded the logging in the favour of SLF4J
and I just deleted the exclusion XML tag to get more logging info. So, it becomes like this,
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency>
B. Now, I get the logging info. I have this error message like,
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'userService' for bean class [mobi.puut.services.UserServiceImpl] conflicts with existing, non-compatible bean definition of same name and class [mobi.puut.services.UserService2Impl]
You get the ConflictingBeanDefinitionException
for creating the bean with the same name. Though I have some experience with the Spring
, I need to create with a project with the Apache Cxf
and hence, clone a demo project for the very purpose. They have the one same entity User
and same definition - interface
and same implementation
of that. Though I have refactored
to all classes in the project and there seem no apparent errors, I still keep an issue - I keep the same bean name "userService
in 2 implementations of the interface.
The User2
class in the entities,
public class User2 { private Integer id; private String name; public User2() { } public User2(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return String.format("{id=%s,name=%s}", id, name); }}
The User
class in the entities,
@Entity@Table(name = "users")public class User { @Id @Column @NotNull @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @NotNull @Column(name = "name") @Size(min = 5, max = 45, message = "Name must be between 5 and 45 characters.") private String name; public User() { } public User(int id, String name) { super(); this.id = id; this.name = name; } public User(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof User)) return false; User user = (User) o; if (getId() != user.getId()) return false; return getName().equals(user.getName()); } @Override public int hashCode() { int result = getId(); result = 31 * result + getName().hashCode(); return result; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; }}
The interfaces in the services
directories,
public interface IUserService2 { Collection<User2> getUsers(); User2 getUser(Integer id); Response add(User2 user);}public interface IUserService { List<User> getCurrentStatuses(); void create(User user); List<User> getAllUsers();}
The implementations of the interfaces inside the services
derectory,
@Service("userService")public class UserService2Impl implements IUserService2 { private static Map<Integer, User2> users = new HashMap<Integer, User2>(); static { users.put(1, new User2(1, "foo")); users.put(2, new User2(2, "bar")); users.put(3, new User2(3, "baz")); } public UserService2Impl() { } @Override public Collection<User2> getUsers() { return users.values(); } @Override public User2 getUser(Integer id) { return users.get(id); } @Override public Response add(User2 user) { user.setId(users.size()+1); users.put(user.getId(), user); //do more stuff to add user to the system.. return Response.status(Response.Status.OK).build(); }}@Service("userService")public class UserServiceImpl implements IUserService { @Autowired @Qualifier("userDao") public IUserDao userDao; public List<User> getCurrentStatuses() { return userDao.getAllUsers(); } public void create(User user) { userDao.saveOrUpdate(user); } public List<User> getAllUsers() { List<User> users = userDao.getAllUsers(); if (Objects.isNull(users)) { return null; } return users; }}
And, there was the error
- I annotaed 2 classes with the same bean name of "userService"
. I had to change it like this to provide the bean a different name,
@Service("user2Service")public class UserService2Impl implements IUserService2 { // some code}
And, that error goes away. In short, the error ConflictingBeanDefinitionException
due to the 2 beans of the same name and I just had to provide a different name.
C. I still had things to fix. Afterwards, when I run the program, I get the
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'mobi.puut.database.IUserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=userDao)}// some consequent error messages not necessay to solve the issueCaused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'mobi.puut.database.IUserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=userDao)}
The error here is UnsatisfiedDependencyException
and says it can't create a bean with the name of userService
. OK, this is the code I had and integrated into the project. The Unsatisfied dependency expressed through the field namely userDao
. userDao
is the instance of the interface of IUserDao
and it was @autowired
like this,
@Autowired public IUserDao userDao;
Here is more insight of the code,
@Service("userService")public class UserServiceImpl implements IUserService { @Autowired public IUserDao userDao; public List<User> getCurrentStatuses() { return userDao.getAllUsers(); } public void create(User user) { userDao.saveOrUpdate(user); } public List<User> getAllUsers() { List<User> users = userDao.getAllUsers(); if (Objects.isNull(users)) { return null; } return users; }}
I have an interface in the database
directory and consequent implementation of the interface for the user. The interface name is IUserDao
and looks
soemthing like this, public interface IUserDao { boolean create(User user); void saveOrUpdate(User user); boolean create(List<User> users); List<User> getAllUsers(); User getById(int id);}
And, the implementation,
@Repository("userDao")public class UserDaoImpl implements IUserDao { @Autowired private SessionFactory sessionFactory; // the HQL queries}
The consequent part of the error message was NoSuchBeanDefinitionException
and the app doesn't find a qualifying bean of the type (class) of IUserDao
. I have all the HQL
queries in the implementation of the IUserDao
and the code works perfectly before.
I have to take a moment to think and finally, I have an intuition that may be the database
layer is NOT
integrated into the app. Here is the configuration
I used,
public class WebAppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { servletContext.addListener(new ContextLoaderListener(createWebAppContext())); addApacheCxfServlet(servletContext); } private void addApacheCxfServlet(ServletContext servletContext) { CXFServlet cxfServlet = new CXFServlet(); ServletRegistration.Dynamic appServlet = servletContext.addServlet("CXFServlet", cxfServlet); appServlet.setLoadOnStartup(1); Set<String> mappingConflicts = appServlet.addMapping(AppConfig.API_BASE); } private WebApplicationContext createWebAppContext() { AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext(); // register all the config classes here appContext.register(AppConfig.class); return appContext; }}
Obviously, the no database code is integrated in the WebInitializer
. I wrote a new class provides all the info of the database connection
and the hibernate integration
and looks like,
@Configuration@EnableWebMvc@EnableTransactionManagement@ComponentScan(basePackages = {"mobi.puut.database"})public class DatabaseConfig { @Bean public LocalSessionFactoryBean sessionFactory() { // mobi.puut.entities LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); sessionFactory.setDataSource(dataSource()); sessionFactory.setPackagesToScan( new String[]{"mobi.puut.entities"}); sessionFactory.setHibernateProperties(hibernateProperties()); return sessionFactory; } @Bean @Autowired public HibernateTransactionManager transactionManager( SessionFactory sessionFactory) { HibernateTransactionManager txManager = new HibernateTransactionManager(); txManager.setSessionFactory(sessionFactory); return txManager; } @Bean public PersistenceExceptionTranslationPostProcessor exceptionTranslation() { return new PersistenceExceptionTranslationPostProcessor(); } @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); // dataSource.setUrl("jdbc:mysql://localhost:3306/wallet?createDatabaseIfNotExist=true"); dataSource.setUrl("jdbc:mysql://localhost:3306/Wallet"); dataSource.setUsername("testuser"); dataSource.setPassword("testpassword"); return dataSource; } Properties hibernateProperties() { Properties properties = new Properties();// properties.setProperty("hibernate.hbm2ddl.auto", "create-drop"); properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect"); return properties; }}
And, then finally integrate into the WebInitializer
. This is the code I used to registered
the config earlier in the WebInitializer
,
appContext.register(AppConfig.class);
Updated the line with the,
appContext.register(AppConfig.class, DatabaseConfig.class);
Finally, everything works fine. So, the config
directory looks like,
SUMMERY
I had to solve this problem through the 3 errors,
i. ConflictingBeanDefinitionExceptionii. UnsatisfiedDependencyExceptioniii. NoSuchBeanDefinitionException
ConflictingBeanDefinitionException
-> 2 beans with the same name
UnsatisfiedDependencyException
-> Have a bean (= "userDao")
in the class which was not correct to use
NoSuchBeanDefinitionException
-> the code was correct but needed to add the datbase layer in the congig so the Spring IoC
finds the bean.
I sincerely hope this will help some people.