Подання даних SAP R/3 в Oracle Database з допомогою SAP Java Connector

черговий раз виникла необхідність пов'язати дві відомі системи між собою, тепер це будуть Oracle Database і SAP. Можливо, існують платні методи зв'язування, але в даному випадку мова йде про необхідність скористатися невеликими порціями даних.

Розповім про те, як можна відобразити дані, взяті з SAP, з допомогою оператора select. Приклад дуже простий, для демонстрації принципової можливості. Створений він на основі доданих до SAP JCo або загальнодоступних вихідних текстів.

Відразу зауважу, що SAP JCo видають тільки тим, хто платить. А Oracle Database не вітає викликів java з використанням бінарних бібліотек, тому, за замовчуванням така можливість повинна бути спеціально дозволена.

Необхідні типи для таблиці та її рядків в Oracle Database, sap_table.tps:

drop type sap_rows;
/
drop type sap_row;
/
create or replace type sap_row as object(p1 varchar(30), p2 varchar(30), p3 varchar(30), p4 varchar(30), p5 varchar(30), dt varchar(20))
/
create or replace type sap_rows as table of sap_row
/

Основний компонент, в якому відбувається процес отримання даних з SAP і підготовки до використання в select, art0int_sap.java:

package com.art0int;

import java.util.*;
import java.io.*;
import java.math.*;
import java.util.Calendar;
import java.text.SimpleDateFormat;
import java.sql.*;

import oracle.sql.*;
import oracle.jdbc.driver.OracleDriver;

import java.util.HashMap;
import java.util.Properties;

import com.sap.conn.jco.AbapException;
import com.sap.conn.jco.JCoContext;
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.JCoFunctionTemplate;
import com.sap.conn.jco.JCoStructure;
import com.sap.conn.jco.JCoTable;
import com.sap.conn.jco.ext.DataProviderException;
import com.sap.conn.jco.ext.DestinationDataEventListener;
import com.sap.conn.jco.ext.DestinationDataProvider;

public class SAP_TABLE
{

//масив для наповнення даними
static Vector vrows;
//тут ми будемо зберігати структуру, що забезпечує доступ до даних SAP
static MyDestinationDataProvider myProvider = null;

//власне, цей метод і надсилає отримані з SAP дані для використання оператором select
public static oracle.sql.ARRAY SQL_sap_rows (BigDecimal nrows) throws SQLException {

Connection conn = new OracleDriver().defaultConnection();

//користуємося створеними типами для таблиці та її рядків
ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor("SAP_ROWS", conn );
StructDescriptor outDesc = StructDescriptor.createDescriptor("SAP_ROW", conn);

int nrowsval = nrows.intValue();
vrows = new Vector(nrowsval);
Object[] out_attr = new Object[6];

try {

if (myProvider == null) {
myProvider = new MyDestinationDataProvider();
try {
com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
} catch(IllegalStateException providerAlreadyRegisteredException) {
throw new Error(providerAlreadyRegisteredException);
}
}

String destName = "ABAP_AS";
SAP_TABLE test = new SAP_TABLE();

//готуємо з'єднання з SAP
myProvider.changeProperties(destName, getDestinationPropertiesFromUI());

JCoDestination dest;
try {
//встановлюємо параметри з'єднання
dest = JCoDestinationManager.getDestination(destName);
//перевіряємо доступність системи
dest.ping();
System.out.println("Destination " + destName + " works");

JCoFunction function = dest.getRepository().getFunction("MY_SAPFUNCTION");
try {
//отримуємо дані
function.execute(dest);
} catch(AbapException e) {
System.out.println(e.toString());
return null;
}

//заповнюємо масив
JCoTable out = function.getTableParameterList().getTable(0);
for (int i = 0; i < out.getNumRows(); i++) {
out.setRow(i);

out_attr[1-1] = out.getString(1-1);
out_attr[2-1] = out.getString(2-1);
out_attr[3-1] = out.getString(3-1);
out_attr[4-1] = out.getString(4-1);
out_attr[5-1] = out.getString(5-1);
out_attr[6-1] = (Object)new String(now());
vrows.add((Object)new STRUCT(outDesc, conn, out_attr));

nrowsval--;
if (nrowsval==0) break;
}

} catch(JCoException e) {
e.printStackTrace();
System.out.println("Execution on destination " + destName+ " failed");
}


} catch(Exception e) {
e.printStackTrace();
}

//віддаємо масив для обробки в таблиці Oracle 
oracle.sql.ARRAY outArray = new oracle.sql.ARRAY(descriptor,conn,vrows.toArray());

return outArray;

}

//цей метод подає параметри підключення до SAP
static Properties getDestinationPropertiesFromUI() {

Properties connectProperties = new Properties();
connectProperties.setProperty(DestinationDataProvider.JCO_R3NAME, "MY_R3NAME");
connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST, "MY_IP");
connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, "MY_CLIENTNO");
connectProperties.setProperty(DestinationDataProvider.JCO_USER, "MY_SAPUSER");
connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, "MY_SAPPASSWORD");
connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR, "MY_SYSNR");
connectProperties.setProperty(DestinationDataProvider.JCO_LANG, "en");
return connectProperties;
}

//клас використовується як постачальник даних SAP
static 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;
}

void changeProperties(String destName, Properties properties)
{
synchronized(secureDBStorage)
{
if(properties==null)
{
if(secureDBStorage.remove(destName)!=null)
eL.deleted(destName);
}
else
{
secureDBStorage.put(destName, властивості);
eL.updated(destName);
}
}
}
}

//допоміжні методи, вони просто можуть згодитися
public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss";

public static String now() {
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
return sdf.format(cal.getTime());

}

}

Обгортка для завантаження исходника java в Oracle Database, sap_table.js:

create or replace and compile java source named sap_table as
@art0int_sap.java
/

Обгортки для дзвінків java з боку Oracle Database, SAP_TABLE.spc:

create or replace package is SAP_TABLE

function get_java_property(prop in varchar2)
return varchar2 is
language java name 'java.lang.System.getProperty(java.lang.String) return java.lang.String';

function sap_rows_table(nrows in number)
return sap_rows
IS LANGUAGE JAVA
name 'com.art0int.SAP_TABLE.SQL_sap_rows(java.math.BigDecimal) return oracle.sql.ARRAY';

end SAP_TABLE;
/

І остаточний скрипт, який все збере воєдино. Зверніть увагу на видачу прав для виклику бібліотеки sapjco3.

export ORACLE_SID=MYDB

sqlplus '/ as sysdba' <<EOF

drop user sapacc cascade;
/

create user sapacc identified by "psapacc" default tablespace users temporary tablespace temp profile monitor;
grant connect to resource sapacc;

exec dbms_java.grant_permission('SAPACC', 'SYS:java.net.SocketPermission', 'MY_IP:MY_PORT', 'connect,resolve' );
exec dbms_java.grant_permission('SAPACC', 'SYS:java.io.FilePermission', '.', 'read' );

--тут зверніть увагу: залежить від версії Oracle Database, потрібно отримати номер включеного заборони на виклики бібліотек і відключити його тимчасово, видати дозвіл на запуск, потім знову включити
--select seq, kind, grantee, name, enabled from dba_java_policy where name like '%java.lang.RuntimePermission%';
--98 RESTRICT PUBLIC 0:java.lang.RuntimePermission#дзвінки на loadlibrary.* ENABLED
exec dbms_java.disable_permission(98);
exec dbms_java.grant_permission('SAPACC', 'SYS:java.lang.RuntimePermission', 'дзвінки на loadlibrary.sapjco3', " );
exec dbms_java.enable_permission(98);

EOF

#завантажуємо класи в створену схему
dropjava -user sapacc/psapacc -verbose sapjco3.jar
loadjava -user sapacc/psapacc -order -resolve -verbose -resolver "((* sapacc) (* PUBLIC) (* -))" sapjco3.jar

sqlplus -S /nolog <<EOF
connect sapacc/psapacc
set pagesize 1000
set linesize 160

@sap_table.tps
show errors
@sap_table.js
show errors
@SAP_TABLE.spc
show errors
EOF

export NLS_LANG=American_America.UTF8
sqlplus -S /nolog<<EOF
connect sapacc/psapacc
set on serveroutput
call dbms_java.set_output(1000000);

set linesize 160
set pagesize 1000
set off feedback

col p1 for a5
col p2 for a3
col p3 for a3
col p4 for a5
col p5 for a25
col dt for a25

select
sapacc.SAP_TABLE.get_java_property('java.library.path') as java_library_path
,sapacc.SAP_TABLE.get_java_property('java.version') as java_version
from dual;

select * from table(sapacc.SAP_TABLE.sap_rows_table(15)) order by dt;

exit;
EOF

Результат Ви побачите, якщо покладете бібліотеки SAP Java Connector відповідний java.library.path каталог, який з'являється при першому виклику скрипта.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.