2

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

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

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 调用同时发生,一旦第一个查询注册了目标数据提供者,随后的查询,也尝试注册目标数据提供者,失败,因为它们使用相同的值 'destName ' 在 executeAvailabilityCall 方法中。

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

    String destName = "ABAP_AS";

像这样:

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

这将保证 destName 变量的唯一值,从而保证唯一的目标提供程序名称。

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

4

1 回答 1

0

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

关于您的例外,只需注册一次 MyDestinationDataProvider ,不要永久注册和注销它。这不是 JCo 期望的实现方式。引用 JCo JavaDoc 的内容com.sap.conn.jco.ext.DestinationDataProvider

只能注册 DestinationDataProvider 的一种实现。为了注册另一个实现,基础设施必须首先取消注册当前注册的实现。不建议永久交换 DestinationDataProvider 注册。一个注册的实例应该全局管理整个基础设施环境的所有目标配置。

于 2017-05-26T17:02:26.933 回答