sapjco3 驱动程序问题

Issue with sapjco3 driver

我已经编写了一个 Spring MVC (Spring framework 4.1.1) java 1.8 应用程序,它使用 sapjco3.jar 驱动程序成功连接到 SAP,我已使用 CustomDestinationDataProvider 技术完成此操作。然后我使用这个驱动器在我的 SAP R/3 系统中调用 RFC。 java 代码是通过 AngularJS 前端应用程序的 api 调用执行的。

我发现调用 SAP 时大约有 5% 的时间会发生以下错误:

NestedServletException: Handler processing failed; nested exception is 
java.lang.Error: java.lang.IllegalStateException: DestinationDataProvider 
already registered 

这是我的 CustomDestinationDataProvider.java 文件的内容:

public class CustomDestinationDataProvider {

public class MyDestinationDataProvider implements DestinationDataProvider {
    private DestinationDataEventListener eL;
    private HashMap<String, Properties> secureDBStorage = new HashMap<String, Properties>();
    public Properties getDestinationProperties(String destinationName) {
        try {
            Properties p = secureDBStorage.get(destinationName);
            if(p!=null) {
                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);
        }
    }
    public void setDestinationDataEventListener(DestinationDataEventListener eventListener) {
        this.eL = eventListener;
    }
    public boolean supportsEvents() {
        return true;
    }
    public 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
            }
        }
    }
}

public ArrayList<MaterialBean> executeAvailabilityCall(Properties connectProperties, String searchString) {
    String destName = "ABAP_AS";
    SAPDAO sapDAO = new SAPDAO(); 
    ArrayList<MaterialBean> searchResults = new ArrayList<MaterialBean>();
    MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
    JCoDestination dest;
    try {
        com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
    } catch(IllegalStateException providerAlreadyRegisteredException) {
    }
    myProvider.changeProperties(destName, connectProperties);
    try {
        dest = JCoDestinationManager.getDestination(destName);
        searchResults = sapDAO.searchAvailability(dest, searchString);
    } catch(JCoException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
    myProvider.changeProperties(destName, null);
    try {
        com.sap.conn.jco.ext.Environment.unregisterDestinationDataProvider(myProvider);
    } catch(IllegalStateException providerAlreadyRegisteredException) {
        throw new Error(providerAlreadyRegisteredException);
    }
    return searchResults;
}   // end method executeAvailabilityCall()
}   // end class CustomDestinationProvider()

我的猜测是多个 api 调用同时发生,一旦第一个查询注册了目标数据提供者,随后的查询也尝试注册目标数据提供者,但失败了,因为他们在 executeAvailabilityCall 方法中对 'destName' 使用相同的值。

乍一看,我似乎应该为 destName 变量使用动态值,而不是仅对所有查询使用 "ABAP_AS"。换句话说,我应该更改以下行:

    String destName = "ABAP_AS";

像这样:

    String destName = "ABAP_AS_" + LocalDateTime.now();

这将保证 destName 变量的值是唯一的,因此目标提供商名称是唯一的。

对尝试此操作的智慧有何想法?如果这不是一个好主意,还有什么其他解决方案值得探索?

是的,您应该为各种登录属性配置集使用多个唯一的目标名称。您的 class MyDestinationDataProvider 已经以这种方式实现。但是为什么要在目的地名称中加入时间戳呢?为什么不简单地使用像 "TargetSystem_<SID>_with_<username>" 这样的目标名称架构?

关于您的异常,只需注册 MyDestinationDataProvider 一次,不要永久注册和注销它。这不是 JCo 期望的实现方式。来自 JCo JavaDoc 的引述 com.sap.conn.jco.ext.DestinationDataProvider:

Only one implementation of DestinationDataProvider can be registered. For registering another implementation the infrastructure has first to unregister the implementation that is currently registered. It is not recommended to permanently exchange DestinationDataProvider registrations. The one registered instance should globally manage all destination configurations for the whole infrastructure environment.