Ich habe eine Spring MVC (Spring Framework 4.1.1) Java 1.8-Anwendung geschrieben, die erfolgreich mit SAP mit dem sapjco3.jar-Treiber verbindet, und ich habe dies mithilfe der CustomDestinationDataProvider-Technik erreicht. Ich benutze dieses Laufwerk dann, um RFCs in meinem SAP-R/3-System aufzurufen. Der Java-Code wird über einen API-Aufruf von einer AngularJS-Frontend-Anwendung ausgeführt.Problem mit sapjco3-Treiber
Etwas, das ich habe etwa 5% der Zeit entdeckt auftreten, dass der Aufruf von SAP geschieht, ist der folgende Fehler auftritt:
NestedServletException: Handler processing failed; nested exception is
java.lang.Error: java.lang.IllegalStateException: DestinationDataProvider
already registered
Hier ist der Inhalt meiner CustomDestinationDataProvider.java Datei:
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()
Meine Vermutung ist, dass mehrere API-Aufrufe zur gleichen Zeit auftreten, und sobald die erste Abfrage den Zieldatenanbieter registriert, schlagen die nachfolgenden Abfragen, die versuchen, auch den Zieldatenanbieter registrieren, weil sie den gleichen Wert verwenden zum ' Zielname 'in der executeAvailabilityCall-Methode.
Auf den ersten Blick scheint es mir, als ob ich einen dynamischen Wert für die Variable destName verwenden sollte, anstatt nur "ABAP_AS" für alle Abfragen zu verwenden.
String destName = "ABAP_AS";
etwas wie folgt aus:: Mit anderen Worten, ich sollte die folgende Zeile ändern
String destName = "ABAP_AS_" + LocalDateTime.now();
einen eindeutigen Wert für die Variable Destname Dies würde gewährleisten, so dass ein einzigartiges Ziel Anbieternamen.
Irgendwelche Gedanken über die Weisheit, dies zu versuchen? Wenn das keine gute Idee ist, welche andere Lösung wäre es wert, erforscht zu werden?
Ok. In Bezug auf die Registrierung 'MyDestinationDataProvider' nur einmal, sollte ich versuchen, dies über Singleton Style-Klasse zu tun, oder sollte ich nur bedingte Logik um den 'registerDestinationDataProvider' Linie, die nur läuft, wenn Data Provider noch nicht registriert wurde? – Stephen
Ich empfehle die Registrierung des 'DestinationDataProvider' innerhalb einer Initialisierungsroutine, die nur einmal aufgerufen wird.Wiederholtes Überprüfen, ob der 'DestinationDataProvider' bereits registriert wurde, ist meiner Meinung nach kein effizienter Codierungsstil. – Trixx
Das klingt nach einer guten Idee. Vielen Dank für Ihre Anregungen! :) – Stephen