Spring bean change configuration on runtime

125 Views Asked by At

I need to session manager to connection SAP server. I use spring bean to create session manager. I get SAP server configuration from hibersap.xml. I was connecting to a single server before. But now I need to connect to two or more servers. In order to read the necessary configuration from hibersap.xml, I need to update the spring bean on runtime, how can I do it?

Session manager of 2 separate whose names are BHD and AHD.

<session-manager name="BHD">
    <context>org.hibersap.execution.jco.JCoContext</context>
    <properties>
        <property name="jco.client.client" value="111" />
        <property name="jco.client.user" value="AAA" />
        <property name="jco.client.passwd" value="11Q1Q1Q1Q" />
        <property name="jco.client.ashost" value="11.11.11.11" />
        <property name="jco.client.sysnr" value="00" />
    </properties>
    <annotated-classes>
        <annotated-class>com.ldap.connection.sap.bapi.RfcLdapParameters</annotated-class>
        <annotated-class>com.ldap.connection.sap.bapi.RfcSapUserDetails</annotated-class>
        <annotated-class>com.ldap.connection.sap.bapi.UsernameAndMailModel</annotated-class>
    </annotated-classes>
</session-manager>
<session-manager name="AHD">
    <context>org.hibersap.execution.jco.JCoContext</context>
    <properties>
        <property name="jco.client.client" value="222" />
        <property name="jco.client.user" value="BBB" />
        <property name="jco.client.passwd" value="2Q2Q2Q2Q22" />
        <property name="jco.client.ashost" value="22.22.22.22" />
        <property name="jco.client.sysnr" value="00" />
    </properties>
    <annotated-classes>
        <annotated-class>com.ldap.connection.sap.bapi.RfcLdapParameters</annotated-class>
        <annotated-class>com.ldap.connection.sap.bapi.RfcSapUserDetails</annotated-class>
        <annotated-class>com.ldap.connection.sap.bapi.UsernameAndMailModel</annotated-class>
    </annotated-classes>
</session-manager>

Updating the BHD value in this line (-> configuration = new AnnotationConfiguration("BHD")) below to AHD will solve the problem. but how can I do that at runtime?

@Configuration
@Slf4j
public class HibersapConfiguration {    

@Bean
public SessionManager sessionManager() {
    String sname = System.getProperty("java.systemname");
    AnnotationConfiguration configuration = null;
    
    if(sname != null) {
        if(BtcConstants.LIVE_SYSTEM_NAME.equals(sname)) {
            log.info("SAP üretim canlı sisteme bağlanılıyor...");
        }
        
        if(BtcConstants.DEV_SYSTEM_NAME.equals(sname)){
            configuration = new AnnotationConfiguration("BHD");
            log.info("SAP üretim dev sisteme bağlanılıyor...");
        }
        
    }else {
        configuration = new AnnotationConfiguration("BHD");
        log.info("Local'den SAP üretim dev sisteme bağlanılıyor...");
    }
    
    
    return configuration.buildSessionManager();
 } }

The repository where I inject the session manager:

@Slf4j
@Repository
public class SapUserRepository implements SapUserRepositoryI {

private final SessionManager sessionManager;

public SapUserRepository(SessionManager sessionManager) {
    this.sessionManager = sessionManager;
}

@Override
public List<SapUser> findPersonalDetailFromSap(Date currentDate, String pernr) {

    List<SapUser> sapUserList = new ArrayList<SapUser>();
    RfcSapUserDetails details = new RfcSapUserDetails();
    details.setCurrentDate(currentDate);
    details.setPernr(pernr);
    
    Session session = null;
    if(sessionManager != null) {
        log.info("'findPersonalDetailFromSap' için session açıldı");
        session = sessionManager.openSession();
        
        try {
            log.info("Personel verileri çekiliyor...");
            session.execute(details);
            sapUserList = details.getSapUserList();
        } finally {
            session.close();
            log.info("session kapatıldı");
        }
    }else {
        log.error("sessionManager null geldi !");
    }

    return sapUserList;
}
1

There are 1 best solutions below

0
Kamii0909 On

I'm not an expert on SAP technology, but you probably want to create 2 separate SessionManagers and inject them by name(@Qualifier).

class SAPConfig {
  // return a bean of BHD here
  @Bean("BHDSAPServer")
  SessionManager createBHDServer() { ... } 
  
  // return a bean of AHD here
  @Bean("AHDSAPServer")
  SessionManager createAHDServer() { ... }
}

// this snippet is in SAPUserRepository
public SapUserRepository(
  @Qualifier("BHDSAPServer") SessionManager bhdSessionManager,
  @Qualifier("AHDSAPServer") SessionManager ahdSessionManager) {
    this.bhdSessionManager = bhdSessionManager;
    this.ahdSessionManager = ahdSessionManager;
}

How do Spring wants to know which Server you want to connect to in your repository? You have to tell it to Spring yourself. The above code is one of such way.

Now if you want to have AHD Server as a fallback (if connect to BHD fails, connect to AHD), you can create a nice wrapper like

@Component
class ChainedSAPServer {
  Map<String, SessionManager> sessionManagers;
  
  // Spring will automatically inject (@Autowired) every
  // SessionManager coercible (can be cast to SessionManager)
  // beans in this constructor. If you happen to not use 
  // classpath scanning (@ComponentScan), Spring will also
  // 
  ChainedSAPServer(List<SessionManager> sessionManagers) {
    this.sessionManagers = sessionManagers
       .stream()
       .collect(Collectors.toMap(sm -> sm.getName(), Function.identity());
  }

  Session openSession() {
    // check each session manager one by one, if one fails go to next
  }

  // You can also smartly decide which SessionManager is
  // responsible for what usage. You can simply inject only
  // this wrapper to your repositories.
  Session openSessionWith(String sessionManagerName) {
    return sessionManagers.get(sessionManagerName).openSession();
  }
}