Как войти в несколько систем SAP с помощью SAP jco

Я новичок в SAP JCo. У меня есть потребность вызывать несколько систем SAP с помощью SAP Jco. Но я не могу одновременно подключить несколько систем sap ......

Вот код:

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;

    }

}

person Developer Desk    schedule 21.09.2016    source источник
comment
Есть ли файл с именем dev_jco_rfc.trc в каталоге вашей Java-программы? Обычно это хорошая отправная точка для проверки параметров подключения и учетных данных.   -  person lichtbringer    schedule 26.09.2016


Ответы (2)


В документации JCo JavaDoc говорится:

Можно зарегистрировать только одну реализацию DestinationDataProvider. Для регистрации другой реализации инфраструктура должна сначала отменить регистрацию реализации, зарегистрированной в данный момент. Не рекомендуется постоянно обмениваться регистрациями DestinationDataProvider. Один зарегистрированный экземпляр должен глобально управлять всеми конфигурациями назначения для всей среды инфраструктуры.

Итак, вам нужно зарегистрировать ОДИН экземпляр DestinationDataProvider, это экземпляр вашего класса MyDestinationDataProvider. Эта реализация должна управлять и хранить ВСЕ различные конфигурации входа в систему для всех ваших систем SAP, доступные через отдельную строку имени назначения. Простой HashMap<String, Properties> будет достаточной формой хранения для этого. Добавьте два экземпляра Properties с разными строками имени места назначения в HashMap и верните экземпляр Properties, связанный с переданным именем назначения, из метода MyDestinationDataProvider.getDestinationProperties(String destinationName). Таким образом, вы можете получить доступ к нужному экземпляру JCoDestination, предназначенному для любой системы SAP, через его конкретное имя назначения. В вашем примере вы использовали «SAP_SERVER» для обеих конфигураций назначения, которые не будут работать. Например, вместо этого используйте идентификатор системы SAP (SID) в качестве целевого имени (ключа).

Если использовать имена из вашего примера, то JCoDestinationManager.getDestination("CRM") вернет экземпляр JCoDestination для системы "CRM" и JCoDestinationManager.getDestination("R3") экземпляр JCoDestination для системы "R3". И то, и другое можно использовать независимо и одновременно. Вот и все.

person Trixx    schedule 17.01.2017

Делюсь с вами решением, которое я недавно придумал для этой точной проблемы. Я обнаружил два разных способа реализации CustomDestinationDataProvider, чтобы можно было использовать несколько мест назначения.

Что-то, что я сделал, что помогло с обоими моими разными решениями, было изменение метода в CustomDestinationDataProvider, который создает экземпляр внутреннего класса MyDestinationDataProvider, так что вместо возврата ArrayList он возвращает JCoDestination. Я изменил имя этого метода с executeSAPCall на getDestination.

Первый способ, который я обнаружил, который позволил мне использовать несколько назначений, успешно меняя места назначения, заключался в том, чтобы ввести переменную класса для MyDestinationDataProvider, чтобы я мог сохранить свою экземплярную версию. Обратите внимание, что для этого решения класс CustomDestinationDataProvider по-прежнему встроен в код моего Java-приложения.

Я обнаружил, что это решение работает только для одного приложения. Мне не удалось использовать этот механизм в нескольких приложениях на одном сервере tomcat, но, по крайней мере, я наконец-то смог успешно переключаться между пунктами назначения. Вот код CustomDestinationDataProvider.java для этого первого решения:

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;

} } Это код в моем классе сервлета, который я использую для создания экземпляра и вызова CustomDestinationDataProvider в коде моего приложения:

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();
}

Опять же, это решение работает только в одном приложении. Если вы внедрите этот код непосредственно в более чем одно приложение, первое приложение, которое вызовет этот код, получит ресурс, а другое выдаст ошибку.

Второе решение, которое я придумал, позволяет нескольким Java-приложениям одновременно использовать класс CustomDestinationDataProvider. Я вырвал класс CustomDestinationDataProvider из кода своего приложения и создал для него отдельное приложение java spring (не веб-приложение) с целью создания jar. Затем я преобразовал внутренний класс MyDestinationDataProvider в синглтон. Вот код одноэлементной версии 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;
}
}

Поместив этот код в приложение с файлом jar и создав файл jar (я называю его JCOConnector.jar), я поместил файл jar в путь к классам общей библиотеки моего сервера tomcat и перезапустил сервер tomcat. В моем случае это был /opt/tomcat/shared/lib. Проверьте файл /opt/tomcat/conf/catalina.properties на наличие строки shared.loader для определения пути к классам вашей общей библиотеки. Мой выглядит так:

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

Я также поместил копию этого jar-файла в папку «C:\Users\userid\Documents\jars» на своей рабочей станции, чтобы код тестового приложения мог увидеть код в jar-файле и скомпилировать его. Затем я сослался на эту копию файла jar в моем файле pom.xml в обоих моих тестовых приложениях:

<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>

После добавления этого в файл pom.xml я щелкнул правой кнопкой мыши по каждому проекту, выбрал Maven -> Обновить проект..., а затем снова щелкнул правой кнопкой мыши по каждому проекту и выбрал «Обновить». Я усвоил одну очень важную вещь: не добавлять копию JCOConnector.jar напрямую ни в один из моих тестовых проектов. Причина этого в том, что я хочу использовать код из файла jar в /opt/tomcat/shared/lib/JCOConnector.jar. Затем я создал и развернул каждое из своих тестовых приложений на сервере tomcat.

Код, который вызывает мою разделяемую библиотеку JCOConnector.jar в моем первом тестовом приложении, выглядит так:

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();
}

Код в моем втором тестовом приложении, которое вызывает мою разделяемую библиотеку JCOConnector.jar, выглядит следующим образом:

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();
}

Я знаю, что пропустил многие шаги, связанные с первой установкой библиотеки SAP JCO 3 на вашу рабочую станцию ​​и сервер. Я очень надеюсь, что это поможет хотя бы одному другому человеку преодолеть холм, пытаясь заставить несколько Spring mvc java spplications общаться с SAP на одном сервере.

person Stephen    schedule 27.10.2017