After marking a class as @Transactional
(see below), I'm getting LinkageErrors about "attempted duplicate class definition" when my app is reloaded while a user is still logged in.
Error:
org.springframework.security.authentication.InternalAuthenticationServiceException: java.lang.LinkageError-->loader 'app' (instance of jdk.internal.loader.ClassLoaders$AppClassLoader) attempted duplicate class definition for [redacted].AdminUserDao$$FastClassBySpringCGLIB$$2ffa020e.
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:126)
Where adminUserDao is the dao we use in our custom UserDetailsService to load users during authentication.
Original AdminUserDao.java
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class AdminUserDao {
private final SessionFactory sessionFactory;
@Autowired
public AdminUserDao(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public AdminUser getUserByEmailAddress(String emailAddress) {
String hql = "from " + AdminUser.class.getName()
+ " admin_user where admin_user.emailAddress = :emailAddress";
Session session = null;
try {
session = sessionFactory.openSession();
return session
.createQuery(hql, AdminUser.class)
.setParameter("emailAddress", emailAddress)
.uniqueResult();
} finally {
if (session != null && session.isOpen()) {
session.close();
}
}
}
Transactional AdminUserDao.java
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@Repository
public class AdminUserDao {
private final SessionFactory sessionFactory;
@Autowired
public AdminUserDao(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public AdminUser getUserByEmailAddress(String emailAddress) {
String hql = "from " + AdminUser.class.getName()
+ " admin_user where admin_user.emailAddress = :emailAddress";
return sessionFactory.getCurrentSession()
.createQuery(hql, AdminUser.class)
.setParameter("emailAddress", emailAddress)
.uniqueResult();
}
}
Transaction Manager Bean
@Bean
public HibernateTransactionManager transactionManager(@Qualifier("sessionFactory") SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
Steps to reproduce:
- Log in
- Restart the app (without logging out)
- Reload the page (user should still be logged in)
- Log out
- Attempt to log in again with the same user. The error is triggered when Spring attempts to authenticate the user.
I've seen others on Stack Overflow run into this issue when they incorrectly define a custom ClassLoader (here); however, I'm not using a custom ClassLoader.
Potentially relevant dependency versions:
- Java: 11
- Spring Boot: 2.1.1.RELEASE
- Spring: 5.1.3.RELEASE
- Spring Security: 5.1.4.RELEASE