I am trying to change bean property value during the runtime.
WebConfig
#Configuration
public class WebConfig extends WebMvcConfigurationSupport {
#Autowired
private SecurityService service;
#Bean
public SecurityPolicy securityPolicy() {
SecurityPolicy policy = new SecurityPolicy();
//takes data from db, it works fine
policy.setMaxAttempt = service.getMaxAttempts();
return policy;
}
}
Controller
#Controller
public class SecurityPolicyController {
#Autowired
private SecurityPolicy policy;
#Autowired
private ApplicationContext context;
#Autowired
private SecurityService service;
#RequestMapping(value = "/security")
public ModelAndView update() {
ModelAndView model = new ModelAndView();
//set data to db, it works fine aswell
service.setMaxAttempts(7);
//now i am trying to reload my beans
((ConfigurableApplicationContext)context).refresh();
//something reloading but i still get the same value
System.out.println(policy.getMaxLoginAttempts());
model.setViewName("security");
return model;
}
}
Changing the value occurs only when the server is rebooted.
Can you suggest example how to achieve bean reloading during the runtime or tell what I'm doing wrong? All help appreciated
why not inject the service into policy? and everytime you call the policy.getMaxLoginAttempts() the call gets delegated to service.getMaxAttempts(). So you get new values returned, without having to reload.
So the config looks like this:
#Bean
public SecurityPolicy securityPolicy() {
return new SecurityPolicy(service);
}
And the SecurityPolicy.getMaxLoginAttempts() like this:
public int getMaxLoginAttempts(){
return service.getMaxAttempts();
}
Related
I try to test my spring app but encounter following problem:
In "normal mode"(mvn spring-boot:run) the app starts as expected and adapterConfig gets set and is NOT NULL. When I start my testclass to test the MVC, adapterConfig does not get set. Spring ignores the whole config class.
test:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = StudentController.class)
public class StudentControllerTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private StudentService service;
#MockBean
private StudentRepository repository;
#Test
public void shouldReturnABC() throws Exception{
MvcResult result = this.mockMvc.perform(get("/students/abc")).andReturn();
}
}
controller:
#RestController
#RequestMapping("/students")
#PermitAll
public class StudentController {
#Autowired
StudentService studentService;
//get
#GetMapping("/abc")
public String abc (){
return "abc";
}
config:
#Configuration
public class SpringBootKeycloakConfigResolver implements KeycloakConfigResolver {
private KeycloakDeployment keycloakDeployment;
private AdapterConfig adapterConfig;
#Autowired
public SpringBootKeycloakConfigResolver(AdapterConfig adapterConfig) {
this.adapterConfig = adapterConfig;
}
#Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
if (keycloakDeployment != null) {
return keycloakDeployment;
}
keycloakDeployment = KeycloakDeploymentBuilder.build(adapterConfig);
return keycloakDeployment;
}
}
adapterConfig is null when hitting the test but gets set & created when hitting it the normal way, any idea?
Using #WebMvcTest, the container will inject only components related to Spring MVC (#Controller, #ControllerAdvice, etc.) not the full configuration use #SpringBootTest with #AutoConfigureMockMvc instead.
Spring Boot Javadoc
Keycloak's AutoConfiguration is not included by #WebMvcTest.
You could
Include it manually via #Import(org.keycloak.adapters.springboot.KeycloakSpringBootConfiguration.class)
Or use #SpringBootTest
with spring boot 2.5 i had I had to import KeycloakAutoConfiguration into my test.
#WebMvcTest(value = ApplicationController.class, properties = "spring.profiles.active:test")
#Import(KeycloakAutoConfiguration.class)
public class WebLayerTest {
// ... test code ....
}
This question already has answers here:
Spring and hibernate: No Session found for current thread
(6 answers)
Closed 4 years ago.
I'm getting this error when I try to upload a picture in my project. The project executes fine until it has to effectively upload the picture to the database (I'm using postgresql), but this last step never works.
The following code was updated having considered the answers below.
Here's my controller (a part of it):
#Autowired
private FileUploadImpl fileUploadImpl;
...
#RequestMapping(value = "publish4" ,method = RequestMethod.POST)
public ModelAndView publish4(#Valid #ModelAttribute("fourthPublicationForm") final FourthPublicationForm form, final BindingResult errors,
#RequestParam("type") String type, #RequestParam("operation") String operation , #RequestParam CommonsMultipartFile[] fileUpload) {
if (errors.hasErrors()) {
//return helloPublish3(form,operation,type);
}
System.out.println("operation: "+ operation);
System.out.println("type: "+ type);
ps.create(form.getTitle(), form.getAddress(), operation, form.getPrice(), form.getDescription(),
type, form.getBedrooms(), form.getBathrooms(), form.getFloorSize(), form.getParking());
if (fileUpload != null && fileUpload.length > 0) {
for (CommonsMultipartFile aFile : fileUpload){
System.out.println("Saving file: " + aFile.getOriginalFilename());
UploadFile uploadFile = new UploadFile();
uploadFile.setAddress(form.getAddress());
uploadFile.setData(aFile.getBytes());
fileUploadImpl.save(uploadFile);
}
}
return new ModelAndView("redirect:/hello/home");
}
This is fileUploadDao in interface:
public interface FileUploadDao {
void save(UploadFile uploadFile);
}
This is in services:
#Service
public class FileUploadImpl {
#Autowired
private FileUploadDao fileUploadDao;
public FileUploadImpl() {
}
#Transactional
public void save(UploadFile uploadFile) {
fileUploadDao.save(uploadFile);
}
}
THe following in persistence:
#Repository
public class FileUploadDAOImpl implements FileUploadDao {
#Autowired
private SessionFactory sessionFactory;
public FileUploadDAOImpl() {
}
public FileUploadDAOImpl(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public void save(UploadFile uploadFile) {
sessionFactory.getCurrentSession().save(uploadFile);
}
}
I got this in WebConfig.java (among other stuff)
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(
new String[] { "ar.edu.itba.paw" }
);
//sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Autowired
#Bean(name = "fileUploadDao")
public FileUploadDao getUserDao(SessionFactory sessionFactory) {
return new FileUploadDAOImpl(sessionFactory);
}
#Bean(name = "multipartResolver")
public CommonsMultipartResolver getCommonsMultipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(20971520); // 20MB
multipartResolver.setMaxInMemorySize(1048576); // 1MB
return multipartResolver;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(
SessionFactory sessionFactory) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
A little bit more of the error:
org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:106)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at ar.edu.itba.paw.persistence.FileUploadDAOImpl.save(FileUploadDAOImpl.java:25)
at ar.edu.itba.paw.webapp.controller.HelloWorldController.publish4(HelloWorldController.java:260)
I've seen other questions where the answer was the lack of use of "transactional". I'm using that annotation here, but I'm not sure if the way it's 100% correct.
First remove #Transactional from FileUploadDAOImpl.
Change base package accordingly,
sessionFactory.setPackagesToScan(
new String[] { "base.package.to.scan" }
);
base.package.to.scan seems like invalid base package naming, change it to ar.edu.itba.paw.
You need a transaction manager to get use of #Transactional. Add it to WebConfig
#Bean
#Autowired
public HibernateTransactionManager transactionManager(
SessionFactory sessionFactory) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory());
return txManager;
}
This might get this code work, give it a try.
UPDATE: Also make sure following annotations present on WebConfig class,
#Configuration
#ComponentScan({"ar.edu.itba.paw"})
#EnableTransactionManagement(mode = AdviceMode.PROXY)
public class WebConfig {
// code
}
As you said from the first place, you have confused the actual layers. Still you could make it work properly in your situation but lets discuss a bit your implementation.
FileUploadDao is it a DAO or is it a Service ?
FileUploadImpl seems that you're confusing #Service with #Repository ,
maybe reading this out might help you. Spring Data Repositories , Spring Service Annotation
You ve made a transactional method , save in which i cannot say what you want to achieve exactly. You are also autowiring both FileUploadDao and SessionFactory, although you want to implement the first and inside the method you are trying to persist the object twice by first calling save upon the repository (thats a StackOverflowError from the first place, but you are lucky because Spring knows what to autowire) and then you are trying to call save a second time upon the Hibernate's SessionFactory , which breaks the abstract JPA contract. Also if you noticed , the error at the logs you posted , comes from the second save.
#Transactional not going to discuss how is this working , as you haven't posted your whole app-config. But again , you could read this for more info.
So based on the examples you shared , i am going to prepare 2 cases which might help you understand whats going on underneath.
First Case , Spring DATA , not really care if its Hibernate or another JPA provider underneath.
Your FileUploadImpl Becomes : FileUploadService
#Service
public class FileUploadService {
#Autowired
private FileUploadDao fileUploadDao;
public FileUploadService() {
}
#Transactional
public void save(UploadFile uploadFile) {
fileUploadDao.save(uploadFile);
}
}
Inside your controller , you are Autowiring the Service (layer) not directly the Repository/DAO(layer). There is not anything that stops you tho , its just a matter of design(if you still not get that point, raise another question).
A part of your part's Controller
#Autowired
private FileUploadService fileUploadService;
#RequestMapping(value = "publish4" ,method = RequestMethod.POST)
public ModelAndView publish4(#Valid #ModelAttribute("fourthPublicationForm") final FourthPublicationForm form, final BindingResult errors,
#RequestParam("type") String type, #RequestParam("operation") String operation , #RequestParam CommonsMultipartFile[] fileUpload) {
.........
fileUploadService.save(uploadFile);
}
Second Case , if you really want to use hibernate goodies , then there is not any reason autowiring the Repository , but simply implement those calls by yourself.
import org.springframework.stereotype.Component;
#Component
public class FileUploadDao {
#Autowired
private SessionFactory sessionFactory;
public FileUpload save(FileUpload obj) {
return sessionFactory.getCurrentSession().save(obj);
}
public FileUpload merge(FileUpload obj) {
return sessionFactory.getCurrentSession().merge(obj);
}
..... delete / update / or custom queries(SQL/JPQL/HQL) can be placed here
}
Your service simply exposes those methods , check the difference , i am applying the #Transactional annotation on this layer(ofc again you can put it in the DAO layer, but as i said its a matter of design).
#Service
public class FileUploadService {
#Autowired
private FileUploadDao fileUploadDao;
public FileUploadService() {
}
#Transactional
public UploadFile save(UploadFile uploadFile) {
fileUploadDao.save(uploadFile);
}
#Transactional
public UploadFile merge(UploadFile uploadFile) {
fileUploadDao.merge(uploadFile);
}
....rest of the methods you want to expose , or combinations of mulitple DAOs
}
Your controller remains the same , and thats the actual reason you need to have layers.
My configuration class where I initialize the hessian bean
#RefreshScope
#Configuration // #Component also not working
public class HessianConfiguration {
#Value("${sample.hessian.url}")
private String sampleUrl;
#Bean
public HessianProxyFactoryBean initSampleBean() {
HessianProxyFactoryBean invoker = new HessianProxyFactoryBean();
invoker.setServiceUrl(sampleUrl);
invoker.setServiceInterface(Sample.class);
return invoker;
}
}
And a sample component class where I use the bean.
#RefreshScope
#Component
public class SampleService {
#Autowired
Sample sample;
public String doRemoteOperation(String value){
return sample.doRemoteOperation(value);
}
}
sample.hessian.url is retrieved from spring config server.
But after changing the value of sample.hessian.url and calling "refresh" endpoint, the autowired sample bean is still trying to hit the old url value.
What am I doing wrong here ?
I'm using Spring Boot 1.3.5 with Rest Controllers and everything is working fine.
I am also using Spring's validation sample techniques from the official documentation (JSR-303 Bean Validation API and Spring's validator interface, i tried both and faced the same problem) and the validations are working, but I am not able to configure custom messages.
I have configured a messages.properties file, and I can access the messages on this file just fine. However this validation seems not to be capable of reading or accessing my messages source (messages.properties) configured automatically via spring boot.
I can access the messages directly from the messages source object injected in controller via #Autowired (there's a comment in the code). However, the binding result of the Spring's validator interface or the JSR-303 Bean Validation seems to not be capable of accessing the messages.properties loaded in MessageSource. The result I have is that my errors have codes but don't have default messages.
Here is my Application class:
#SpringBootApplication
#ImportResource({ "classpath:security/cas-context.xml", "classpath:security/cas-integration.xml",
"classpath:security/security.xml" })
#EnableAutoConfiguration(exclude = VelocityAutoConfiguration.class) // http://stackoverflow.com/questions/32067759/spring-boot-starter-cache-velocity-is-missing
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServletRegistrationBean cxfServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/services/*");
}
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
#Bean
public Nfse nfseService() {
return new NfseImpl();
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), nfseService());
endpoint.publish("/nfseSOAP");
return endpoint;
}
}
Here is my Bean:
public class Protocolo {
private Long id;
#NotNull
#Min(1)
#Max(1)
private String protocolo;
private StatusProtocoloEnum status;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getProtocolo() {
return protocolo;
}
public void setProtocolo(String protocolo) {
this.protocolo = protocolo;
}
public StatusProtocoloEnum getStatus() {
return status;
}
public void setStatus(StatusProtocoloEnum status) {
this.status = status;
}
}
Here is My rest controller:
#RestController
public class ProtocoloController {
#Autowired
private MessageSource messageSource;
#Autowired
private ProtocoloDAO protocoloDAO;
#RequestMapping(value = "/prot", method = RequestMethod.POST)
public void testar(#Valid #RequestBody Protocolo p) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println(auth.getAuthorities());
System.out.println(messageSource.getMessage("protocolo.tamanho", null, null));
// IN THIS PART I'M ABLE TO PRINT THE MESSAGE IF VALIDATION IS DISABLED
System.out.println(p.getProtocolo());
}
}
So, this code works fine and the method is not called since i'm calling the method with a invalid Protocolo. However, my angularJS client receives the response with the errors codes populated but with all the default messages empty since the validation is not seeing my loaded messages.properties.
Is there a way to make my Spring validation Interfaces or JSR-303 validation incorporate the loaded message.properties (messagesource) in spring boot ? How can i correct this ? If it's necessary i can paste my code sample of Spring Validation interfaces also.
Thank's a lot,
Tarcísio.
TEST CODE:
#RestController
public class ProtocoloController {
#Autowired
private MessageSource messageSource;
#Autowired
private ProtocoloDAO protocoloDAO;
#RequestMapping(value = "/prot", method = RequestMethod.POST)
public void testar(#Valid #RequestBody Protocolo p, BindingResult bindingResult) {
System.out.println(messageSource.getMessage("Min.protocolo.protocolo", null, null));
if (bindingResult.hasErrors()) {
System.out.println(bindingResult.getFieldError().getDefaultMessage());
System.out.println(bindingResult.getFieldError().getCode());
}
System.out.println(p.getProtocolo());
}
}
Edit:
Known Bug in Spring Boot 1.5.3 see https://github.com/spring-projects/spring-boot/issues/8979
In Spring Boot since 1.5.3 you need to do this
#Configuration
public class ValidationMessageConfig {
#Bean
public LocalValidatorFactoryBean mvcValidator(MessageSource messageSource) {
LocalValidatorFactoryBean factory = new LocalValidatorFactoryBean();
factory.setValidationMessageSource(messageSource);
return factory;
}
}
and then it will work.
With version 1.5.2 and before you can extend WebMVcConfigurerAdapter
#Configuration
public class ProfileMvcConfig extends WebMvcConfigurerAdapter {
private MessageSource messageSource;
#Autowired
public ProfileMvcConfig(MessageSource messageSource) {
this.messageSource = messageSource;
}
/**
* This method is overridden due to use the {#link MessageSource message source} in bean validation.
*
* #return A Validator using the {#link MessageSource message source} in bean validation.
*/
#Override
public Validator getValidator() {
LocalValidatorFactoryBean factory = new LocalValidatorFactoryBean();
factory.setValidationMessageSource(messageSource);
return factory;
}
}
also see the documentation
In Spring Boot applicaton MessageSource is configured with a MessageSourceAutoConfiguration and you don't need to autowire it. For jsr303, create proper key-value pair in the messages.properties file. For "protocolo" field, you should have following values in property file.
NotNull.protocolo.protocolo=Field cannot be left blank
Min.protocolo.protocolo=Minimum value must be {1}
You can also check messages from property file like below in your code.
public void testar(#Valid #RequestBody Protocolo p,BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
System.out.println(bindingResult.getFieldError().getDefaultMessage());
}
}
you should have following values in property file:
Min.protocolo.protocolo=Minimum value must be {1}
then in the controller you obtain the message by calling function getMessage from messageSource object
Test code:
#RequestMapping(value = "/prot", method = RequestMethod.POST)
public void testar(#Valid #RequestBody Protocolo p, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
bindingResult.getFieldErrors().forEach(fieldError ->
System.out.println(messageSource.getMessage(fieldError, Locale.getDefault()))
);
}
System.out.println(p.getProtocolo());
}
I solved this in custom message in Spring validation read the last part of my answer.
Check this example as well.
I used a custom validator with custom annotation. I needed to change code in my custom validator.
public class PersonValidator implements ConstraintValidator {
#Override
public boolean isValid(final Person person, final ConstraintValidatorContext context) {
if (somethingIsInvalid()) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("Something is invalid.").addConstraintViolation();
return false;
}
return true;
}
}
I have several classes in a Spring Boot project, some work with #Autowired, some do not. Here my code follows:
Application.java (#Autowired works):
package com.example.myproject;
#ComponentScan(basePackages = {"com.example.myproject"})
#Configuration
#EnableAutoConfiguration
#EnableJpaRepositories(basePackages = "com.example.myproject.repository")
#PropertySource({"classpath:db.properties", "classpath:soap.properties"})
public class Application {
#Autowired
private Environment environment;
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Bean
public SOAPConfiguration soapConfiguration() {
SOAPConfiguration SOAPConfiguration = new SOAPConfiguration();
SOAPConfiguration.setUsername(environment.getProperty("SOAP.username"));
SOAPConfiguration.setPassword(environment.getProperty("SOAP.password"));
SOAPConfiguration.setUrl(environment.getProperty("SOAP.root"));
return SOAPConfiguration;
}
HomeController (#Autowired works):
package com.example.myproject.controller;
#Controller
class HomeController {
#Resource
MyRepository myRepository;
MyService (#Autowired does not work):
package com.example.myproject.service;
#Service
public class MyServiceImpl implements MyService {
#Autowired
public SOAPConfiguration soapConfiguration; // is null
private void init() {
log = LogFactory.getLog(MyServiceImpl.class);
log.info("starting init, soapConfiguration: " + soapConfiguration);
url = soapConfiguration.getUrl(); // booom -> NullPointerException
I do not get the SOAPConfiguration but my application breaks with a null pointer exception when I try to access it.
I have already read many Threads here and googled around, but did not find a solution yet. I tried to deliver all necessary information, please let me know if anything misses.
I guess you call init() before the autowiring takes place. Annotate init() with #PostConstruct to make it call automatically after all the spring autowiring.
EDIT: after seeing your comment, I guess you are creating it using new MyServiceImpl(). This takes away the control of the MyServiceImpl from Spring and gives it to you. Autowiring won't work in those case
Did you created a bean for the class SOAPConfiguration in any of your configuration classes? If you want to autowire a class in your project, you need to create a bean for it. For example,
#Configuration
public class SomeConfiguration{
#Bean
public SOAPConfiguration createSOAPConfiguration(){
return new SOAPConfiguration();
}
}
public class SomeOtherClass{
#Autowired
private SOAPConfiguration soapConfiguration;
}