2017-05-26 1 views
2

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?

Antwort

0

Ja, Sie sollten mehrere eindeutige Zielnamen für Ihre verschiedenen Konfigurationssätze für die Anmeldeeigenschaften verwenden. Ihre Klasse MyDestinationDataProvider ist bereits auf diese Weise implementiert. Warum aber einen Zeitstempel in den Zielnamen einfügen? Warum nicht einfach ein Zielnamensschema wie "TargetSystem_ <SID> _ with_ <Benutzername>" verwenden?

In Bezug auf Ihre Ausnahme, registrieren Sie einfach MyDestinationDataProvider nur einmal und nicht dauerhaft registrieren und die Registrierung aufheben. JCo geht davon aus, dass dies nicht umgesetzt wird. Zitat aus dem JCo JavaDoc unter :

Es kann nur eine Implementierung von DestinationDataProvider registriert werden. Zum Registrieren einer anderen Implementierung muss die Infrastruktur zuerst die Registrierung der aktuell registrierten Implementierung aufheben. Es ist nicht empfohlen, DestinationDataProvider Registrierungen dauerhaft auszutauschen. Die eine registrierte Instanz sollte alle Zielkonfigurationen für die gesamte Infrastrukturumgebung global verwalten.

+0

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

+0

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

+0

Das klingt nach einer guten Idee. Vielen Dank für Ihre Anregungen! :) – Stephen