背景:
有報表項目需要抓取多個SAP主機的資料(RFC),之前的的方式是配置到數據庫,使用時臨時加載切換。後來找到資料實現隨Spingboot自動加載的實現方式,此方式僅需要數據庫配置好JCO連接,項目啟動后直接按照名字取用即可。故做技術整理。
1.關鍵文件CustomDestinationDataProvider
實現DestinationDataProvider接口,獲取JCO連接配置,重寫JCO事件監聽(對Destination的監聽與通知),JCO連接配置的安全存儲。(也可以參考代碼中的英文注釋)
import java.util.HashMap;
import java.util.Properties;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.JCoException;
import com.sap.conn.jco.ext.DataProviderException;
import com.sap.conn.jco.ext.DestinationDataEventListener;
import com.sap.conn.jco.ext.DestinationDataProvider;
/**
* Each application using Java Connector 3 deals with destinations. A destination represents a logical address
* of an ABAP system and thus separates the destination configuration from application logic. JCo retrieves
* the destination parameters required at runtime from DestinationDataProvider and ServerDataProvider registered
* in the runtime environment. If no provider is registered, JCo uses the default implementation that reads the
* configuration from a properties file. It is expected that each environment provides a suitable
* implementation that meets security and other requirements. Furthermore, only one DestinationDataProvider
* and only one ServerDataProvider can be registered per process. The reason behind this design decision
* is the following: the provider implementations are part of the environment infrastructure.
* The implementation should not be application specific, and in particular must be separated from
* application logic.
*
* This example demonstrates a simple implementation of the DestinationDataProvider interface and shows how
* to register it. A real world implementation should save the configuration data in a secure way.
*/
public class CustomDestinationDataProvider
{
//The custom destination data provider implements DestinationDataProvider and
//provides an implementation for at least getDestinationProperties(String).
//Whenever possible the implementation should support events and notify the JCo runtime
//if a destination is being created, changed, or deleted. Otherwise JCo runtime
//will check regularly if a cached destination configuration is still valid which incurs
//a performance penalty.
static class MyDestinationDataProvider implements DestinationDataProvider
{
private DestinationDataEventListener eL;
private HashMap<String, Properties> secureDBStorage = new HashMap<String, Properties>();
public Properties getDestinationProperties(String destinationName)
{
try
{
//read the destination from DB
Properties p = secureDBStorage.get(destinationName);
if(p!=null)
{
//check if all is correct, for example
if(p.isEmpty())
throw new DataProviderException(DataProviderException.Reason.INVALID_CONFIGURATION, "destination configuration is incorrect", null);
return p;
}
return null;
}
catch(RuntimeException re)
{
throw new DataProviderException(DataProviderException.Reason.INTERNAL_ERROR, re);
}
}
//An implementation supporting events has to retain the eventListener instance provided
//by the JCo runtime. This listener instance shall be used to notify the JCo runtime
//about all changes in destination configurations.
public void setDestinationDataEventListener(DestinationDataEventListener eventListener)
{
this.eL = eventListener;
}
public boolean supportsEvents()
{
return true;
}
//implementation that saves the properties in a very secure way
void changeProperties(String destName, Properties properties)
{
synchronized(secureDBStorage)
{
if(properties==null)
{
if(secureDBStorage.remove(destName)!=null)
eL.deleted(destName);
}
else
{
secureDBStorage.put(destName, properties);
eL.updated(destName); // create or updated
}
}
}
} // end of MyDestinationDataProvider
// //business logic
JCoDestination executeCalls(String destName)
{
JCoDestination dest = null;
try
{
dest = JCoDestinationManager.getDestination(destName);
dest.ping();
System.out.println("Destination " + destName + " works");
}
catch(JCoException e)
{
e.printStackTrace();
System.out.println("Execution on destination " + destName+ " failed");
}
return dest;
}
}
2.關鍵文件JcoRegisterAfterStartup
聲明CustomDestinationDataProvider,讀取數據庫里JCO連接配置數據,循環配置List動態放入JCO環境中,同時放入內存
import java.util.List;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import com.abc.provider.entity.RelSapConfigT;
import com.abc.provider.service.JcoConnService;
import com.sap.conn.jco.ext.DestinationDataProvider;
//no use
@Component
public class JcoRegisterAfterStartup implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private JcoConnService jcoConnService;
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
List<RelSapConfigT> sapConfigTList = jcoConnService.getAllJcoConn();
if (!CollectionUtils.isEmpty(sapConfigTList)) {
CustomDestinationDataProvider.MyDestinationDataProvider myProvider = new CustomDestinationDataProvider.MyDestinationDataProvider();
//register the provider with the JCo environment;
//catch IllegalStateException if an instance is already registered
try
{
com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
}
catch(IllegalStateException providerAlreadyRegisteredException)
{
//somebody else registered its implementation,
//stop the execution
throw new Error(providerAlreadyRegisteredException);
}
for (RelSapConfigT sc : sapConfigTList) {
Properties jcoInfo = initParam(sc);
myProvider.changeProperties(sc.getJcoName(), jcoInfo);
CustomDestinationDataProvider test = new CustomDestinationDataProvider();
test.executeCalls(sc.getJcoName());
}
}
}
public Properties initParam(RelSapConfigT sc) {
// TODO Auto-generated method stub
Properties connectProperties = new Properties();
connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST,sc.getJcoAshost());
connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR,sc.getJcoSysnr());
connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT,sc.getJcoClient());
connectProperties.setProperty(DestinationDataProvider.JCO_USER,sc.getJcoUser());
connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD,sc.getJcoPasswd());
connectProperties.setProperty(DestinationDataProvider.JCO_LANG,sc.getJcoLang());
connectProperties.setProperty(DestinationDataProvider.JCO_MAX_GET_TIME,sc.getJcoMaxGetTime().toString());
connectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT,sc.getJcoPeakLimit().toString());
connectProperties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, sc.getJcoPoolCapacity().toString());
System.out.println("Loading jco conn info:"+sc.getJcoName());
return connectProperties;
}
}