JCO 連線SAP

sdvingo發表於2010-11-10
2月4日
033-使用JCo遠端呼叫SAP系統函式
要用Java程式遠端呼叫ABAP函式可以使用SAP提供的RFC針對Java程式的API——JCo。
0.JCo的安裝很不幸,雖然JCo是用Java語言編寫的但卻是平臺相關的。(具體來說,JCo的Java包sapjco.jar確實是平臺無關的,但是執行JCo需要RFC庫支援,而RFC庫是平臺相關的)
目前為止我還只有在Windows系統中安裝的版本,因為JCo不是一個開源元件,甚至在SAP官方下載它是需要SAP Service MarketPlace的使用者帳號的。目前我手上的是JCo2.1.6Windows版本。
除了API文件和一些DEMO程式,你需要用到的有3個檔案:librfc32.dll,sapjcorfc.dll,sapjco.jar。
安裝配置步驟如下:
(1).將sapjco.jar匯入到你的專案庫路徑中。SAP官方的指導是要你配置CLASSPATH,這種方法在我看來不值得提倡,因為eclipse或netBeans這樣的IDE可以幫你輕鬆匯入jar庫而無需擺弄系統環境變數。
(2).將sapjcorfc.dll檔案複製至sapjco.jar所在的資料夾中。注意,這兩個檔案必須放在同一個資料夾裡,它們倆必須像熱戀中的情人一樣一直黏在一起才能使JCo正常工作。
(3).將librfc32.dll檔案複製至C:WINDOWSsystem32中。如果系統中已安裝過SAP客戶端,那麼可能本來就已經有一個librfc32.dll在那裡了,SAP官方要求你覆蓋那個檔案,這是一種很不負責的方法。當你覆蓋這個檔案後很可能你的SAPLogon就無法工作了。正確的做法是備份原來的檔案,然後嘗試覆蓋後使用SAPLogon,如果出問題了,把原來那個檔案找回來。我在安裝了710 Final Release版本的SAPLogon機器上使用原來的librfc32.dll執行JCo目前還沒有發生什麼異常。
好了,到現在為止,JCo算是安裝好了,我們可以開始編寫程式了。
1.使用JCo呼叫遠端ABAP函式
使用Java程式遠端呼叫ABAP函式的大致流程是這樣地:先要有SAP系統所在伺服器的IP地址、要登入的SAP系統的客戶端號、系統編號、使用者名稱、使用者密碼,有了這些後,就可以建立一個到該SAP系統的連線,然後在這個連線上呼叫允許遠端呼叫的函式,得到呼叫後的結果,關閉連線。
這其中牽涉到一些細節,我們先看程式:
package jcousage;
import com.sap.mw.jco.IFunctionTemplate;
import com.sap.mw.jco.JCO;
import java.util.Properties;
public class TestJCo {
public static void main(String[] args) {
/*獲得一個到SAP系統的連線 START */
Properties logonProperties = new Properties();
logonProperties.put("jco.client.ashost","192.168.1.123"); //系統的IP地址
logonProperties.put("jco.client.client","800"); //要登入的客戶端
logonProperties.put("jco.client.sysnr","00"); //系統編號
logonProperties.put("jco.client.user","young98"); //登入使用者名稱
logonProperties.put("jco.client.passwd","password"); //使用者登入口令
//用上述條件建立一個連線物件
JCO.Client myConnection = JCO.createClient( logonProperties );
/*獲得一個到SAP系統的連線 END */
myConnection.connect(); //進行實際連線
//如果連線不為null並且處於活動狀態
if (myConnection != null && myConnection.isAlive()) {
//從連線獲得一個邏輯意義上的“倉庫”物件(Repository)
JCO.Repository myRepository =
new JCO.Repository("Repository", //只是一個名字
myConnection); //活動的連線
//要呼叫的SAP函式名稱
String strFunc = "BAPI_FLIGHT_GETLIST";
//從“倉庫”中獲得一個指定函式名的函式模板
IFunctionTemplate ft = myRepository.getFunctionTemplate(strFunc.toUpperCase());
//從這個函式模板獲得該SAP函式的物件
JCO.Function function = ft.getFunction();
//獲得函式的import引數列表
JCO.ParameterList input = function.getImportParameterList();
//設定一個import引數的值。引數名為“MAX_ROWS”,設定值為10
input.setValue(10, "MAX_ROWS");
/*設定結構引數 START */
//如果引數是一個結構,用引數名獲得一個對應型別的結構物件
JCO.Structure sFrom = input.getStructure("DESTINATION_FROM");
//設定結構中的變數。變數名為“CITY”,設定值為“NEW YORK”
sFrom.setValue("NEW YORK", "CITY");
//將處理好的結構賦給該引數
input.setValue(sFrom, "DESTINATION_FROM");
/*設定結構引數 END */
/*設定table引數 START */
//用引數名獲得一個對應型別的內部表物件
JCO.Table tDateRange = function.getTableParameterList().getTable("DATE_RANGE");
//接下來基本屬於體力活了......
//新增一條空行
tDateRange.appendRow();
//定位到第0行
tDateRange.setRow(0);
//設定該行對應變數
tDateRange.setValue("I", "SIGN");
tDateRange.setValue("EQ", "OPTION");
tDateRange.setValue("20040330", "LOW");
//新增一條空行
tDateRange.appendRow();
//定位到第1行
tDateRange.setRow(1);
//設定該行對應變數
tDateRange.setValue("I", "SIGN");
tDateRange.setValue("EQ", "OPTION");
tDateRange.setValue("20040427", "LOW");
//......
/*設定table引數 END */
//執行函式
myConnection.execute(function);
//在執行函式後可用相同方式獲得輸出結果
JCO.Table flights = function.getTableParameterList().getTable("FLIGHT_LIST");
//JCO.Table物件可以直接輸出到html檔案
flights.writeHTML("C:/function.html");
//也可以如下單獨獲得表中個別變數
System.out.println("Airline" + "tt"
+ "from city" + "t"
+ "to city" + "tt"
+ "departure time" + "tt"
+ "price" + "t"
+ "CURR");
for (int i = 0; i < flights.getNumRows(); i++) {
flights.setRow(i);
System.out.println(flights.getString("AIRLINE") + "t"
+ flights.getString("CITYFROM") + "t"
+ flights.getString("CITYTO") + "t"
+ flights.getDate("FLIGHTDATE") + "t"
+ flights.getDouble("PRICE") + "t"
+ flights.getString("CURR"));
}
//斷開連線
myConnection.disconnect();
} else {
System.out.println(false);
}
}
}//:~
首先,將連線需要的資訊放到一個Properties裡,對應的鍵值都是固定的。需要注意的是給定的使用者名稱必須有足夠的許可權進行遠端呼叫。
JCO.Client myConnection = JCO.createClient( logonProperties );
這樣就建立了一個到SAP系統的連線物件了。接著只要在這個物件上呼叫connect()方法就能啟用連線。為了保證這個連線確實可用,加上這一句:
if (myConnection != null && myConnection.isAlive())
接下來比較麻煩,先要從這個活動的連線上拿到一個“倉庫”,然後從這個“倉庫”裡拿到函式模板,再從函式模板裡拿到對應函式物件。
JCO.Repository myRepository =
new JCO.Repository("Repository", //只是一個名字
myConnection); //活動的連線
String strFunc = "BAPI_FLIGHT_GETLIST";
IFunctionTemplate ft = myRepository.getFunctionTemplate(strFunc.toUpperCase());
JCO.Function function = ft.getFunction();
整個過程很唬人,其實也就那麼回事。這裡我們呼叫的是一個叫“BAPI_FLIGHT_GETLIST”函式,這個函式在SAP系統中被宣告為Remote-Enabled Module,能夠被遠端呼叫的函式都必須宣告為Remote-Enabled Module。
好了,我們現在有函式物件了。我們知道,SAP函式主要就是有5個輸入輸出介面:Import、Export、Changing、Tables、Exceptions。其中除了Changing在遠端呼叫中是不被允許的外,其他都是可用的屬性:
Import:透過在函式物件上呼叫getImportParameterList()方法獲得Import變數列表。
JCO.ParameterList input = function.getImportParameterList();
Export:在函式被執行後透過在函式物件上呼叫getExportParameterList()方法獲得Export變數列表。
JCO.ParameterList output = function.getExportParameterList();
Changing:不支援。
Tables:透過在函式物件上呼叫getTableParameterList()方法獲得Tables變數列表。
JCO.ParameterList tables = function.getTableParameterList();
Exceptions:可透過在函式物件上呼叫getExceptionList()方法獲得Exceptions變數陣列,也可呼叫getException(java.lang.String key)獲得單個異常物件。
JCO.AbapException[] abapExceptions = function.getExceptionList();
JCO.AbapException abapException = function.getException( "1" );
Import、Export、Tables所獲得的都是JCO.ParameterList物件。
如果引數是基本型別,則可直接設定:
input.setValue(10, "MAX_ROWS");
Object object = output.getValue("MAX_ROWS");
當然你這樣getValue得到的只能是一個Object物件,如果你知道你會得到什麼型別(一般情況下你應該知道),你可以直接get這個型別。比如,我們知道"MAX_ROWS"是個int值,所以:
int maxRows = output.getInt("MAX_ROWS");
如果引數是一個結構(如果你是個物件導向的程式設計師,請回憶一下C語言中的結構變數,謝謝),那麼你得先得到一個對應此引數的結構物件,然後對結構中的變數成員get或者set:
JCO.Structure sFrom = input.getStructure("DESTINATION_FROM");
sFrom.setValue("NEW YORK", "CITY");
當然如果是Import的話最後別忘了把這個結構放回列表中:
input.setValue(sFrom, "DESTINATION_FROM");
在對Tables的引數進行操作時,也得先得到一個與該引數內部表對應的內部表物件:
JCO.Table tDateRange = tables.getTable("DATE_RANGE");
現在這個內部表物件還是空的,所以要先加一行資料,然後將一個邏輯指標指向該行,再進行各個元素的賦值或取值:
tDateRange.appendRow();
tDateRange.setRow(0);
tDateRange.setValue("I", "SIGN");
tDateRange.setValue("EQ", "OPTION");
tDateRange.setValue("20040330", "LOW");
內部表物件支援直接將表內容傳送到HTML檔案:
JCO.Table flights = function.getTableParameterList().getTable("FLIGHT_LIST");
flights.writeHTML("C:/function.html");
想知道內部表中有幾條資料,可以呼叫getNumRows()方法:
int rows = flights.getNumRows();
結束呼叫時要記得斷開連線:
myConnection.disconnect();
2.使用連線池很多時候我們需要進行大量連線,SAP系統本來就脆弱,連線再一多那真的是老牛拉車了。所以我們要用連線池進行連線。下面是一個使用連線池的例子:
package jcousage;
import com.sap.mw.jco.JCO;
import java.util.Properties;
public class UsageForPool {
public static void createConnectionPool( String poolName,
int maxConnection,
Properties logonProperties) {
JCO.Pool pool = JCO.getClientPoolManager().getPool(poolName);
if(pool == null) {
JCO.addClientPool( poolName, // 連線池名
maxConnection, // 最大連線數
logonProperties ); // logon設定引數
}
}
public static JCO.Client getConnectionInPool( String poolName ) {
JCO.Client connection = null;
JCO.Pool pool = JCO.getClientPoolManager().getPool(poolName);
if(pool != null) {
connection = JCO.getClient(poolName);
}
return connection;
}
public static void releaseConnection( JCO.Client connection ) {
JCO.releaseClient( connection );
}
public static void removeConnectionPool(String poolName) {
JCO.removeClientPool(poolName);
}
public static void main(String[] args) {
Properties logonProperties = new Properties();
logonProperties.put("jco.client.ashost","192.168.1.123");
logonProperties.put("jco.client.client","800");
logonProperties.put("jco.client.sysnr","00");
logonProperties.put("jco.client.user","young98");
logonProperties.put("jco.client.passwd","password");
String poolName = "ThePool";
createConnectionPool(poolName, 6, logonProperties);
JCO.Client connection = getConnectionInPool(poolName);
connection.connect();
if (connection != null && connection.isAlive()) {
System.out.println("Connection is alive!");
releaseConnection(connection);
removeConnectionPool(poolName);
} else {
System.out.println("Connection FALSE!");
}
}
}
首先,我們仍然需要一個Properties物件用來建立連線,不過這次是建立的連線是一個連線池:
JCO.addClientPool( poolName, maxConnection, logonProperties );
這個方法同時還需要池的名字以及最大連線數一起作為引數。當然在新建一個連線池之前,檢查一下有沒有相同名字的池已經存在是一個好習慣。
有了池我們就可以在連線池裡獲得連線啦:
JCO.Client connection = JCO.getClient(poolName);
然後在這個連線物件上啟用連線就可以啦:
connection.connect();
接下來就跟上面說的一樣用啦,簡單吧。
用完別忘了釋放連線:
JCO.releaseClient( connection );
如果連線池也不用了就刪了它:
JCO.removeClientPool(poolName);
你學會了嗎?
[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/443058/viewspace-1041270/,如需轉載,請註明出處,否則將追究法律責任。

相關文章