java应用中如何捕抓SAS存储过程输出的流信息
这个指南演示如何在存储过程中使用ods格式化输出流并被用某种语言编写的应用所接收,如java语言
首先我们引用一个简单例子,其用ods格式化输出2个html报表,由于其使用的是sashelp中的数据,故下面例子你可以直接运行在你的环境中,例子如下
l 注:宏 ®ION 用于指示取数据的一个子集
%let REGION=Pacific;
ods html body="shoe_sales.html";
title Shoe Sales by Subsidiary;
title2 Region: ®ION;
proc report data=sashelp.shoes(where=(region="®ION")) nowd;
column subsidiary sales;
define subsidiary / group;
define sales / analysis sum;
run;
title Shoe Sales Detail;
title2 Region: ®ION;
proc report data=sashelp.shoes(where=(region="®ION")) nowd;
column subsidiary product stores sales;
define subsidiary / order;
define product / order;
define sales / analysis sum;
run;
ods html close;
只需要下面3个简单步骤就可以
1、 每一个存储过程应该有一个标志用来指示其是存储过程,如下行,该行应该在%let 和 第一个ods输出之间
*ProcessBody;
2、 转换宏符号作为存储过程的参数,如下
%global REGION;
3、 我们应该初始化ods的开始和结束句子,在存储过程中,我们可以使用下面宏帮助我们自动初始化
%stpbegin
%stpend
改后的代码如下:
%global REGION;
*ProcessBody;
%stpbegin;
title Shoe Sales by Subsidiary;
title2 Region: ®ION;
proc report data=sashelp.shoes(where=(region="®ION")) nowd;
column subsidiary sales;
define subsidiary / group;
define sales / analysis sum;
run;
title Shoe Sales Detail;
title2 Region: ®ION;
proc report data=sashelp.shoes(where=(region="®ION")) nowd;
column subsidiary product stores sales;
define subsidiary / order;
define product / order;
define sales / analysis sum;
run;
%stpend;
我们把该后的程序部署到存储过程服务器上去
一旦部署完成,我们就可以使用外部工具来访问该存储过程,如用excel (需要装Add-In for Microsoft Office)
当然,也可以通过浏览器来访问该存储过程
你可以使用SAS IT APIS 来调用存储过程,你甚至可以使用SAS8.2来完成这一步骤
在开始写java代码前,我们应该明白下面概念
Integrated Object Model (IOM) 是我们远端调用SAS的API.
当我们连接到SAS时,我们将从SAS会话中获得workspace对象.
然后我们从Workspace.中取得其他服务对象
简单的java代码
要调用一个存储过程,你应该包含一个 IStoredProcessService 对象的引用并告诉它:
repository 位置,它是一个定位存储过程的目录路径,格式如 "file:/some/directory". 如果我们异步调用存储过程,那java将不会等待存储过程执行完
存储过车文件的名字
存储过程参数,如"name1=value1 name2=value2 …"
下面通过java定义了一个方法,用于设置且执行存储过程,代码如下:
private SocketListener executeImpl(
String stpName,
Properties params,
IWorkspace workspace)
throws IOException, GenericError
{
// create a socket for SAS to send result stream
// SocketListener is an AppDev Studio utility
SocketListener socket = new SocketListener();
int port = socket.setup();
socket.start();
// get IOM objects from Workspace
ILanguageService languageService = workspace.LanguageService();
IStoredProcessService stpService =
languageService.StoredProcessService();
// get stored process call info
String repository =
LocalEnvironment.getProperty("stp.repository");
String stpParams = buildParamString(params);
Log.debug("repository = "+repository);
Log.debug("stpName = "+stpName);
Log.debug("stpParams = "+stpParams);
// define fileref for socket and issue startup statements
initWorkspace(languageService, port);
// call stored process
stpService.Repository(repository);
languageService.Async(false);
Log.debug("calling stp");
stpService.Execute(stpName, stpParams);
// send SAS log to web app log
Log.debug("checking log");
dumpLog(languageService);
return socket;
}
在上面的代码中,使用了下面3个iom的接口
IWorkspace, ILanguageService, IStoredProcessService
SocketListener类用于监听SAS输出信息,通过端口号和SAS建立联络
该类还使用了2个实用类
LocalEnvironment 类用于环境信息初始和输出
Log类用于简单的记录输出
其他有用的方法,我们将在下面列出
一个存储过程可以接收0个或者多个参数,如下
private String buildParamString(Properties params)
{
StringBuffer paramBuff = new StringBuffer();
Iterator iter = params.keySet().iterator();
while (iter.hasNext())
{
String key = (String) iter.next();
paramBuff.append(key);
paramBuff.append('=');
paramBuff.append('/"');
paramBuff.append(params.getProperty(key));
paramBuff.append('/"');
paramBuff.append(' ');
}
String stpParams = paramBuff.toString();
return stpParams;
}
如同通过api直接调用存储过程,SAS本身不知道是如何输出到指定的socket端口中取,而在默认情况下会输出到_webout中取,故我们可以在初始化时分配好_webout并指向指定的socket端口
private void initWorkspace(ILanguageService languageService, int port)
throws IOException, GenericError
{
// create fileref to my server socket
// CHEAT ALERT: using localhost as host name for socket!
StringBuffer block = new StringBuffer();
block.append("filename _WEBOUT SOCKET ");
block.append('/'');
block.append("localhost");
block.append(':');
block.append(port);
block.append('/'');
block.append(';');
block.append("/n");
String startupStatement =
LocalEnvironment.getProperty("stp.startupStatement");
if (startupStatement != null)
block.append(startupStatement);
Log.debug("submitting "+block.toString());
languageService.Submit(block.toString());
}
直接调用languageService的flushlog方法可以及时地把日志输出
private void dumpLog(ILanguageService languageService)
throws GenericError
{
int count = 0;
String log = null;
Log.sas("/nLOG START/n/n");
do
{
log = languageService.FlushLog(BUFFSIZE);
Log.sas(log);
count++;
}
while (log.length() == BUFFSIZE);
Log.sas("/nLOG END (" + count + ")/n");
}