SAPJCo

Um aus Java eine Verbindung zu SAP herzustellen, wird der SAP Java Connector (SAPJCO) benötigt. Dieser kann vom SAP Marketplace downgeloadet werden – eventuell auch aus anderen Quellen.

Der SAP Java Connector (SAP JCo) ist eine Middleware-Komponente, die die Entwicklung von SAP-fähigen Komponenten und Anwendungen in Java ermöglicht. SAP JCo unterstützt die Kommunikation mit dem SAP Server in beiden Richtungen: inbound (Java ruft ABAP) und outbound calls (ABAP ruft Java).

Der SAP JCo kann sowohl mit Desktop- als auch mit (Web)Server-Anwendungen eingesetzt werden.

Mit dem SAPJCo Download erhält man auch Beispielprogramme, eines davon ist der „StepByStepClient“. Dieses kann auch hier betrachtet werden. Dieser zeigt, wie man die SAP Verbindung herstellt und einige Funktionsbausteine bzw. BAPIs aufruft. Es wird auch darauf hingewiesen, dass die Destinationskonfiguration in einer Datei abgelegt wird und dies aus Sicherheitsgründen in der Praxis vermieden werden soll. Aber wie soll das nun gemacht werden? Seltsam, dass dafür kein Beispiel existiert…

Ich will eine Lösung dafür vorstellen!

Hierzu wird die Abstrakte Klasse com.sap.conn.jco.ext.DestinationDataProvider implementiert. Die erstellte Klasse „MyDestinationDataProvider“ hat intern eine HashMap, in welcher die Verbindungsdaten als Properties abgelegt werden. Die überschriebene Methode „getDestinationProperties“ liefert dann zum übergebenen DestinationName die passenden Properties an die Aufrufende Methode zurück.

import com.sap.conn.jco.AbapException;import com.sap.conn.jco.JCoDestination;import com.sap.conn.jco.JCoDestinationManager;import com.sap.conn.jco.JCoException;import com.sap.conn.jco.JCoField;import com.sap.conn.jco.JCoFunction;import com.sap.conn.jco.JCoStructure;import com.sap.conn.jco.JCoTable;import com.sap.conn.jco.ext.Environment; public class MyStepByStepClient { public final static String DESTINATION_NAME = "myDest"; public final static String client = "xxx"; public final static String user = "xxx"; public final static String password = "xxx"; public final static String language = "xx"; public final static String host = "xxx"; public final static String systemNumber = "xx"; public static void main(String[] args) throws Exception{ System.out.println("\n======= Connect ======="); MyStepByStepClient.connect(); System.out.println("\n======= Simple Call ======="); MyStepByStepClient.step3SimpleCall(); System.out.println("\n======= Structure ======="); MyStepByStepClient.step3WorkWithStructure(); System.out.println("\n======= Table ======="); MyStepByStepClient.step4WorkWithTable(); System.out.println("\n======= FINISHED ======="); } public static void connect() throws Exception{ MyDestinationData destData = new MyDestinationData(client, user, password, language, host, systemNumber); MyDestinationDataProvider provider = MyDestinationDataProvider.getInstance(); provider.addDestination(DESTINATION_NAME, destData); Environment.registerDestinationDataProvider(provider); JCoDestination dest = JCoDestinationManager.getDestination(DESTINATION_NAME); System.out.println(dest.getAttributes()); } /** * The following example executes a simple RFC function STFC_CONNECTION. * In contrast to JCo 2 you do not need to take care of repository management. * JCo 3 manages the repository caches internally and shares the available * function metadata as much as possible. * @throws JCoException */ public static void step3SimpleCall() throws JCoException { //JCoDestination is the logic address of an ABAP system and ... JCoDestination destination = JCoDestinationManager.getDestination(DESTINATION_NAME); // ... it always has a reference to a metadata repository JCoFunction function = destination.getRepository().getFunction("STFC_CONNECTION"); if(function == null) throw new RuntimeException("BAPI_COMPANYCODE_GETLIST not found in SAP."); //JCoFunction is container for function values. Each function contains separate //containers for import, export, changing and table parameters. //To set or get the parameters use the APIS setValue() and getXXX(). function.getImportParameterList().setValue("REQUTEXT", "Hello SAP"); try { //execute, i.e. send the function to the ABAP system addressed //by the specified destination, which then returns the function result. //All necessary conversions between Java and ABAP data types //are done automatically. function.execute(destination); } catch(AbapException e) { System.out.println(e.toString()); return; } System.out.println("STFC_CONNECTION finished:"); System.out.println(" Echo: " + function.getExportParameterList().getString("ECHOTEXT")); System.out.println(" Response: " + function.getExportParameterList().getString("RESPTEXT")); System.out.println(); } /** * ABAP APIs often uses complex parameters. This example demonstrates * how to read the values from a structure. * @throws JCoException */ public static void step3WorkWithStructure() throws JCoException { JCoDestination destination = JCoDestinationManager.getDestination(DESTINATION_NAME); JCoFunction function = destination.getRepository().getFunction("RFC_SYSTEM_INFO"); if(function == null) throw new RuntimeException("RFC_SYSTEM_INFO not found in SAP."); try { function.execute(destination); } catch(AbapException e) { System.out.println(e.toString()); return; } JCoStructure exportStructure = function.getExportParameterList().getStructure("RFCSI_EXPORT"); System.out.println("System info for " + destination.getAttributes().getSystemID() + ":\n"); //The structure contains some fields. The loop just prints out each field with its name. for(int i = 0; i < exportStructure.getMetaData().getFieldCount(); i++) { System.out.println(exportStructure.getMetaData().getName(i) + ":\t" + exportStructure.getString(i)); } System.out.println(); //JCo still supports the JCoFields, but direct access via getXXX is more efficient as field iterator System.out.println("The same using field iterator: \nSystem info for " + destination.getAttributes().getSystemID() + ":\n"); for(JCoField field : exportStructure) { System.out.println(field.getName() + ":\t" + field.getString()); } System.out.println(); } /** * A slightly more complex example than before. Query the companies list * returned in a table and then obtain more details for each company. * @throws JCoException */ public static void step4WorkWithTable() throws JCoException { JCoDestination destination = JCoDestinationManager.getDestination(DESTINATION_NAME); JCoFunction function = destination.getRepository().getFunction("BAPI_COMPANYCODE_GETLIST"); if(function == null) throw new RuntimeException("BAPI_COMPANYCODE_GETLIST not found in SAP."); try { function.execute(destination); } catch(AbapException e) { System.out.println(e.toString()); return; } JCoStructure returnStructure = function.getExportParameterList().getStructure("RETURN"); if (! (returnStructure.getString("TYPE").equals("")||returnStructure.getString("TYPE").equals("S")) ) { throw new RuntimeException(returnStructure.getString("MESSAGE")); } JCoTable codes = function.getTableParameterList().getTable("COMPANYCODE_LIST"); for (int i = 0; i < codes.getNumRows(); i++) { codes.setRow(i); System.out.println(codes.getString("COMP_CODE") + '\t' + codes.getString("COMP_NAME")); } //move the table cursor to first row codes.firstRow(); for (int i = 0; i < codes.getNumRows(); i++, codes.nextRow()) { function = destination.getRepository().getFunction("BAPI_COMPANYCODE_GETDETAIL"); if (function == null) throw new RuntimeException("BAPI_COMPANYCODE_GETDETAIL not found in SAP."); function.getImportParameterList().setValue("COMPANYCODEID", codes.getString("COMP_CODE")); //We do not need the addresses, so set the corresponding parameter to inactive. //Inactive parameters will be either not generated or at least converted. function.getExportParameterList().setActive("COMPANYCODE_ADDRESS",false); try { function.execute(destination); } catch (AbapException e) { System.out.println(e.toString()); return; } returnStructure = function.getExportParameterList().getStructure("RETURN"); if (! (returnStructure.getString("TYPE").equals("") || returnStructure.getString("TYPE").equals("S") || returnStructure.getString("TYPE").equals("W")) ) { throw new RuntimeException(returnStructure.getString("MESSAGE")); } JCoStructure detail = function.getExportParameterList().getStructure("COMPANYCODE_DETAIL"); System.out.println(detail.getString("COMP_CODE") + '\t' + detail.getString("COUNTRY") + '\t' + detail.getString("CITY")); }//for }}

In Zeile 35 erstellen wir ein Objekt MyDestinationData, das alle Verbindungsparameter hält. Je nachdem ob wir einen Application oder Message Server verbinden, gibt es 2 verschiedene Konstruktoren. Danach holen wir die Instanz des MyDestinationDataProviders und fügen das soeben erstellte MyDestinationDataObjekt hinzu. Wie schon beschrieben, wird dieses dann in der internen HashMap des Providers abgelegt.

Der Aufruf von „Environment.registerDestinationDataProvider(provider);“ bewirkt dann, dass unser MyDestinationDataProvider als der zu verwendende Provider gesetzt wird. Schlussendlich wird das Attribut provider der Klasse „DefaultDestinationManager“ auf unser „MyDestinationManager“ gesetzt.

Beim Aufruf von JCoDestinationManager.getDestination(DESTINATION_NAME);“ wird dann die Methode „getDestinationInstance“ vom „DefaultDestinationManager“ aufgerufen. Diese wiederum ruft „searchDestination“, dann „getProperties“ und dort werden die Properties von „MyDestinationDataProvider“ aufgerufen. Hier ein Bild vom Stack:

So nun fehlen noch die beiden Sources von MyDestinationDataProvider

import java.util.HashMap;import java.util.Properties; import com.sap.conn.jco.ext.DestinationDataEventListener;import com.sap.conn.jco.ext.DestinationDataProvider; public class MyDestinationDataProvider implements DestinationDataProvider{ private HashMap<String, Properties> destinationMap; private static MyDestinationDataProvider provider = new MyDestinationDataProvider(); /** * Privater Konstruktor */ private MyDestinationDataProvider(){ if(provider == null){ destinationMap = new HashMap<String, Properties>(); } } /** * Get Singleton * @return */ public static MyDestinationDataProvider getInstance(){ return provider; } @Override public Properties getDestinationProperties(String destName) { if (destinationMap.containsKey(destName)) { return destinationMap.get(destName); } else { return null; } } @Override public void setDestinationDataEventListener(DestinationDataEventListener arg0) { // TODO Auto-generated method stub } @Override public boolean supportsEvents() { // TODO Auto-generated method stub return false; } public void addDestination(String destinationName, MyDestinationData myDestinationData) { Properties properties = new Properties(); if (MyDestinationData.APPLICATION_SERVER.equals(myDestinationData.getType())) { properties.setProperty(DestinationDataProvider.JCO_USER, myDestinationData.getUser()); properties.setProperty(DestinationDataProvider.JCO_PASSWD, myDestinationData.getPassword()); properties.setProperty(DestinationDataProvider.JCO_LANG, myDestinationData.getLanguage()); properties.setProperty(DestinationDataProvider.JCO_ASHOST, myDestinationData.getHost()); properties.setProperty(DestinationDataProvider.JCO_CLIENT, myDestinationData.getClient()); properties.setProperty(DestinationDataProvider.JCO_SYSNR, myDestinationData.getSystemNumber()); } else if (MyDestinationData.MESSAGE_SERVER.equals(myDestinationData.getType())) { properties.setProperty(DestinationDataProvider.JCO_USER, myDestinationData.getUser()); properties.setProperty(DestinationDataProvider.JCO_PASSWD, myDestinationData.getPassword()); properties.setProperty(DestinationDataProvider.JCO_LANG, myDestinationData.getLanguage()); properties.setProperty(DestinationDataProvider.JCO_MSHOST, myDestinationData.getHost()); properties.setProperty(DestinationDataProvider.JCO_CLIENT, myDestinationData.getClient()); properties.setProperty(DestinationDataProvider.JCO_R3NAME, myDestinationData.getSystemID()); properties.setProperty(DestinationDataProvider.JCO_GROUP, myDestinationData.getGroupName()); } destinationMap.put(destinationName, properties); }}

und MyDestinationData

public class MyDestinationData { public static final String APPLICATION_SERVER = "APPLICATION_SERVER"; public static final String MESSAGE_SERVER = "MESSAGE_SERVER"; private String type; private String client; private String user; private String password; private String language; private String host; private String systemNumber; private String systemID; private String groupName; public String getType() { return type; } public String getClient() { return client; } public String getUser() { return user; } public String getPassword() { return password; } public String getLanguage() { return language; } public String getHost() { return host; } public String getSystemNumber() { return systemNumber; } public String getSystemID() { return systemID; } public String getGroupName() { return groupName; } public MyDestinationData(String client, String user, String password, String language, String host, String systemNumber) { this.client = client; this.user = user; this.password = password; this.language = language; this.host = host; this.systemNumber = systemNumber; this.type = APPLICATION_SERVER; } public MyDestinationData(String client, String user, String password, String language, String host, String systemID, String groupName) { this.client = client; this.user = user; this.password = password; this.language = language; this.host = host; this.systemID = systemID; this.groupName = groupName; this.type = MESSAGE_SERVER; }}

Somit habt Ihr alles um das lästige File loszuwerden!

Viel Spass… Manfred Fettinger