[백업][가리사니] sap rfc jco (java sapjco3.jar)를 이용한 접근방법
java

이 문서는 가리사니 개발자 포럼에 올렸던 글의 백업 파일입니다. 오래된 문서가 많아 현재 상황과 맞지 않을 수 있습니다.

SAP JCO 시리즈

준비

우선 SAP 연계정보 제공사를 통해 sapjco3.jar 파일과 리눅스 .so 윈도우 .dll 을 다운받습니다. (연계정보 제공사를 통하지 않아도 아래에서 받을 수 있습니다.) [http://maven.mit.edu/nexus/content/repositories/public/com/sap/conn/jco/sapjco3/3.0.14/ 라이브러리 파일 (.so, .dll)을 시스템 폴더에 넣습니다.

연동 예제

public static void main(String[] args) throws Exception {
	// SAP 연결
	SapManager sap = SapManager.newSapManagerBinder()
		.set(DestinationDataProvider.JCO_ASHOST, "호스트") // AS 호스트 : 경우에 따라 MS 호스트[JCO_MSHOST] 등도 사용함.
		.set(DestinationDataProvider.JCO_MSSERV, "9999") // MS 포트 [AS, MS는 MS, GW는 JCO_GWSERV]
		.set(DestinationDataProvider.JCO_SYSNR, "01") // 시스템 번호
		.set(DestinationDataProvider.JCO_GROUP, "Group Name") // 그룹
		.set(DestinationDataProvider.JCO_LANG, "KO") // 언어
		.set(DestinationDataProvider.JCO_CLIENT, "100") // 클라이언트 번호
		.set(DestinationDataProvider.JCO_USER, "user") // 계정
		.set(DestinationDataProvider.JCO_PASSWD, "password") // 암호
		.bind();

	// 접속 후 RFC 연결
	SapRfcFunction rfc = sap.getRfcFunction("RFC 함수 이름");

	// IMPORT
	rfc.imports().setValue("AAA", "A"); // AAA
	rfc.imports().setValue("BBB", "B"); // BBB
	rfc.imports().setValue("CCC", "C"); // CCC

	// 실행
	rfc.execute();

	// EXPORT
	System.out.println(rfc.exports().getValue("DDD"));
	System.out.println(rfc.exports().getValue("EEE"));
	System.out.println(rfc.exports().getValue("FFF"));

	// 테이블 조회
	List<ZSPADEPT_NEW> table1 = rfc.getTable("TABLE_NAME", 클래스매핑.class);
	table1.stream().limit(10).forEach(System.out::println); // 10개만 조회
}

연동 예제 (인자로 테이블 보내기)

사실상 테이블을 넣는걸 제외하곤 위 연동 예제 와 동일합니다. 먼저 jcoTable.appendRow(); 를 써서 row를 할당한 후 jcoTable.setValue("컬럼이름", "값"); 으로 하시면됩니다. 다만 타입을 제공자가 준 것과 동일하게 맞춰야합니다.

public static void main(String[] args) throws Exception {
	// SAP 연결
	SapManager sap = SapManager.newSapManagerBinder()
		.set(DestinationDataProvider.JCO_ASHOST, "호스트") // AS 호스트 : 경우에 따라 MS 호스트[JCO_MSHOST] 등도 사용함.
		.set(DestinationDataProvider.JCO_MSSERV, "9999") // MS 포트 [AS, MS는 MS, GW는 JCO_GWSERV]
		.set(DestinationDataProvider.JCO_SYSNR, "01") // 시스템 번호
		.set(DestinationDataProvider.JCO_GROUP, "Group Name") // 그룹
		.set(DestinationDataProvider.JCO_LANG, "KO") // 언어
		.set(DestinationDataProvider.JCO_CLIENT, "100") // 클라이언트 번호
		.set(DestinationDataProvider.JCO_USER, "user") // 계정
		.set(DestinationDataProvider.JCO_PASSWD, "password") // 암호
		.bind();

	// 접속 후 RFC 연결
	SapRfcFunction rfc = sap.getRfcFunction("RFC 함수 이름");

	// IMPORT
	rfc.imports().setValue("AAA", "A"); // AAA
	rfc.imports().setValue("BBB", "B"); // BBB
	rfc.imports().setValue("CCC", "C"); // CCC

	JCoTable jcoTable = rfc.importTable("TABLE_NAME");

	if (jcoTable == null) {
		// SAP 내에서 테이블 구현이 안된경우.
		// SAP 제공자에게 테이블 이름과 함께 문의
	}

	// 0행 삽입 예제
	jcoTable.setValue("컬럼이름1", "0번 행의 컬럼 값1 : 자료형을 제공자가 준 것과 같이 맞춰야함!!");
	jcoTable.setValue("컬럼이름2", 3123);

	// 1행 삽입 예제
	jcoTable.appendRow();
	jcoTable.setValue("컬럼이름1", "1번 행의 컬럼 값1");
	jcoTable.setValue("컬럼이름2", 22);

	// 실행
	rfc.execute();

	// EXPORT
	System.out.println(rfc.exports().getValue("DDD"));
	System.out.println(rfc.exports().getValue("EEE"));
	System.out.println(rfc.exports().getValue("FFF"));

	// 테이블 조회
	List<ZSPADEPT_NEW> table1 = rfc.getTable("TABLE_NAME", 클래스매핑.class);
	table1.stream().limit(10).forEach(System.out::println); // 10개만 조회
}

라이브러리 파일

아래소스는 예전 버전

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
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.JCoFieldIterator;
import com.sap.conn.jco.JCoFunction;
import com.sap.conn.jco.JCoParameterList;
import com.sap.conn.jco.JCoTable;

/**
 * SAP RFC 유틸
 * @author		박용서
 * @since		2018. 2. 20.
 */
public class SapManager {

	final static String CONN_FILE_NAME = "SAP";

	// newSapManagerBinder 통해 부르세요.
	SapManager() {
	}

	/** 연결을 시도합니다.
	 * @throws JCoException */
	public SapRfcFunction getRfcFunction(String rfcFunctionName) throws JCoException {
		return new SapRfcFunction(CONN_FILE_NAME, rfcFunctionName);
	}

	/**
	 * 함수를 선언합니다.
	 * @author		박용서
	 * @since		2018. 2. 20.
	 */
	public static class SapRfcFunction {

		final SimpleDateFormat ymdhms = new SimpleDateFormat("yyyyMMddHHmmss");
		final JCoFunction function;
		final JCoDestination destination;
		final String functionName;
		JCoParameterList tables;
		JCoParameterList imports;
		JCoParameterList exports;

		long startTime = 0L;

		// 생성
		private SapRfcFunction(String connectionFileName, String rfcFunctionName) throws JCoException {
			this.functionName = rfcFunctionName;
			destination = JCoDestinationManager.getDestination(connectionFileName);
			function = destination.getRepository().getFunction(rfcFunctionName);
		}

		/**
		 * import 값을 입력합니다.<br>
		 * execute() 이전에 불려야 합니다.
		 * @return JCoParameterList
		 */
		public JCoParameterList imports() {
			if (imports == null) {
				imports = function.getImportParameterList();
			}
			return imports;
		}

		/**
		 * import 할 테이블을 가져옵니다.
		 * @param tableName
		 * @return
		 */
		public JCoTable importTable(String tableName) {
			return function.getTableParameterList().getTable(tableName);
		}

		/**
		 * export 값을 출력합니다.<br>
		 * execute() 이후 불려야합니다.
		 * @return JCoParameterList
		 */
		public JCoParameterList exports() {
			if (exports == null) {
				exports = function.getExportParameterList();
			}
			return exports;
		}

		/**
		 * 실행
		 * @throws JCoException
		 */
		public void execute() throws JCoException {
			/*
			startTime = System.currentTimeMillis();
			final StringBuilder logs = new StringBuilder(1024).append("SAP RFC FUNCTION : ").append(functionName);
			// IMPORT
			logs.append("\n# IMPORT\n - ");
			imports().forEach(e -> logs.append(e.getName()).append(':').append(e.getValue().toString()).append(", "));
			*/
			// 실행
			function.execute(destination);
			/*
			// EXPORT
			logs.append("\n\n# EXPORT\n - ");
			exports().forEach(e -> logs.append(e.getName()).append(':').append(e.getValue().toString()).append(", "));
			logs.append("\n\n");

			// 테이블 목록
			logs.append("\n\n# nTABLE [").append(getTableCount()).append("]");
			tables.forEach(table -> logs.append("\n - ").append(table.getName()).append(" : ").append(table.getTable().getNumRows()));

			// 최종시간
			logs.append("\n\nSAP RFC FUNCTION -- END TIME ").append(System.currentTimeMillis() - startTime);
			//log.info(logs.toString());
			*/
		}

		/**
		 * 테이블 갯수를 가져옵니다.<br>
		 * execute() 이후에 불려야합니다.
		 * @return
		 */
		public int getTableCount() {
			if (tables == null) {
				tables = function.getTableParameterList();
			}
			return tables.getFieldCount();
		}

		/**
		 * 테이블을 가져옵니다.<br>
		 * execute() 이후에 불려야합니다.
		 * @param index
		 * @return
		 * @throws Exception
		 */
		public <T> List<T> getTable(int index, final Class<T> T) throws Exception {
			return getTable(index, null, T);
		}

		/**
		 *  테이블을 가져옵니다.<br>
		 * execute() 이후에 불려야합니다.
		 * @param name
		 * @param T
		 * @return
		 * @throws Exception
		 */
		public <T> List<T> getTable(String name, final Class<T> T) throws Exception {
			return getTable(0, name, T);
		}

		public <T> List<T> getTable(int tableIndex, String tableName, final Class<T> T) throws Exception {

			return getTable(tableIndex, tableName, cols -> {
				Field[] cdo = new Field[cols.length];

				for (int i = 0 ; i < cols.length ; i++) {
					try {
						Field field = T.getDeclaredField(underscoreToCamelcase(cols[i]));
						field.setAccessible(true);
						cdo[i] = field;
					} catch (NoSuchFieldException e) {
						// 필드가 없는것은 무시한다.
					}
				}

				return cdo;
			}, (fields, keys, values) -> {
				T t = T.newInstance();
				for (int i = 0 ; i < fields.length ; i++) {
					Field field = fields[i];
					if (field != null) {
						Object val = values[i];
						if (val != null) {
							switch (val.getClass().getName()) {
								case "java.util.Date" : field.set(t, ymdhms.format((Date)val)); break;
								default : field.set(t, val.toString());
							}
						}
					}
				}
				return t;
			});
		}

		/**
		 * 테이블을 가져옵니다.<br>
		 * execute() 이후에 불려야합니다.
		 * @param tableIndex tableName가 null 인경우 해당 인덱스의 테이블을 가져옵니다.
		 * @param tableName 테이블을 이름으로 가져옵니다. (우선순위)
		 * @param columnDefiner
		 * @param rowMapper
		 * @return
		 * @throws Exception
		 */
		public <CDO, RC> List<RC> getTable(int tableIndex, String tableName, ColumnDefiner<CDO> columnDefiner, RowMapper<CDO, RC> rowMapper) throws Exception {

			// 체크
			if (getTableCount() <= tableIndex) {
				throw new IllegalArgumentException("out of table index [" + tableIndex + "/" + getTableCount() + "]");
			}

			// 선언
			List<RC> list = new ArrayList<>();
			JCoTable table = tableName != null ? tables.getTable(tableName) : tables.getTable(tableIndex);

			// 로우가 있는지 확인
			if (table != null && table.isFirstRow()) {

				// 필드를 구합니다.
				List<String> columnList = new ArrayList<>();
				JCoFieldIterator columnIterator = table.getFieldIterator();
				while (columnIterator.hasNextField()) {
					columnList.add(columnIterator.nextField().getName());
				}
				String[] columns = columnList.toArray(new String[columnList.size()]);
				int columnLength = columns.length;
				// 컬럼 디파이너를 부릅니다. Column Definer Object
				CDO cdo;
				if (columnDefiner != null) {
					cdo = columnDefiner.call(columns);
				} else {
					cdo = null;
				}

				do {

					int valueIndex = 0;
					Object[] values = new Object[columnLength];
					JCoFieldIterator valueIterator = table.getFieldIterator();

					while (valueIterator.hasNextField()) {
						Object val = valueIterator.nextField().getValue();
						if (val != null && "java.util.Date".equals(val.getClass().getName())) {
							val = ymdhms.format((Date)val);
						}
						values[valueIndex++] = val;
					}

					list.add(rowMapper.call(cdo, columns, values));

				} while (table.nextRow());
			}

			return list;
		}

		/**
		 * 테이블을 가져옵니다.<br>
		 * execute() 이후에 불려야합니다.
		 * @param index
		 * @return
		 * @throws Exception
		 */
		public List<Map<String, Object>> getTableMap(int index) throws Exception {
			return getTable(index, null, null, (cdo, keys, values) -> {
				Map<String, Object> map = new LinkedHashMap<>();
				for (int i = 0 ; i < keys.length ; i++) {
					map.put(keys[i], values[i]);
				}
				return map;
			});
		}

		/**
		 * 테이블을 가져옵니다.<br>
		 * execute() 이후에 불려야합니다.
		 * @param index
		 * @return
		 * @throws Exception
		 */
		public List<Map<String, Object>> getTableMap(String name) throws Exception {
			return getTable(0, name, null, (cdo, keys, values) -> {
				Map<String, Object> map = new LinkedHashMap<>();
				for (int i = 0 ; i < keys.length ; i++) {
					map.put(keys[i], values[i]);
				}
				return map;
			});
		}

		/**
		 * 모든 테이블을 맵으로 가져옵니다.<br>
		 * execute() 이후에 불려야합니다.
		 * @return
		 * @throws Exception
		 */
		public List<List<Map<String, Object>>> getAllTableMapList() throws Exception {
			List<List<Map<String, Object>>> list = new ArrayList<>();
			int tableCount = getTableCount();
			for (int i = 0 ; i < tableCount ; i++) {
				list.add(getTableMap(i));
			}
			return list;
		}

		/**
		 * 모든 테이블을 맵으로 가져옵니다.<br>
		 * execute() 이후에 불려야합니다.
		 * @return
		 * @throws Exception
		 */
		public Map<String, List<Map<String, Object>>> getAllTableMapMap() throws Exception {
			final Map<String, List<Map<String, Object>>> root = new LinkedHashMap<>();
			tables.forEach(table -> {
				try {
					String name = table.getName();
					root.put(name, getTableMap(name));
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			});
			return root;
		}
	}

	/** 연결을 시도합니다. */
	public static SapManagerBinder newSapManagerBinder() {
		return new SapManagerBinder(new Properties());
	}

	/**
	 * 커넥터 연결
	 * @author		박용서
	 * @since		2018. 2. 20.
	 */
	public static class SapManagerBinder {

		// 환경
		final Properties properties;

		// 내부함수
		SapManagerBinder(Properties properties) {
			this.properties = properties;
		}

		public synchronized SapManager bind() throws JCoException, IOException {
			return bind(false);
		}

		/** 바인드 */
		public synchronized SapManager bind(boolean force) throws JCoException, IOException {
			File connectionFile = new File(CONN_FILE_NAME+".jcoDestination");
			if (force && connectionFile.exists()) {
				connectionFile.delete();
			}
			if (!connectionFile.exists()) {
				FileOutputStream fos = new FileOutputStream(connectionFile, false);
				properties.store(fos, "make connection file");
				fos.close();
			}
			return new SapManager();
		}

		/** 값 세팅 */
		public SapManagerBinder set(String key, String value) {
			properties.setProperty(key, value);
			return this;
		}
	}

	final public static ColumnDefiner<Map<String, Integer>> MAP_COLUMN_DEFINER = (column) -> {
		Map<String, Integer> map = new HashMap<>();
		for (int i = 0 ; i < column.length ; i++) {
			map.put(column[i], i);
		}
		return map;
	};

	/**
	 * 처음 생성된 Column을 보여주는 뷰입니다.
	 * @author		박용서
	 * @since		2018. 2. 20.
	 */
	public static interface ColumnDefiner<CDO> {
		CDO call(String[] rows) throws Exception;
	}

	/**
	 * 행 매핑
	 * @author		박용서
	 * @since		2018. 2. 20.
	 */
	public static interface RowMapper<CDO, RC> {
		RC call(CDO columnDefiner, String[] rows, Object[] values) throws Exception;
	}

	private static String underscoreToCamelcase(String camelcase) {
		if (camelcase == null) {
			return null;
		}
		boolean flag = false;
		char[] under = camelcase.toCharArray();
		char[] camel = new char[under.length];
		int pos = 0;

		for (char c : under) {
			if (c == '_') {
				flag = true;
			} else {
				camel[pos++] = flag ? Character.toUpperCase(c) : Character.toLowerCase(c);
				flag = false;
			}
		}

		return new String(camel, 0 , pos);
	}
}