I am having trouble setting an attribute value ie. shortname to a SessionScope bean in Spring Boot.
Here is my class:
import java.util.Map;
public class LdapUser {
private String shortname = "";
private Map<String,String> token = null;
private String id = "";
public LdapUser() {
}
public String getshortname() {
return shortname;
}
public void setshortname(String shortname) {
this.shortname = shortname;
}
... remaining geters and setters
My Bean definition is here:
import xxx.controllers.SwitchController;
import xxx.isim.LdapUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.WebApplicationContext;
#Configuration
public class RestTemplateClient {
Logger logger = LoggerFactory.getLogger(SwitchController.class);
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Bean
#Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public LdapUser sessionScopedLdapUser() {
logger.info("LdapUser bean instance created");
return new LdapUser();
}
}
I am using the Bean in a Controller:
import xxx.errors.IsimConnectionException;
import xxx.isim.IsimConnection;
import xxx.isim.LdapUser;
import xxx.services.IsimRestApiService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
import java.security.Principal;
#Controller
public class HomeController {
private static final Logger log = LoggerFactory.getLogger(HomeController.class);
#Autowired
IsimRestApiService isimConn;
#Resource(name = "sessionScopedLdapUser")
LdapUser sessionScopedLdapUser;
#RequestMapping("/")
public String index(Principal principal) throws IsimConnectionException {
Authentication authentication = (Authentication) principal;
/
if ((authentication.getPrincipal() != null) && (authentication.isAuthenticated())) {
// set the shortname for the session
String shortname = (String)authentication.getPrincipal();
sessionScopedLdapUser.setshortname(shortname); //<-----
My Bean's value for shortname remains null after the line with the arrow even though I correctly get the shortname String value and that one is not null. Can you please point me out what am I doing wrong when setting the bean attribute values. I followed the example here for SessionScope Beans
Update:
I also tried to use autowired instead of #Resource(name = "sessionScopedLdapUser") but the value still remains null after executing sessionScopedLdapUser.setshortname(shortname);
#Autowired
LdapUser sessionScopedLdapUser
Also in the log I can see the LdapUser bean instance is created three times. How is that possible?
2021-09-21 10:55:55,469 INFO [http-nio-8080-exec-4] xxx.config.RestTemplateClient: LdapUser bean instance created
2021-09-21 10:57:05,247 INFO [http-nio-8080-exec-4] xxx.config.RestTemplateClient: LdapUser bean instance created
2021-09-21 10:58:08,401 INFO [http-nio-8080-exec-4] xxx.config.RestTemplateClient: LdapUser bean instance created
The ideas is to have one bean per HTTP session. I am really confused and would appreciate some hints. I was reading this article and maybe that is because I am trying to inject a Session Scope bean to a Singletone bean.
My file structure is:
xxx
---auth
---config
--- RestRemplateClient
---controllers
--- HomeController
---errors
---isim
--- LdapUser
---services
Mainapp
Thanks to #M. Deinum I was able to figure it out. I was looking at the field value in the debugger and that one was always null since I was looking the proxy and not the real object.
Here is the code that injects a session scoped bean in the #Controller class. It also works correctly and in the same way in the #Service class.
public class LdapUser {
private String shortname = "";
private Map<String,String> token = new HashMap<>();
private String id = "";
public LdapUser() {
this.shortname = shortname;
this.token = token;
this.id = id;
}
public String getshortname() {
return shortname;
}
public void setshortname(String shortname) {
this.shortname = shortname;
}
... other getters and setters
My bean configuration class:
#Configuration
public class RestTemplateClient {
Logger logger = LoggerFactory.getLogger(SwitchController.class);
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Bean
#SessionScope
public LdapUser sessionScopedLdapUser() {
logger.info("LdapUser bean instance created at "+ LocalDateTime.now());
return new LdapUser();
}
}
My controller class:
#Controller
public class HomeController {
private static final Logger log = LoggerFactory.getLogger(HomeController.class);
#Autowired
IsimRestApiService isimConn;
#Autowired
LdapUser sessionScopedLdapUser;
#RequestMapping("/")
public String index(Principal principal) throws IsimConnectionException {
Authentication authentication = (Authentication) principal;
//System.out.println("******* USER IS " + authentication.getPrincipal());
if ((authentication.getPrincipal() != null) && (authentication.isAuthenticated())) {
// set the shortname for the session
String shortname = (String)authentication.getPrincipal();
sessionScopedLdapUser.setshortname(shortname);
You can specify your configurations as below:-
import org.springframework.context.annotation.Scope;
import java.time.LocalDateTime;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class LdapUser {
private String shortName = "LdapUser Session Scope";
// other properties
public LdapUser() {
System.out.println("LdapUser SessionScope Constructor Called at "+LocalDateTime.now());
}
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
}
In configuration:
#Configuration
public class RestTemplateClient {
Logger logger = LoggerFactory.getLogger(SwitchController.class);
#Autowired
private LdapUser ldapUser;
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public void setLdapUser(LdapUser ldapUser) {
this.ldapUser = ldapUser;
}
public LdapUser getLdapUser() {
return ldapUser;
}
}
and into your controller:-
#Controller
public class HomeController {
// Other Codes
#Autowired
private RestTemplateClient restTemplateClient;
private static final Logger log = LoggerFactory.getLogger(HomeController.class);
#Autowired
IsimRestApiService isimConn;
#RequestMapping("/")
public String index(Principal principal) throws IsimConnectionException {
Authentication authentication = (Authentication) principal;
/
if ((authentication.getPrincipal() != null) && (authentication.isAuthenticated())) {
// set the shortname for the session
String shortname = (String)authentication.getPrincipal();
restTemplateClient.getLdapUser().setShortName("LdapUser Session Scope Updated");
// .... Other codes
}
}
}
Related
I've been trying to write on Elasticsearch with Multiple Indexes. Currently I have installed Spring v5.2.3.RELEASE and Spring Boot v2.2.4.RELEASE.
I found several solutions that allow to use multiple indexes using Spring's SPEL technology but I can't make it work.
I currently have these files:
ElasticDBDbConfig.java
package edu.unifi.disit.datamanager.config;
#Configuration
#EnableTransactionManagement
#EnableElasticsearchRepositories(basePackages = { "edu.unifi.disit.datamanager.datamodel.elasticdb"})
public class ElasticDBDbConfig {
#Value("${elasticsearch.protocol}")
private String esProtocol;
#Value("${elasticsearch.host}")
private String esHost;
#Value("${elasticsearch.port}")
private int esPort;
#Value("${elasticsearch.clustername}")
private String esClusterName;
#Bean(destroyMethod = "close")
public RestHighLevelClient elasticsearchClient() {
return new RestHighLevelClient(RestClient.builder(new HttpHost(esHost,esPort,esProtocol)));
}
#Bean
public ElasticsearchRestTemplate elasticsearchTemplate() {
ElasticsearchRestTemplate elasticsearchTemplate = new
ElasticsearchRestTemplate(elasticsearchClient());
elasticsearchTemplate.putMapping(KPIElasticValue.class);
return elasticsearchTemplate;
}
ConfigIndexBean.java
package edu.unifi.disit.datamanager.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
#Configuration
#Component("configIndexBean")
public class ConfigIndexBean {
#Value("${elasticsearch.dummyIndex}")
private String indexName;
public String getIndexName() {
return indexName;
}
public void setIndexName(String indexName) {
this.indexName = indexName;
}
}
KPIElasticValue.java
package edu.unifi.disit.datamanager.datamodel.elasticdb;
#JsonInclude(JsonInclude.Include.NON_NULL)
#Entity
#JsonSerialize(using = KPIElasticValueSerializer.class)
#JsonDeserialize(using = KPIElasticValueDeserializer.class)
#ComponentScan(basePackages = "edu.unifi.disit.datamanager.config")
#Document(type = "_doc", indexName = "#{#configIndexBean.getIndexName()}")
public class KPIElasticValue {
Do you have any idea?
I also try
#Document(type = "_doc", indexName = "#{configIndexBean.getIndexName()}")
#Document(type = "_doc", indexName = "#{configIndexBean.indexName}")
The exception is:
EL1057E: No bean resolver registered in the context to resolve access to bean 'configIndexBean'
I solved like this:
ConfigIndexBean.java
package edu.unifi.disit.datamanager.config;
public class ConfigIndexBean {
private static String indexName = "dummy";
public static final String getIndexName() {
return indexName;
}
public static void setIndexName(String indexName) {
ConfigIndexBean.indexName = indexName;
}
}
and the annotation on KPIElasticValue.java:
#Document(type = "_doc",indexName = "#
{T(edu.unifi.disit.datamanager.config.ConfigIndexBean).getIndexName()}")
In the services, before call the repository I change the index like this:
ConfigIndexBean.setIndexName("newindex");
When I run the test, I get a dependency error of UserService is not able to be found to be injected. This is weird because no where in ConstantsController.java did I use UserService. Also, UserService is label properly with #Service annotation.
I have tried using the #MockBean annotation in the controller test class. It gave me unidentifiable errors. I even tried autowiring the bean in the configuration because the log said define bean of type of UserService in configuration. Still no luck.
UserService
package com.GMorgan.RateMyFriendv5.Service;
import com.GMorgan.RateMyFriendv5.Entitiy.Role;
import com.GMorgan.RateMyFriendv5.Entitiy.User;
import com.GMorgan.RateMyFriendv5.Repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
#Slf4j
#Service
public class UserService {
private UserRepository repository;
public boolean login(String username, String password) {
List<User> userList = repository.findByUsername(username);
boolean isSuccessful = userList.get(0).isPassword(password);
log.info("Username: {} isSucessful: {}", username, isSuccessful);
return isSuccessful;
}
public boolean signUp(String email, String username, String password) {
if (userEmailExists(email) || userUsernameExists(username)) return false;
User user = new User();
user.setEmail(email);
user.setUsername(username);
user.setPassword(password);
user.setRole(Role.USER);
repository.save(user);
log.info("User email: {} username: {}", email, username);
return true;
}
public boolean userEmailExists(String email) {
return !repository.findByEmail(email).isEmpty();
}
public boolean userUsernameExists(String username) {
return !repository.findByUsername(username).isEmpty();
}
}
ConstantsController.java
package com.GMorgan.RateMyFriendv5.Controller;
import com.GMorgan.RateMyFriendv5.Utils.Mappings;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class ConstantsController {
#Value("${controller.constant.ping.message}")
public String pingMessage;
#RequestMapping(Mappings.PING)
public String ping() {
return pingMessage;
}
}
ConstantsControllerTest
#RunWith(SpringRunner.class)
#WebMvcTest
#AutoConfigureMockMvc
public class ConstantsControllerTest {
#Autowired
private MockMvc mockMvc;
#Value("${controller.constant.ping.message}")
public String pingMessage;
#Test
public void pingTest() throws Exception {
this.mockMvc.perform(get(Mappings.PING)).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString(pingMessage)));
}
}
I want the test to pass. In runtime, when I go to the link, I see the ping message. I want the test to do so as well.
you need to specify your controller that you want to test:
Add #WebMvcTest(ConstantsController.class) to your annotation
I want make simple web service using java spring restful web service .
I Use request Mapping annotation in controller class but when i run project there is no mapping there .
Here is controller Class :
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import guru.webservice.domain.Customer;
import guru.webservice.services.CustomerService;
#RestController
#RequestMapping(CustomerController.BASE_URL)
public class CustomerController {
public static final String BASE_URL = "api/v1/customers";
private final CustomerService customerService;
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
#GetMapping
List<Customer> getAllCustomers() {
return customerService.findAllCustomer();
}
#GetMapping("/{id}")
public Customer getCustomerById(#PathVariable Long id) {
return customerService.findCustomerById(id);
}
}
To let Spring scan and configure #Controller annotated classes, you need to configure component scanning on packages where controllers are stored.
i.e: /src/main/java/guru/webservices/spring/config/AppConfig.java
AppConfig.java:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
#Configuration
#EnableWebMvc //THIS
#ComponentScan(basePackages = "guru.services") //THIS
public class AppConfig {
}
Also:
#Autowired
private CustomerService customerService;
And then:
#GetMapping("/{id}")
public ResponseEntity getCustomerById(#PathVariable("id") Long id) {
Customer customer = customerDAO.get(id);
if (customer == null) {
return new ResponseEntity("No Customer found for ID " + id, HttpStatus.NOT_FOUND);
}
return new ResponseEntity(customer, HttpStatus.OK);
}
If I #Autowire the BlahService with SCOPE_PROTOTYPE below, I get the IllegalArgumentException because name is null:
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
class BlahService {
private String name;
#PostConstruct
public void init()
{
If (name == null) {
throw new IllegalArgumentException("");
}
}
private void setName(String name) {
this.name = name;
}
}
class Foo {
#Autowired
private BlahService service;
}
What's the proper way to ensure name gets set in BlahService?
I assume, you have something like
#Bean
public BlahService getBlahService() {
Blahservice bean = new BlahService();
return bean;
}
and you have to modify it to
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public BlahService getBlahService() {
Blahservice bean = new BlahService();
bean.setName( findProperName() );
retunrn bewn;
}
Full tests is:
Main
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
BlahService bean1 = ac.getBean(BlahService.class);
System.out.println(bean1.getName());
BlahService bean2 = ac.getBean(BlahService.class);
System.out.println(bean2.getName());
FooService bean3 = ac.getBean(FooService.class);
bean3.print();
}
}
BlahService
package test;
public class BlahService {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
FooService
package test;
import org.springframework.beans.factory.annotation.Autowired;
public class FooService {
#Autowired
BlahService blahService;
public void print() {
System.out.println("FooService#print: " + blahService.getName());
}
}
Config
package test;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
#Configuration
public class Config {
static int counter = 0;
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public BlahService getBlahService() {
BlahService bean = new BlahService();
bean.setName("name" + counter++);
return bean;
}
#Bean
public FooService getFooService () {
return new FooService();
}
}
executing Main#main prints:
name1
name2
FooService#print: name0
edit (extended)
JUnit
package test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=Config.class)
public class MyTest {
#Autowired
BlahService blahService;
#Autowired
FooService fooService;
#Test
public void test() {
System.out.println(blahService.getName());
fooService.print();
}
}
prints:
name1
FooService#print: name0
Okay— I am assuming I am making a very obvious mistake here— but I can’t seem to find my answer through the several Google/SO searches I have done. My background with Java programming is heavily JSF based, and at work I only have experience using Faces to manage my beans (using the #Managed annotation). However, I am working on a personal project in which I am trying to use CDI for the first time. I am having trouble injecting a Service bean into a SessionScoped controller bean… It’s odd because I inject the same service bean into a different RequestScoped bean no problem… I don’t see what I am missing. I have the SessionScoped bean implementing Serializable, yet I am still getting the following error when I try to deploy, ONLY once I have added an #Inject variable to the bean (without which the bean will be pretty useless…): Caused by: org.apache.webbeans.exception.WebBeansConfigurationException: Passivation capable beans must satisfy passivation capable dependencies. Here’s a bit of my service and controller beans code:
UserService.java
import ds.nekotoba.model.User;
import java.util.Date;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import org.apache.deltaspike.jpa.api.transaction.Transactional;
import org.apache.shiro.authc.credential.DefaultPasswordService;
import org.apache.shiro.authc.credential.HashingPasswordService;
import org.apache.shiro.crypto.hash.Hash;
#Named
#Transactional//Makes all methods in bean transactional
public class UserService {
#Inject
private EntityManager em;
public User findById(Long id) {
return em.find(User.class, id);
}
public User findByUsername(String username) {
return em.createNamedQuery("User.findByUsername", User.class)
.setParameter("username", username)
.getSingleResult();
}
public User find(String username, String password) {
List<User> found = em.createNamedQuery("User.find", User.class)
.setParameter("username", username)
.setParameter("password", password)
.getResultList();
return found.isEmpty() ? null : found.get(0);
}
#Produces
#Named("users")
#RequestScoped
public List<User> list() {
return em.createNamedQuery("User.list", User.class).getResultList();
}
public Long create(User user) {
….
}
public void update(User user){
em.merge(user);
}
public void delete(User user){
em.remove(em.contains(user) ? user : em.merge(user));
}
}
LoginController.java Note: The #Inject calls in this bean work no problem!
import ds.nekotoba.model.User;
import ds.nekotoba.service.UserService;
import ds.nekotoba.util.Globals;
import java.io.IOException;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.web.util.SavedRequest;
import org.apache.shiro.web.util.WebUtils;
import org.omnifaces.util.Faces;
import org.omnifaces.util.Messages;
#Named(value="login")
#RequestScoped
public class LoginController {
public LoginController() {
}
#Inject ProfileController profile;
#Inject UserService userService;
//Variables
private String username;
private String password;
private boolean remember;
//<editor-fold desc="Getters/Setters">
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isRemember() {
return remember;
}
public void setRemember(boolean remember) {
this.remember = remember;
}
//</editor-fold>
public void submit() throws IOException {
try {//Attempt login
SecurityUtils.getSubject().login(new UsernamePasswordToken(username, password, remember));
} catch (AuthenticationException ae) {
Messages.addGlobalError("不明なユーザ、また試してみてください。");
ae.printStackTrace();
}
//If successful, set User in ProfileController, get full user info
if(SecurityUtils.getSubject().isAuthenticated()){
User user = new User();
user.setUsername(username);
user.setPassword(password);
profile.setUserInfo(user);
//profile.refreshUserInfo(); //COMMENTED OUT DUE TO ERROR INJECTED IN ProfileController.java
}
//Redirect to intended page
SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(Faces.getRequest());
Faces.redirect(savedRequest != null ? savedRequest.getRequestUrl() : Globals.HOME_URL);
}
}
ProfileController.java (The error mentioned above manifests once I use any #Inject calls in this bean…)
import ds.nekotoba.model.User;
import ds.nekotoba.service.UserService;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
#Named(value="profile")
#SessionScoped
public class ProfileController implements Serializable {
#Inject UserService userService;//CAUSES ERROR MENTIONED ABOVE
//Variables
private User userInfo;
public ProfileController() {
}
public void refreshUserInfo() {
userInfo = userService.findByUsername(userInfo.getUsername());
}
//<editor-fold desc="Getters/Setters">
public User getUserInfo() {
return userInfo;
}
public void setUserInfo(User userInfo) {
this.userInfo = userInfo;
}
//</editor-fold>
}
Like I said, I am a new-comer to CDI injection, so I am sure I am missing something obvious… I just can’t figure it out. Any help would be greatly appreciated.
Other Project Info:
JSF 2.2
JPA 2.0
TomEE 1.7.1
OmniFaces 1.8.1
Apache Shiro 1.3.0-SNAPSHOT
Apache DeltaSpike (could this be a conflicting point?)
Foo implements Serializable is necessary but not sufficient for Foo to be serializable. All members of Foo also have to be serializable.
In your case, ProfileController is serializable, but its UserService member is not.