#Autowired service bean in Controller Class won't recognize the methods - java

I have this classes:
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Autowired
private EntityManager entityManager;
#Override
public UserDetails loadUserByUsername( String username) throws UsernameNotFoundException {
Optional<User> userOptional = userRepository.findUserByUsername(username);
User user = userOptional.orElseThrow(
()->new UsernameNotFoundException("Username not found in the database")
);
return new MyUserDetails(user);
}
public void saveUser(User user){
User newUser=new User();
newUser.setUsername(user.getUsername());
newUser.setPassword(passwordEncoder.encode(user.getPassword()));
newUser.setEnabled(true);
newUser.setRoles(List.of(entityManager.find(Role.class,1)
));
userRepository.save(newUser);
}
public void deleteUser(User user){
userRepository.delete(user);
}
public UserDetails getCurrentlyLoggedUser(Authentication authentication){
Object principal = authentication.getPrincipal();
if(principal instanceof UserDetails){
String username=((User) principal).getUsername();
UserDetails loggedUser = loadUserByUsername(username);
return loggedUser;
}
return null;
}}
#Controller
#RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
#Autowired
private final UserDetailsService userDetailsService;
#GetMapping("/orderlist")
public String showOrdeList(Model model, #AuthenticationPrincipal Authentication authentication){
userDetailsService.getCurrentlyLoggedUser
}
}
I want to know why the methods from UserDetailsServiceImpl class won't show up in the controller...
I can acces only the methods from the interface, but not the ones implemented besides them.
I tried to use qualifiers, and even made a config class to instantiate the impl class when injecting the bean by interface.
#Configuration
public class OrderConfig {
#Bean("userDetailsService")
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
Can anyone tell me what I'm doing wrong?

Methods defined in UserDetailsServiceImpl but not in UserDetailsService aren't accessible because you're injecting your service by interface UserDetailsService and compiler doesn't know which implementation will be injected in runtime.
So, you need to define your own interface with all methods that you want to expose or inject your service by class.
#Autowired
private final UserDetailsServiceImpl userDetailsService;
I would recommend a custom interface that extends UserDetailsService and injecting your service using your custom interface.

Related

Spring Boot Order of Initialization for reading from Database

I am currently using hibernate repositories and I need roles in my database, as one of my service layers uses the roles at startup.
#SpringBootApplication
public class FedditBackendApplication {
public static void main(String[] args) {
SpringApplication.run(FedditBackendApplication.class, args);
}
#Bean
CommandLineRunner init (RoleRepository roleRepo) {
return args -> {
for(ERole role: ERole.values()) {
if (!roleRepo.existsByName(role)){
roleRepo.save(new Role(role));
}
}
};
}
#Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
To initialize my roles into my database, I let spring do my schema creation, however I need to put these rows into the database directly afterwards.
Is There a better way to do this? The issue I'm running into is that one of my Service components gets from the roleRepository in the constructor and it's not finding anything when I use this initialization method.
#Service
#Transactional
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final AuthenticationManager authenticationManager;
private final RoleRepository roleRepository;
private final PasswordEncoder encoder;
private final JwtUtils jwtUtils;
private final Set<Role> userRoles;
public UserServiceImpl(UserRepository userRepository, AuthenticationManager authenticationManager, RoleRepository roleRepository, PasswordEncoder encoder, JwtUtils jwtUtils,Set<Role> userRoles){
this.userRepository = userRepository;
this.authenticationManager = authenticationManager;
this.roleRepository = roleRepository;
this.encoder = encoder;
this.jwtUtils = jwtUtils;
this.userRoles = new HashSet<>();
Optional<Role> userRole = this.roleRepository.findByName(ERole.ROLE_USER);
// throws error on next line because userRole is not present in the DB
this.userRoles.add(userRole.get());
}
}
I've tried changing the order of the bean initialization with no success.
It seems that the CommandLineRunner bean initializes after the service layers.
#Bean
CommandLineRunner init (RoleRepository roleRepo) {
return args -> {
for(ERole role: ERole.values()) {
if (!roleRepo.existsByName(role)){
roleRepo.save(new Role(role));
}
}
};
}
I'd like to initialize these rows after the schema is created, though I'm not really sure how.
https://github.com/vw0389/feddit-backend/tree/comments is the repo and branch if relevant

Field myUserDetailsService in com.api.WebSecurityConfig required a single bean, but 2 were found

Full error:
Field myUserDetailsService in com.api.WebSecurityConfig required a single bean, but 2 were found:
user1: defined in file [/tools/tomcat/instances/webapps/api/WEB-INF/classes/com/api/jwt/users/test1.class]
user2: defined in file [/tools/tomcat/instances/webapps/api/WEB-INF/classes/com/api/jwt/users/test2.class]
I have the following classes:
Test1.java
#Service
#Component("user1")
public class Test1 implements UserDetailsService {
#Value("${test1.username}")
private String test1Username;
#Value("${test1.password}")
private String test1Password;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username != null && username.equals(test1Username)) {
return new User(username, test1Password, new ArrayList<>());
} else {
throw new UsernameNotFoundException("Username not found: " + username);
}
}
}
Test2.java
#Service
#Component("user2")
public class Test2 implements UserDetailsService {
#Value("${test2.username}")
private String test2Username;
#Value("${test2.password}")
private String test2Password;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username != null && username.equals(test2Username)) {
return new User(username, test2Password, new ArrayList<>());
} else {
throw new UsernameNotFoundException("Username not found: " + username);
}
}
}
SpringMainApplication.java
#EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService myUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/**/access-token").permitAll().
anyRequest().authenticated().and().
exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
JwtRequestFilter.java
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
#Qualifier(value = "user1")
private Test1 test1;
#Autowired
#Qualifier(value = "user2")
private Test2 test2;
#Override
protected void doFilterInternal() throws ServletException, IOException {
// SOME CODE
}
I thought that by using #Component and #Qualifier, I would be able to set up two user detail classes, but it doesn't seem to be the case.
Is there anything that I am missing or is it just not possible in this design?
If I make another class named MyUserDetailsService.java then compiling works and the correct classes are being accessed in the respective endpoints. I just don't understand why MyUserDetailsService must exist and I cannot use the Test1 and Test2 as user classes.
EDIT:
Test1.java
#Service
#Component("myUserDetailsService")
public class Test1 implements UserDetailsService {
#Value("${test1.username}")
private String test1Username;
#Value("${test1.password}")
private String test1Password;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username != null && username.equals(test1Username)) {
return new User(username, test1Password, new ArrayList<>());
} else {
throw new UsernameNotFoundException("Username not found: " + username);
}
}
}
Test2.java
#Service
#Component("myUserDetailsService")
public class Test2 implements UserDetailsService {
#Value("${test2.username}")
private String test2Username;
#Value("${test2.password}")
private String test2Password;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username != null && username.equals(test2Username)) {
return new User(username, test2Password, new ArrayList<>());
} else {
throw new UsernameNotFoundException("Username not found: " + username);
}
}
}
SpringMainApplication.java
#EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier(value = "myUserDetailsService")
private UserDetailsService myUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/**/access-token").permitAll().
anyRequest().authenticated().and().
exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
JwtRequestFilter.java
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private Test1 test1;
#Autowired
private Test2 test2;
#Override
protected void doFilterInternal() throws ServletException, IOException {
// SOME CODE
}
This gives me the following error:
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.api.SpringMainApplication];
nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException:
Annotation-specified bean name 'myUserDetailsService' for bean class [com.api.jwt.users.Test2DetailsService] conflicts with existing, non-compatible bean definition of same name and class [com.api.jwt.users.Test1DetailsService]
You don't need qualifiers for these declarations. Spring can inject those by type as there's a single matching bean for each type (Test1 and Test2).
#Autowired
#Qualifier(value = "user1")
private Test1 test1;
#Autowired
#Qualifier(value = "user2")
private Test2 test2;
However you do need need a qualifier for this declaration because you have two implementations of UserDetailsService and spring doesn't know which one to inject, resulting in your error.
#Autowired
private UserDetailsService myUserDetailsService;
Edit
In your updated example you are defining two beans with the same bean id
#Component("myUserDetailsService")
public class Test1 implements UserDetailsService {
#Component("myUserDetailsService")
public class Test2 implements UserDetailsService {
This causes the new error you're seeing (It might help to review Spring documentation):
Annotation-specified bean name 'myUserDetailsService' for bean class
[com.api.jwt.users.Test2DetailsService] conflicts with existing,
non-compatible bean definition of same name and class
[com.api.jwt.users.Test1DetailsService]
In your example it doesn't really make sense to have two UserDetailsService implementations. You could refactor by combining them into a single class.

Changing Spring XML configuration to #Configuration

My spring application is already configured via xml stye.I am trying to change it with #Configuration classes.
My app is used another project as maven library.I have a service, annotated with #Named and this service is used by another service in the library.
#Named("userDetailsService")
public class UserDetailsServiceImpl extends AbstractServiceImpl implements UserDetailsService {
#Inject
private UserService userService;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userService.getByUserName(username);
}
}
#Named("userService")
public class UserServiceImpl extends BaseDaoServiceImpl<User, UserDao> implements UserService {
#Inject
private AuthorityService authorityService;
#Inject
private UserAuthorityService userAuthorityService;
#Override
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public User getByUserName(String username) {
return dao.getByUserName(username);
}
#Override
public List<User> getUserWithHasAuthority(String authorityName) {
return dao.getUserWithHasAuthority(authorityName);
}
#Override
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public User insert(User user) {
user.setEnabled(true);
super.insert(user);
Authority authority = authorityService.getByName("ROLE_USER");
UserAuthority userAuthority = new UserAuthority();
userAuthority.setAuthority(authority);
userAuthority.setUser(user);
userAuthorityService.insert(userAuthority);
return user;
}
}
On my new #Configuration class
#Configuration
#ComponentScan(basePackages = {"com.mylibrary","com.myapp"})
#EnableAspectJAutoProxy(proxyTargetClass = true)
#EnableTransactionManagement
public class ServiceTestConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
and this is my test method:
#Test
public void test() {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("admin", "admin");
authenticationManager.authenticate(authenticationToken);
}
it gives null pointer exception on userService property that exist in userDetailsService.Both of them #Named.
Thnx for your help
Test classes :
#ContextConfiguration(classes = {ServiceTestConfiguration.class, DataSourceConfiguration.class, SecurityConfiguration.class})
#Transactional
public class AbstractTest {
}
#RunWith(SpringJUnit4ClassRunner.class)
public class ServiceTest extends AbstractTest {
#Inject
private AuthenticationManager authenticationManager;
#Test
public void test() {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("admin", "admin");
authenticationManager.authenticate(authenticationToken);
}
}

Mock objects returns null

I have below Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringTestConfig.class)
public class UserServiceTest {
#Inject
private UserRepository userRepository;
#Inject
private UserService userService;
#Test
public void testProcessInvoice() throws SQLException {
User user = new User();
user.setFirstName("abc");
when(userRepository.save(any(User.class))).thenReturn(user);
Assert.assertNotNull(userService);
User savedUser = userService.save(user);
Assert.assertEquals("abc", savedUser.getFirstName());
}
}
I have below SpringTestConfig.java
#Configuration
public class SpringTestConfig {
#Bean
public UserService userService() {
return Mockito.mock(UserService.class);
}
#Bean
public UserRepository userRepository() {
return Mockito.mock(UserRepository.class);
}
}
call to User savedUser = userService.save(user); returns null user object. I am not able to figure it out why it is returning null.
EDIT:
UserRepository is JpaRepository, if this is a problem
public interface UserRepository extends JpaRepository<User, Long> {
}
Your UserService is a mock object, and has no defined behavior for dealing with the #save(User) method.
Mocking the object under test is probably not what you are after here. I would recommend your objects under test are instantiated in the test, and injected with the mocks or stubs of the objects that they utilize.
Your configuration needs to return a real UserService:
#Configuration
public class SpringTestConfig {
#Bean
public UserService userService() {
return new UserServiceImpl(); // or whatever your implementation is
}
#Bean
public UserRepository userRepository() {
return Mockito.mock(UserRepository.class);
}
}
Mocks are for collaborators, not for the thing you're testing.

How can I use a DAO inside a Controller in Spring MVC?

In a Spring MVC project I've a DAO class myproj.models.UserDAO:
#Repository
#Transactional
public class UserDAO {
// UserDAO methods ...
}
and I should use it inside a controller, say myproj.controllers.UserController:
#Controller
public class UserController {
// UserController methods ...
#RequestMapping(value="/{user}")
public String create(String user) {
// Here I want to use the UserDAO
// ...
}
}
How can I create an instance of the UserDAO object and use it inside a controller method?
You could try following
#Repository
#Transactional
public class UserDAO {
// UserDAO methods ...
}
Controller:
#Controller
public class UserController {
#Autowired //this will give you the reference to UserDAO
UserDAO userDao;
// UserController methods ...
#RequestMapping(value="/{user}")
public String create(String user) {
// Here I want to use the UserDAO
userDao.userDaoMethod();
// ...
}
}
For more information on #Autowired explore this
User Autowired annotation to inject a bean instance of your DAO:
#Controller
public class UserController {
#Autowired
UserDAO userDao;
#RequestMapping(value="/{user}")
public String create(String user) {
userDao.method();
}
}

Categories