2

I am new to SAP JCo I have requirement to call multiple SAP System using SAP Jco. But I am unable to connect multiple sap system at the same time......

Here is code :

package com.sap.test;


import java.util.Properties;

import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.JCoException;
import com.sap.conn.jco.JCoRepository;
import com.sap.conn.jco.ext.DestinationDataProvider;
import com.sap.conn.jco.ext.Environment;
import com.sap.utils.MyDestinationDataProvider;
import com.sap.utils.SapSystem;


public class TestMultipleSAPConnection {

    public static Properties properties;
    public static JCoDestination dest = null;
    public static JCoRepository repos = null;
    public static SapSystem system = null;
    String SAP_SERVER = "SAP_SERVER";
    MyDestinationDataProvider myProvider = null;



    public static void main(String[] args) throws JCoException {            
        getConnection_CRM();
        getConnection_R3();     
    }


public static JCoDestination getConnection_R3() {

        boolean connR3_flag = true;
        JCoDestination dest = null;
        JCoRepository repos = null;

        String SAP_SERVER = "SAP_SERVER";
        Properties properties = new Properties();
        SapSystem system = new SapSystem();

        system.setClient("100");
        system.setHost("r3devsvr.myweb.com");
        system.setLanguage("en");
        system.setSystemNumber("00");
        system.setUser("SAP-R3-USER");
        system.setPassword("init1234");

        properties.setProperty("jco.client.ashost", system.getHost());
        properties.setProperty("jco.client.sysnr", system.getSystemNumber());
        properties.setProperty("jco.client.client", system.getClient());
        properties.setProperty("jco.client.user", system.getUser());
        properties.setProperty("jco.client.passwd", system.getPassword());
        properties.setProperty("jco.client.lang", system.getLanguage());
        System.out.println("******* Connection Parameter Set *******");
        MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
        System.out.println("******* Destination Provider Set *******");
        myProvider.changePropertiesForABAP_AS(properties);
        if (!Environment.isDestinationDataProviderRegistered()) {
            System.out.println("Registering Destination Provider R3");
            Environment.registerDestinationDataProvider((DestinationDataProvider) myProvider);
        }else{
            System.out.println("Destination Provider already set..R3");
            connR3_flag = false;
        }
        try {
            dest = JCoDestinationManager.getDestination((String) SAP_SERVER);
            repos = dest.getRepository();
            if (repos == null) {
                System.out.println("Repos is null.....");
            } else {
                System.out.println("Repos is not null.....");
            }
            System.out.println("After getting repos...");
            if(connR3_flag){
               System.out.println("R3 Connection Successfull...");
            }
        } catch (Exception ex) {
            System.out.println(ex);
        }
        return dest;
    }

    public static JCoDestination getConnection_CRM() {
        boolean connCRM_flag = true;
        JCoDestination dest = null;
        JCoRepository repos = null;

        String SAP_SERVER = "SAP_SERVER";
        Properties properties = new Properties();
        SapSystem system = new SapSystem();

        system.setClient("200");
        system.setHost("crmdevsvr.myweb.com");
        system.setLanguage("en");
        system.setSystemNumber("00");
        system.setUser("SAP-CRM-USER");
        system.setPassword("init1234");

        properties.setProperty("jco.client.ashost", system.getHost());
        properties.setProperty("jco.client.sysnr", system.getSystemNumber());
        properties.setProperty("jco.client.client", system.getClient());
        properties.setProperty("jco.client.user", system.getUser());
        properties.setProperty("jco.client.passwd", system.getPassword());
        properties.setProperty("jco.client.lang", system.getLanguage());
        System.out.println("******* Connection Parameter Set *******");
        MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
        System.out.println("******* Destination Provider Set *******");
        myProvider.changePropertiesForABAP_AS(properties);
        if (!Environment.isDestinationDataProviderRegistered()) {
            System.out.println("Registering Destination Provider CRM");
            Environment.registerDestinationDataProvider((DestinationDataProvider) myProvider);
        }else{
            System.out.println("Destination Provider already set..CRM");
            connCRM_flag = false;
        }
        try {
            dest = JCoDestinationManager.getDestination((String) SAP_SERVER);
            repos = dest.getRepository();
            if (repos == null) {
                System.out.println("Repos is null.....");
            } else {
                System.out.println("Repos is not null.....");
            }
            System.out.println("After getting repos...");
            if(connCRM_flag){
               System.out.println("CRM Connection Successfull...");
            }
        } catch (Exception ex) {
            System.out.println(ex);
        }

        return dest;

    }

}
Sandra Rossi
  • 11,934
  • 5
  • 22
  • 48
Developer Desk
  • 2,294
  • 8
  • 36
  • 77
  • Is there a file named _dev_jco_rfc.trc_ in the directory of your java program? This is usually a good starting point to check if your connection parameters and credentials are ok – lichtbringer Sep 26 '16 at 08:46

2 Answers2

5

The JCo JavaDoc documentation says:

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.

So you have to register ONE instance of the DestinationDataProvider, this is an instance of your class MyDestinationDataProvider. This implementation needs to manage and store ALL the different logon configurations for all your SAP systems, accessible via a distinct destination name string. A simple HashMap<String, Properties> would be a sufficient storage form for this. Add the two Properties instances with distinct destination name strings to the HashMap and return the Properties instance associated to the passed destinationName from method MyDestinationDataProvider.getDestinationProperties(String destinationName). So you can access the desired JCoDestination instance targeting any SAP system via its specific destination name. In your example you used "SAP_SERVER" for both destination configurations which won't work. For example, use the SAP System ID (SID) as the destination name (key) instead.

If using the names from your example, then JCoDestinationManager.getDestination("CRM") would return the JCoDestination instance for system "CRM" and JCoDestinationManager.getDestination("R3") the JCoDestination for system "R3". And both can be used independently and simultaneously. That's it.

Trixx
  • 1,796
  • 1
  • 15
  • 18
-1

Sharing with you a solution I recently came up with for this exact problem. I discovered two different ways to implement CustomDestinationDataProvider so that I could use multiple destinations.

Something that I did that helped out with both of my different solutions was change out the method in CustomDestinationDataProvider that instantiates the MyDestinationDataProvider inner class so that instead of returning ArrayList, it returns JCoDestination. I changed the name of this method from executeSAPCall to getDestination.

The first way that I discovered that allowed me to use multiple destinations, successfully changing out destinations, was to introduce a class variable for MyDestinationDataProvider so that I could keep my instantiated version. Please note that for this solution, the CustomDestinationDataProvider class is still embedded within my java application code.

I found that this solution only worked for one application. I was not able to use this mechanism in multiple applications on the same tomcat server, but at least I was finally able to successfully switch destinations. Here is the code for CustomDestinationDataProvider.java for this first solution:

public class CustomDestinationDataProvider {

private MyDestinationDataProvider gProvider;    // class version of MyDestinationDataProvider

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) {
        System.out.println("getDestinationProperties: Exception detected!!! message = " + re.getMessage());
        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 JCoDestination getDestination(String destName, Properties connectProperties) {
MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
boolean destinationDataProviderRegistered = com.sap.conn.jco.ext.Environment.isDestinationDataProviderRegistered();
if (!destinationDataProviderRegistered) {
    try {
        com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
        gProvider = myProvider; // save our destination data provider in the class var
    } catch(IllegalStateException providerAlreadyRegisteredException) {
        throw new Error(providerAlreadyRegisteredException);
    }
} else {
    myProvider = gProvider; // get the destination data provider from the class var.
}
myProvider.changeProperties(destName, connectProperties);
JCoDestination dest = null;
try {
    dest = JCoDestinationManager.getDestination(destName);
} catch(JCoException e) {
    e.printStackTrace();
} catch (Exception e) {
    // TODO Auto-generated catch block
}
return dest;

} } This is the code in my servlet class that I use to instantiate and call CustomDestinationDataProvider within my application code:

CustomDestinationDataProvider cddp = new CustomDestinationDataProvider();
SAPDAO sapDAO = new SAPDAO();

Properties p1 = getProperties("SAPSystem01");
Properties p2 = getProperties("SAPSystem02");
try {
    JCoDestination dest = cddp.getDestination("SAP_R3_USERID_01", p1);  // establish the first destination
    sapDAO.searchEmployees(dest, searchCriteria);   // call the first BAPI
    dest = cddp.getDestination("SAP_R3_USERID_02", p2); // establish the second destination
    sapDAO.searchAvailability(dest);    // call the second BAPI
} catch (Exception e) {
    e.printStackTrace();
}

Again, this solution only works within one application. If you implement this code directly into more than one application, the first app that calls this code gets the resource and the other one will error out.

The second solution that I came up with allows multiple java applications to use the CustomDestinationDataProvider class at the same time. I broke the CustomDestinationDataProvider class out of my application code and created a separate java spring application for it (not a web application) for the purpose of creating a jar. I then transformed the MyDestinationDataProvider inner class into a singleton. Here's the code for the singleton version of CustomDestinationDataProvider:

public class CustomDestinationDataProvider {
public static class MyDestinationDataProvider implements DestinationDataProvider {

////////////////////////////////////////////////////////////////////
// The following lines convert MyDestinationDataProvider into a singleton. Notice 
// that the MyDestinationDataProvider class has now been declared as static.
private static MyDestinationDataProvider myDestinationDataProvider = null;
private MyDestinationDataProvider() {
}
public static MyDestinationDataProvider getInstance() {
    if (myDestinationDataProvider == null) {
        myDestinationDataProvider = new MyDestinationDataProvider();
    }
    return myDestinationDataProvider;
}
////////////////////////////////////////////////////////////////////

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 JCoDestination getDestination(String destName, Properties connectProperties) throws Exception {
MyDestinationDataProvider myProvider = MyDestinationDataProvider.getInstance();
boolean destinationDataProviderRegistered = com.sap.conn.jco.ext.Environment.isDestinationDataProviderRegistered();
if (!destinationDataProviderRegistered) {
    try {
        com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
    } catch(IllegalStateException providerAlreadyRegisteredException) {
        throw new Error(providerAlreadyRegisteredException);
    }
}
myProvider.changeProperties(destName, connectProperties);
JCoDestination dest = null;
try {
    dest = JCoDestinationManager.getDestination(destName);
} catch(JCoException ex) {
    ex.printStackTrace();
    throw ex;
} catch (Exception ex) {
    ex.printStackTrace();
    throw ex;
}
return dest;
}
}

After putting this code into the jar file application and creating the jar file (I call it JCOConnector.jar), I put the jar file on the shared library classpath of my tomcat server and restarted the tomcat server. In my case, this was /opt/tomcat/shared/lib. Check your /opt/tomcat/conf/catalina.properties file for the shared.loader line for the location of your shared library classpath. Mine looks like this:

shared.loader=\
${catalina.home}/shared/lib\*.jar,${catalina.home}/shared/lib

I also put a copy of this jar file in the "C:\Users\userid\Documents\jars" folder on my workstation so that the test application code could see the code in the jar and compile. I then referenced this copy of the jar file in my pom.xml file in both of my test applications:

<dependency>
    <groupId>com.mycompany</groupId>
    <artifactId>jcoconnector</artifactId>
    <version>1.0</version>
    <scope>system</scope>
    <systemPath>C:\Users\userid\Documents\jars\JCOConnector.jar</systemPath>
</dependency>

After adding this to the pom.xml file, I right clicked on each project, selected Maven -> Update Project..., and I then right clicked again on each project and selected 'Refresh'. Something very important that I learned was to not add a copy of JCOConnector.jar directly to either of my test projects. The reason for this is because I want the code from the jar file in /opt/tomcat/shared/lib/JCOConnector.jar to be used. I then built and deployed each of my test apps to the tomcat server.

The code that calls my JCOConnector.jar shared library in my first test application looks like this:

CustomDestinationDataProvider cddp = new CustomDestinationDataProvider();
JCoDestination dest = null;
SAPDAO sapDAO = new SAPDAO();

Properties p1 = getProperties("SAPSystem01");
try {
    dest = cddp.getDestination("SAP_R3_USERID_01", p1);
    sapDAO.searchEmployees(dest);
} catch (Exception ex) {
    ex.printStackTrace();
}

The code in my second test application that calls my JCOConnector.jar shared library looks like this:

CustomDestinationDataProvider cddp = new CustomDestinationDataProvider();
JCoDestination dest = null;
SAPDAO sapDAO = new SAPDAO();

Properties p2 = getProperties("SAPSystem02");
try {
    dest = cddp.getDestination("SAP_R3_USERID_02", p2);
    sapDAO.searchAvailability(dest);
} catch (Exception ex) {
    ex.printStackTrace();
}

I know that I've left out a lot of the steps involved in first getting the SAP JCO 3 library installed on your workstation and server. I do hope that this helps out at least one other person of getting over the hill of trying to get multiple spring mvc java spplications talking to SAP on the same server.

Stephen
  • 1,977
  • 2
  • 15
  • 19