0

I am building a web application using spring MVC which is connected to the database. This is part of my bean config file.

 <!-- Define Database DataSource / connection pool -->
    <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
          destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/java_task?useSSL=false" />
        <property name="user" value="me" />
        <property name="password" value="me" /> 

        <!-- these are connection pool properties for C3P0 -->
        <property name="minPoolSize" value="5" />
        <property name="maxPoolSize" value="20" />
        <property name="maxIdleTime" value="30000" />
    </bean>  

    <!-- Define Hibernate session factory -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="myDataSource" />
        <property name="packagesToScan" value="com.javatask.entity" />
        <property name="hibernateProperties">
           <props>
              <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
              <prop key="hibernate.show_sql">true</prop>
           </props>
        </property>
   </bean>   

As you can see my data source is defined in a hard way. In code I am using @Autowired annotation to inject sessionFactory. But I would like to inject sessionFactory (jdbc, username, password etc.) with data which I will obtain from user during run time. For example he will write me those data to the textfeild and then I will create sessionFactory (based on his data) which will be connected to his database.

I was looking for an answer but unluckily I have not found anything that would fit to this problem.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Druudik
  • 955
  • 1
  • 10
  • 32
  • Instead of using old way is xml configuration. You can use java configuration to build spring mvc, check this http://javabycode.com/spring-framework-tutorial/spring-mvc-tutorial/spring-mvc-hibernate-mysql-integration-crud-example-tutorial.html – David Pham Nov 12 '17 at 14:33
  • Use session-scoped datasource and pull credentials from user session. – M. Prokhorov Nov 13 '17 at 10:29

2 Answers2

1

There are multiple ways that spring bean definition can be changed in run time. One way is to refresh the Spring Application Context in run time which would have the bean definition specific to the functionality rather than all bean definition.

((ConfigurableApplicationContext)applicationContext).refresh();

Let me explain using one simple use case:

  • Create a PropertyBean which will load value from external properties file.
  • Create PropertiesApplicationContext which would have only PropertyBean definition config rather than all beans belongs the application.
  • Use ConfigurableApplicationContext refresh() method to reload ProperiesBean definition in run time.

More details about below example code snippet:

  • Web Application should display a Property Value from a bean which will read the value from a external property file.
  • Property value will change anytime in external properties file.
  • Web Application should not restarted.
  • Here, bean definition should be changed in run time.

Spring Boot Application Code:

@SpringBootApplication(scanBasePackages = {"vijay.controller","vijay.configuration"})
public class SpringContextRefreshApplication extends SpringBootServletInitializer{

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(SpringContextRefreshApplication.class); 
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringContextRefreshApplication.class, args);
    }
}

MVC Configuration:

package vijay.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * MVC Configuration
 * @author Vijay
 */
@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter{

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/view/");
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);
        registry.viewResolver(resolver);
    }    
}

Custom (Property) Application Context Aware:

package vijay.configuration;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * PropertyApplicationContext Class to load Property Configuration XML.
 * @author Vijay
 */
public class PropertyApplicationContext implements ApplicationContextAware{

    private ApplicationContext applicationContext;

    private static PropertyApplicationContext propertyApplicationContext;

    private PropertyApplicationContext(){
        applicationContext = new ClassPathXmlApplicationContext("property-config.xml");
    }

    @Override
    public void setApplicationContext(ApplicationContext ac) throws BeansException {
        if(applicationContext == null){
            this.applicationContext = ac;
        }
    }

    public ApplicationContext getApplicationContext(){
        return applicationContext;
    }

    public static PropertyApplicationContext getInstance(){
        if(propertyApplicationContext == null){
            propertyApplicationContext = new PropertyApplicationContext();
        }
        return propertyApplicationContext;
    }

}

MVC Controller:

package vijay.controller;

import vijay.beans.PropertyDto;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import vijay.configuration.PropertyApplicationContext;

/**
 * Property List Controller.
 * @author Vijay
 */
@Controller
public class PropertyController {   

    @RequestMapping("/")
    public String testController(){
        System.out.println("vijay.controller.PropertyController.testController()");
        return "sample";
    }


    @RequestMapping(path = "/getProperties") 
    public String testPropertiesController(ModelMap modelMap){
        ApplicationContext applicationContext = PropertyApplicationContext.getInstance().getApplicationContext();
        PropertyDto propertyDto = (PropertyDto) applicationContext.getBean("propertyBean");
        System.out.println("vijay.controller.PropertyController.testPropertiesController() " + propertyDto);
        modelMap.addAttribute("message", propertyDto.getKey());
        return "sample";
    }

    /**
     * Method will refresh ApplicationContext
     * @param modelMap as ModelMap
     * @return viewName as String
     */
    @RequestMapping(path = "/refreshProperties") 
    public String refreshBean(ModelMap modelMap){
        ApplicationContext applicationContext = PropertyApplicationContext.getInstance().getApplicationContext();
        // Refresh Application Context
        ((ConfigurableApplicationContext)applicationContext).refresh();
        PropertyDto propertyDto = (PropertyDto) applicationContext.getBean("propertyBean");
        System.out.println("vijay.controller.PropertyController.refreshBean()" + propertyDto);
        modelMap.addAttribute("message", propertyDto.getKey());
        return "sample";
    }

}

Spring Bean (Property-config.xml) definition File:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" >

    <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="file:C://sample.properties" />
    </bean>

    <context:annotation-config/>
    <context:component-scan base-package="vijay.beans"/>

</beans>

View JSP (sample.jsp):

<!DOCTYPE html>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<html lang="en">
<body>
    <div>
        <div>
            <h1>Spring Boot JSP Example</h1>
            <h2>Property Value :: ${message} !</h2>
        </div>
    </div>
</body>
</html>

Here, MVC Controller has /refreshProperties which will refresh the PropertiesBean defined in PropertiesApplicationContext. As properties-config.xml the only bean definition defined in PropertiesApplicationContext, propertiesBean alone will be refreshed and no other bean definition will be refreshed.

Similar way, you can create Application Context by extending ApplicationContextAware which will have bean definition of sessionFactory. You can get SessionFactory wherever required from this context and refresh the context wherever required.

This is one way to achieve the bean definition change in run time.

  • Thanks dude. Can you give me any other examples where you do not have to create external property file ? – Druudik Nov 20 '17 at 06:05
  • Another flavor: Can use @RefreshScope from spring-cloud framework to refresh stateful beans. Reference: http://cloud.spring.io/spring-cloud-static/docs/1.0.x/spring-cloud.html#_refresh_scope . But this is useful when you have micro services or spring boot project completely. – VIJAYaraaghavan Manoharan Nov 21 '17 at 00:21
0

Did you try this [Change SessionFactory datasource jdbcurl late in runtime] (Change SessionFactory datasource jdbcurl late in runtime)?

losusovic
  • 608
  • 5
  • 22
  • I am sorry but I did not quite understand the answer of that question. Can you be more specific what is required to do ? – Druudik Nov 12 '17 at 14:55