0

I am using Spring in my project to have dependency injection, but i need to change between production envirorment and test according User Login. (there is an checbox to production/test )

Each user can decide to choose between production database or test database.

I read about @Profile, but each profile is defined when the server or application run, but i need to change after this on the login.

How can i do this? Is It Possible with Spring?

This is some class to explain the problem:

applicationContext.xml

<?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-4.0.xsd">

    <bean class="br.com.cpb.gsa.config.AppConfig" />
    <context:component-scan base-package="br.com.cpb.gsa" />

</beans>

AppConfig Class

@Configuration
@EnableVaadin
public class AppConfig {

    private DataSource dataSource;

    @Profile("production")
    @Bean(name = "dataSource")  
    public DataSource datasourceProdction() {
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("net.sourceforge.jtds.jdbc.Driver");
        ds.setUrl("jdbc:jtds:sqlserver://ip:1433;databaseName=myDataBase");
        ds.setUsername("user");
        ds.setPassword("pass");

        return this.dataSource = ds;
    } 

    @Profile("test")
    @Bean(name = "dataSource")  
    public DataSource datasourceProdction() {
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("net.sourceforge.jtds.jdbc.Driver");
        ds.setUrl("jdbc:jtds:sqlserver://ip:1433;databaseName=myDataBaseTest");
        ds.setUsername("user");
        ds.setPassword("pass");

        return this.dataSource = ds;
    }

    public DataSource getDataSource() {
       return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
       this.dataSource = dataSource;
    }

}

GSAUI - My Vaadin Class

@Theme("gsaTheme")
@SpringUI
@SpringViewDisplay
public class GSAUI extends UI {

    private static final long serialVersionUID = -4276841722171307964L;

    public static final String VIEW_NAME = "mainView";

    @Autowired
    ApplicationContext applicationContext;

    @WebListener
    public static class MyContextLoaderListener extends ContextLoaderListener {
    }

    .....

}

DAO - UserDAO

@Repository("userDAO")
public class UserDAOImpl implements UserDAO {

    @Autowired
    private DataSource dataSource;

    @Override
    public Usuario getAuthenticatedUser(String login) {

        try (Connection conn = dataSource.getConnection()){

            //... sample code, just for explanation ...
            Usuario user = new Usuario();
            user.setLogin("test");

           return user;

        } catch (SQLException e) {
            throw new RuntimeException( e );
        }
    }
}

Service - LoginService

@Service("loginService")
public class LoginServiceImpl implements LoginService, Serializable {

    private static final long serialVersionUID = 4014652022146807624L;

    @Autowired
    private UserDAO userDAO;

    public Usuario doLogin(Usuario user){

        if ((user == null) ||                 (JavaUtil.isNull(user.getLogin(),"").trim().length() == 0)){
        throw new RuntimeException(Constant.LOGIN_OR_PASSWORD_NOT_PROVIDED);
        }

        //UsuarioDAO dao = (UsuarioDAO)        applicationContext.getBean("usuarioDAO");   
        Usuario savedUser = userDAO.getAuthenticatedUser(user.getLogin());  

        if  ( (savedUser == null) ||   (!savedUser.getSenha().equals(user.getSenha())) ){
            throw new RuntimeException(Constant.INVALID_USER_OR_PASSWORD);
        }

        return user;
    }

}

LoginView - User's Login and decide profile to use

@SpringView(name = LoginView.VIEW_NAME)
public class LoginView extends LoginDesign implements View {

    private static final long serialVersionUID = 1L;
    public static final String VIEW_NAME = "loginView";

    public LoginView() {
        setSizeFull();

        btnEnter.setClickShortcut(KeyCode.ENTER);
        btnEnter.addClickListener(e -> this.login());
    }

    public void login() {
        Usuario user = new Usuario();

        user.setLogin(login.getValue());
        user.setSenha(senha.getValue());

        /********  Here i would like to define the profile, but the context      already have been loaded ********/
        ConfigurableEnvironment env = (ConfigurableEnvironment)    ((GSAUI)getUI()).getApplicationContext().getEnvironment();

        if (user.getSenha() == "spedtjob"){
            env.setActiveProfiles("producao");
        }else{
            env.setActiveProfiles("test");
        user.setSenha("spedtjob");
        }

        LoginService loginService = (LoginService)    ((GSAUI)getUI()).getApplicationContext().getBean(LoginService.class);
        Usuario loggedUser = loginService.doLogin(user);

        System.out.println(loggedUser.getLogin());
        getUI().removeStyleName("loginview");

        ((GSAUI)getUI()).setUser(loggedUser);

        getUI().getNavigator().navigateTo(((GSAUI)getUI()).getRedirectPage());      
        ((Window)getParent()).close();
    }


}

I am testing with this approach but don't work. I would appreciate some help.

Weles
  • 1,275
  • 13
  • 17
  • 2
    *Why* are you doing it this way instead of launching separately? If you have a nasty bug in your staging code, and you're switching programmatically, your production system is just as at risk as if you eliminated the staging environment altogether. – chrylis -cautiouslyoptimistic- Dec 12 '16 at 19:06
  • Hi @chrylis! I understand the risk, but we have 15 companys that use the same system and to deploy 15 systems would be complicate for us. – Weles Dec 12 '16 at 19:21

2 Answers2

1

You could somehow save the users choice (in a database, inside a bean, etc...) and have connections to both databases. When accessing a database, do a check to decide which database to actually access.

You would probably want to create a bean which is dedicated to deciding which database to use for each user.

However, I would suggest doing what @chrylis said and having separate deployments if your budget allows. It will avoid a boatload of issues (such as the one already pointed out), in addition to reducing your code's complexity.

Adam
  • 2,214
  • 1
  • 15
  • 26
  • Hi @Adam Rosini I can't see how to do this, because the dataSource is injected by "@Autowire" annotation and i can´t pass parameter. Have you a sample? – Weles Dec 13 '16 at 12:27
  • @Weles Inject two datasources (testDatasource, prodDatasource). http://stackoverflow.com/questions/30362546/how-to-use-2-or-more-databases-with-spring. But you will have to be careful to make sure to always use the correct datasource! It is risky but if its what you really want, I would make a bean with a the following method: `getDataSourceForUser(...)` – Adam Dec 13 '16 at 15:13
  • Hi @Adam Rosini! Ok i understood. I would like to find an option like profiles, where was possible to change at runtime. but thanks for explanation. – Weles Dec 13 '16 at 19:28
1

before I give an answer, I would also like to advise to run test and production systems completely separately - I would try to make only a common login page, let the user login and then redirect to the correct server based on the user selection of the system.

But if you want to have a test/production in single JVM then I think that "spring bean scopes" are what you are looking for: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-scopes

The default scope is "singleton" which means, that the beans are created and wired up at the start of the application and it's then very difficult to change them.

If you want to use different DataSource bean for User A and different for User B, then you probably need to use "session" scope for your DataSource bean (maybe even "request" scope) - that means that even though you use @Autowired for the DataSource, the actual bean will be created for each session -> meaning that you can control what bean will be used for each logged in user...

Břetislav Wajtr
  • 338
  • 1
  • 10
  • i am using spring with vaadin and for some reason session or request don´t work for me, but prototype yes. Thanks! – Weles Dec 14 '16 at 18:28