Download Running SAS Scripts Embedded within JSP Tags through SAS/Connect Java API

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
NESUG 17
Applications
Running SAS® Scripts Embedded within JSP Tags through SAS/Connect Java API
Syloke J. Soong, Allegro MicroSystems, Inc.
large amounts of adhoc records are to be constructed and
inserted, it is also a development and debugging
convenience to construct plain records over using SQL
insert-values statements. (On the other hand, it would be
preferable if SAS Institute would decide to allow use of
macros as CARDS records. Why not, is a question SAS
Institute should oblige to answer.)
Abstract
A SAS script executes from a command line by invoking
<sasroot>/sas <scriptname>.sas.
There are three ways of embedding and running that script
from a JSP. They are
1. Using WebAF (AppDev Studio) APIs and tag
libraries.
2. Using SAS/Intrnet.
3. Using SAS/Connect Java API.
Imagine there exists a legacy suite of SAS scripts with
strong motivation to migrate to J2EE as well as to a new
data schema. These are the plans.
1.
2.
The paper briefly discusses the differences between the
three and focuses on the last method.
1. Description of a custom JSP tag to accept blocks of
text.
2. Embedding a SAS script within the JSP tag for
submission to the server.
3. Eliciting a SAS/Connect connection.
4. How the JSP retrieves the output of the SAS script.
5. Eliciting a single user JDBC connection from the
SAS/Connect connection.
6. Submitting SQL blocks embedded in the JSP to the
JDBC connection.
7. Comparison between SAS/Share JDBC and
SAS/Connect JDBC.
8. Migrating a legacy SAS report to the web easily.
9. Selective incremental conversion of legacy SAS
reports to SAS Java technologies.
3.
4.
5.
Deploy SAS/Connect and SAS/Share.
Move SAS jobs lock-stock-and-barrel into J2EE,
without any change to the scripts.
Gradually and selectively modify SAS scripts to Java.
Design a new schema compatible to real-time loading
and analysis as well as corrective reloading.
Modify loading procedure to load into both new
tables as well as deprecated columns.
The following are quality improvement objectives of the
plans.
1.
2.
3.
Introduction
The inspiration that initiated the solution described in this
discussion was attempts to submit SAS scripts from a
servlet/JSP or Java class to a SAS/Connect session and
then retrieving any SAS/Graph objects generated directly
from that connection, without involving the use of external
services like FTP, NFS, SMB or SAS/Intrnet.
With the new schema, the thousands of weekly
regenerated menu hierarchy HTML files would be
generated dynamically by a single JSP.
With SAS/Connect, SAS/Share, JSP and JDBC, all
HTML and excel reports will also be generated
dynamically.
Finally, generate charts dynamically – without
deploying SAS/Intrnet or WebAF. Without using
FTP, PIPE or SOCKET FILENAMEs.
The discussion requires a comprehension of JSP authoring
and terminology. All the example codes are simplified and
exception handlers removed. Some class libraries, e.g.
JUtil, will not be discussed.
The JSP Text Tag
To illustrate exploiting this solution, a scenario involving
the migration of legacy SAS jobs generating static reports
to a J2EE dynamic generation of SAS graphs, reports and
menus drawn from real-time dataset queries.
The term “text object” will mean an instance of the
custom text tag. When declared with an ID, the text object
is instantiated,
Additional inspiration towards this solution was
frustration over the limitation of SAS macros, especially
the limitation where SAS macros cannot be used as
CARDS records. Dynamic CARDS records is an essential
convenience in implementing a dynamic web framework.
It avoids the hassle of managing file system resources to
construct input data files at the remote SAS session. When
<nesug:text id="menu_sql"
scope="request">
Therefore, the compiler would not allow more than one
text tag ID to be declared within the same java code
block, as in the case of any java variable.
1
NESUG 17
Applications
<nesug:text ref="LoadWeekly_sas"
id="LoadWeekly_sas"
scope="request"/>
When declared with a REF, the text object is not
instantiated but referenced.
5. LoadWeekly_sas SAS script is submitted:
<nesug:text ref="menu_sql"
scope="request">
Connect.submitLines
(""+LoadWeekly_sas);
The compiler will throw an error if a REF is made without
a preceding ID declaration. When invoked as REF, the
text tag is also on “append” mode. A text tag invoked with
REF within an iteration loop would allow it to accumulate
text. A text tag invoked with ID within an iteration block
would cause it to be reinstantiated per iteration. JSP
custom tag technology also allows Java code within a tag
enclosure, another way text could also be accumulated.
LoadWeekly.jsp:
<%@taglib uri="/WEB-INF/nesug-tags.tld"
prefix="nesug"%>
<%@page import="example.jdbc.JUtil" %>
<%@page import="javax.mail.*" %>
<%@page import="javax.mail.internet.*" %>
<%@include file="LoadWeekly.jspf"%>
<%@include file="MkSASConnect.jspf"%>
The body text is accumulated into a StringBuffer. At
anytime, the accumulated text to be reproduced using its
ID/REF handle:
<nesug:text ref="LoadWeekly_sas"
id="LoadWeekly_sas" scope="request"/>
<%
Connect.submitLines(""+LoadWeekly_sas);
<%=menu_sql%>
LoadWeekly.jspf:
When a tag is declared in one JSP it can be referenced by
another JSP within the declared scope persistence. This
allows accumulating text in one JSP but used by another.
The referencing page must declare it with both the ID and
REF attributes,
<nesug:text id="LoadWeekly_sas"
scope="request">
%macro loadprod(location);
filename wkdata
"//DataMgr/Manuf/$location..dat";
%if %sysfunc(fexist(wkdata)) %then %do;
data lastwk;
attrib end_date informat yymmdd10.;
attrib lot
format $15;
attrib location format $2;
attrib division format $10;
attrib product format $4;
attrib param
format $20;
attrib value
informat 8.3
file wkdata;
location = "$location";
input end_date yymmdd10.
lot $ division $ product $
param $ value;
run;
%end;
%mend;
<nesug:text id="menu_sql"
ref=”menu_sql” scope="request">
Using the Custom JSP Tag Technology
The following is the script of a simple SAS job loading
weekly data. It is copied and pasted from a scheduled
production job. It is enclosed within a custom JSP text tag
<nesug:text>, in order for a JSP to send it as a text
stream to the SAS server and execution is invoked through
SAS/Connect.
1.
LoadWeekly.jsp calls LoadWeekly.jspf.
%loadprod(VA); %loadprod(MA); %loadprod(CA);
2. The object Connect is the SAS/Connect connection
%let srv=prodshr.example.mydomain;
libname prod server=srv.__5010;
instantiated when LoadWeekly.jspf calls
SASConnect.jspf
proc append base=prod.weekly;
data=lastwk force;
run;
</nesug:text>
<%@include file="SASConnect.jspf"%>
3.
LoadWeekly.jsp calls LoadWeekly.jspf to declare the
text object LoadWeekly_sas. The text object is
declared persistent within a request.
Refer to the appendix for java code for set-up of and
initiating a session SAS/Connect.
Job completion notification email is constructed and sent.
4. The LoadWeekly_sas text object is resurrected by
LoadWeekly.jsp:
1. The text object transforms the query results stored in
String[] verify into HTML table:
2
NESUG 17
Applications
Scheduling a J2EE Job
<nesug:text id="JobStatus_html"
scope="request">
2. The text object is included as a message body:
The job is run as a J2EE URL and therefore requires the
scheduler to trigger the URL of the JSP containing the
SAS script. A Java application, RunUrl, is compiled using
JBUilder, which can generate either a Windows or Solaris
executable file.
3. The message is sent:
RunUrl can be scheduled from any system with a
scheduler and JRE 1.4,
mailattrs.put(JobStatus_html,
mmsgs);
sendmail.send(mailattrs, true);
RunUrl
http://wwwprod/batchjobs/manuf/capability
.jsp?code=7of9
<nesug:text id="Verify_sql" scope="request">
select location, product, division,
count(distinct lot)
from lastwk
</nesug:text>
<%
Object[][] verify
= JUtil.SqlFetch(
Connect.jdbcConnection, ""+Verify_sql);
Connect.disconnect();
%>
Having the URL parameter “code” prevents inadvertent
triggering of the job by URL crawlers. Where at the
beginning of the JSP, the job would not run unless the
parameter “code” is satisfied:
code = request.getParameter("code");
if (code==null || code.notequals("7of9")
return;
<nesug:text id="JobStatus_html"
scope="request">
<html>
Summary of weekly data loaded.<br>
<table border=1 align=left>
<tr>
<th>Location</th> <th>Product</th>
<th>Division</th> <th>Number of Lots</th>
</tr>
<%
for (int i=0; i<verify.length; i++)
{%>
<tr> <%
for (int j=0; j<verify[i].length; j++)
{%>
<td><%=verify[i][j]%> <%
}%>
</tr> <%
}%>
</table>
</html>
</nesug:text>
The code for RunUrl.java is in the appendix.
Treating the Legacy Report
The following legacy SAS script (simplified) has been
running weekly to generate a set of static reports. It
iteratively calls
1.
2.
3.
mkcaprpts macro (not shown) to generate html and
excel reports.
mkshewhart macro to generate boxcharts.
mkchoices to cumulatively refresh a three level
hierarchical choice board.
%macro capabilityreport();
%let srv=prodshr.example.mydomain;
libname prod server=srv.__5010;
/* Send job completion email to interested
parties */
java.util.Properties mailattrs =
new java.util.Properties();
mailattrs.put("from", "SAS@JOB-DoNotReply");
mailattrs.put("host", "exchangesvr");
mailattrs.put(
"subject", "Weekly Load Job Completed");
Hashtable recip = new Hashtable(2);
recip.put(javax.mail.Message.RecipientType.TO,
"janeway@voyager");
recip.put(javax.mail.Message.RecipientType.CC,
"jluc@enterprise");
mailattrs.put("recipient", recip);
mailattrs.put(JobStatus_html, mmsgs);
sendmail.send(mailattrs, true);
proc sql;
create table values as
select * from prod.capable
where date() - rpt_date LT 360;
create table choices as
select distinct division, location, product
from values;
quit;
data choices;
n = _n_;
set choices end=end;
if end then call symput("numrpts", n);
run;
%let rpt = 0;
%do %while &rpt le &numrpts ;
proc sql;
3
NESUG 17
Applications
select min(n), division, location,
product
into :rpt, :div, :loc, :prod
from choices where n gt "&rpt";
quit;
%mkcaprpts(values,&home/&div/&loc/&prod);
%mkshewhart(values,&home/&div/&loc/&prod);
%mkchoices(&home,&div,&loc,&prod);
%end;
%mend;
where date() - rpt_date LT 360
<%=itemwhere%>
<%=Where%>
order by <%=field%>
</nesug:text>
<%
Object[][] values = JUtil.SqlFetch(
MrBean.JdbcLogin, ""+menu_sql);
%>
<nesug:text ref="MenuTitles" scope="session">
<td><%=MrBean.MkTitles(values)%></td>
</nesug:text>
mkshewhart macro:
<nesug:text ref="MenuSelects" scope="session">
<td><%=MrBean.MkSelect(values)%></td>
</nesug:text>
<%}%>
%macro mkshewhart(ds,gpath);
filename boxfile "%trim(%left(&path))";
goptions reset=goptions gsfname=boxfile
gsfmode=replace device=GIF;
<HTML><BODY>
<FORM NAME=’MenuSelects’>
<!-- Code to display menu not shown -->
</FORM>
</BODY></HTML>
/* Code to create sample limits from values
dataset not shown here. */
proc shewhart data=values limits=limits
gout=work.shew;
XSCHART median * product /
haxis=axis1 vaxis=axis2
/* rest of shewhart statements */
run;
MenuItems.jspf:
<jsp:useBean id="MrBean"
class="example.nesug.MrBean" scope="session" />
<%
proc greplay igout=work.shew nofs;
replay shewhart;
run;
filename boxfile clear; quit;
%mend;
if (MrBean.MenuItems==null)
{
// Specify the menu.
Object[][] MenuItemsA =
{
/*{Menu Label, db field name, sql where,
*/
{"Location", "location", null},
{"Division", "division", null},
{"Product", "product",
"product>'AA0000' and product<='ZZ9999'"},
};
MrBean.MenuItems = MenuItemsA;
}
%>
Due to its static and monolithic nature, the whole job is
rerun whenever any erroneous data is reloaded. Therefore,
it needs to be migrated to J2EE for dynamic generation.
The following shows the migration done. Menu.jsp which
calls MenuItems.jspf, creates a dynamic navigator for the
user.
Menu.jsp:
<jsp:useBean id="MrBean"
class="example.nesug.MrBean" scope="session" />
<%@taglib uri="/WEB-INF/nesug-tags.tld"
prefix="nesug"%>
<%@include file="JdbLogin.jspf"%>
<%@include file="MenuItems.jspf"%>
Menu.jsp, in conjunction with MenuItems.jspf, could be
used as a general purpose menu builder reusable with
other applications.
<nesug:text id="MenuTitles" scope="session"/>
<nesug:text id="MenuSelects" scope="session"/>
<%
String[][] Selection = MrBean.ResolveParameters
(request, "SELECT");
GetReport.jsp (not discussed here) allows users to choose
one of many reports. Gradually, as each report is
converted to dynamic generation, the relevant code
generating the link in GetReport.jsp is updated to point to
the new URL. This allows both J2EE jobs to run
comparatively until we are satisfied that the J2EE job is as
least as acceptable as the pre-generated pages.
String param = null;
String Where = MrBean.MkWhere(Selection);
for (int i=0; i<MrBean.MenuItems.length; i++)
{
String label = ""+MrBean.MenuItems[i][0];
String field = ""+MrBean.MenuItems[i][1];
Object itemwhere = MrBean.MenuItems[i][2];
%>
<nesug:text id="menu_sql" scope="request">
select distinct <%=field%>
from prod.capable
The discussion has shown a SAS script could be sectioned
into a number of text objects and each section modified as
desired. The author conjured this technique on
encountering SAS scripts with portions better done with
J2EE and other portions best left alone. The technique
allows migration without breaking up the job into J2EE
4
NESUG 17
Applications
and non-J2EE portions, hence, avoiding increase in
dependencies.
run;
filename boxfile clear;
quit;
%filesucker(<%=tmpfile%>boxplot.gif);
</nesug:text>
Generating Graphs through SAS/Connect
SAS/Connect or JDBC provides no means of transporting
objects like graphs to the J2EE server. Except with the
trick of breaking the GIF file up into streams of characters
to be stored in a dataset. It is then pulled over to the J2EE
server through JDBC and reconstituted by the JSP.
The SQL to pull the dataset records containing the GIF
stream:
<nesug:text id="GetGraph_sql" scope="request">
select g, n
from <%tmpname%>
order by n
</nesug:text>
First, a new macro, FileSucker, is defined:
%macro filesucker(gf);
/* Macro filesz(not shown) is a %sysexec ls –l
* to get size of &gf and sets value of
&filesz.
*/
%filesz(&gf);
%let fldlen = 100;
filename g "&gf" recfm=f lrecl=&filesz;
data <%tmpname%>;
infile g TRUNCOVER ;
informat g $char&fldlen..;
format g $char&fldlen..;
if (_n_-1) * &fldlen le &filesz;
n = _n_;
input g $char&fldlen.. @@;
output;
run;
%mend;
Run SAS/Connect on those text tags:
<%@include file="MkSASConnect.jspf"%>
<nesug:text ref="LoadWeekly_sas"
id="LoadWeekly_sas" scope="request"/>
Connect.submitLines(""+boxplot_sas);
Connect.submitLines(""+LoadWeekly_sas);
Execute the SQL to pull the GIF stream over to the JSP.
Object[][] g
= JUtil.SqlFetch(Connect.jdbcConnection,
""+GetGraph_sql);
if (g==null || g.length==0)
return;
The GIF stream cannot include characters not originally in
the GIF, otherwise, corrupting the stream. Since the text
tag object reproduces CR/LF characters faithfully, care
must be taken where the tag angle brackets are placed to
avoid creating those characters. As in the following loop
to reassemble the GIF stream:
When supplied with an argument &gf as filename, it
proceeds to break the file up to store it as dataset rows of
&fldlen length.
The body of MkShewHart macro is now part of
MkShewHart.jsp. After GREPLAY, FileSucker is called
to suck the file into a dataset. The JSP predetermines the
name of the dataset into which the file is sucked.
for (int i=0; i<g.length; i++)
{%><allegro:text ref="gbuf" scope="session"><%
=g[i][0]%></allegro:text><%}
%>
<jsp:forward page="ShowGif.jsp"/>
The former MkShewHart macro in MkShewHart.jsp:
<%
String tmpname = MrBean.mktmpname();
String tmpfile = MrBean.tmpdir+'/'+tmpname;
%>
<nesug:text id="boxplot_sas" scope="request">
filename boxfile "<%=tmpfile%>boxplot.gif";
goptions reset=goptions gsfname=boxfile
gsfmode=replace device=GIF;
Similarly, the actual content file generated by a JSP and
received by the browser is always riddled with blank lines.
For that reason, ShowGif.jsp must be a one-line jsp.
<%@taglib uri="/WEB-INF/allegro-tags.tld"
prefix="allegro"%><allegro:text id="gbuf"
ref="gbuf" scope="session"></allegro:text><%
=gbuf%>
/* code to create sample limits from values
* dataset not shown here.
*/
proc shewhart data=values limits=limits
gout=work.shew;
XSCHART median * product /
haxis=axis1 vaxis=axis2
/* rest of shewhart statements not shown */
run;
quit;
ShowGif.jsp is called by MkShewHart.jsp to send the GIF
stream to the browser.
Similar intricacy is appropriate when constructing files to
be viewed by Excel plug-in of the browser, as illustrated
by ShowXls.jsp
<%@page contentType="application/vnd.ms-excel"%
><%@taglib uri="/WEB-INF/allegro-tags.tld"
proc greplay igout=work.shew nofs;
replay shewhart;
5
NESUG 17
Applications
prefix="allegro"%><allegro:text
id="TabulateDataLines" ref="TabulateDataLines"
scope="session"></allegro:text><%
=TabulateDataLines%>
from DICTIONARY.MEMBERS
where libname='WORK';
quit;
Where we could also exploit workdir for session transient
file operations, for example, like FileSucker operations.
The author plans to enhance the text tag classes to allow
options to ignore CR/LF characters, to avoid needing such
intricacies. Beyond the boundaries of a text object, a JSP
would still generate an HTTP stream riddled with blank
lines, any file stream like XLS or GIF should not be
projected by the JSP that had generated it. Rather, the
generating JSP should call the appropriate ShowXxx.jsp.
Difference Between SAS/Share JDBC and
SAS/Connect JDBC
Use of JDBC from both SAS/Share and SAS/Connect has
been shown in this discussion.
Sharing a Macro Store
SAS/Share JDBC is invoked using SAS/Share-Net JDBC
driver. It retains the full resource sharing and locking
features of SAS/Share.
SAS macros used by J2EE dynamic reporting should be in
a separate macro store from that used for running batch
jobs, to keep the catalogue file as small as possible. SAS
sessions cannot share a writeable catalogue file, as the
first session would lock it until the session ends. It is also
not recommended to declare the WORK macro catalogue
with WRITE-ONLY access, as that would preclude JSPs
from constructing macros dynamically.
The contents of a dataset produced by SAS script
submitted through SAS/Connect can be extracted to a
servlet using SAS/Connect JDBC, without use of
SOCKET or FTP FILENAMEs, or remote file systems.
SAS/Connect does not have resource sharing capability.
Therefore, SAS/Connect or its JDBC derivative should
not be used for read/write on a shared production dataset.
As shown in the examples, SAS scripts running on
SAS/Connect should reference librefs of SAS/Share
servers when using shared data.
The following procedure is recommended. Say, the file
name of the J2EE macro catalogue is j2ee.macr.sas7bcat.
For each SAS/Connect session invoked by a JSP or servlet
include this as example init script to copy
j2ee.macr.sas7bcat into the WORK libref directory
%let brother=where.art.thou;
libname here server=brother.__5050;
proc sql noprint;
select max(path) into :workdir
from DICTIONARY.MEMBERS
where libname='WORK';
quit;
SAS/Share JDBC is incapable of procedural and iterative
supplementary languages like Sybase TSQL or Oracle
PL/SQL. With the full power of SAS behind it,
SAS/Connect provides superior equivalence to the
supplementary languages of those RDBMSs. A JDBC
connection derived from the SAS/Connect session that
generated a session-transient object, such as a graph, is
essential for sucking the object over.
/* Copy the macro catalog to work directory
* before any macros are called or created
* which would lock work.sasmacr
*/
%sysexec cp /
usr/sas/v8/macros/j2ee.macr.sas7bcat
&workdir/sasmacr.sas7bcat;
SAS/Connect JDBC is a telnet connection to a single user
SAS session. The JDBC connection is elicted from a
SAS/Connect connection simply:
/* Trigger recognition of work.sasmacr as
* default mac store by any macro reference.
*/
%let workdir = %trim(&workdir);
libname macros "&workdir";
Object[][] HanSolo
= JUtil.SqlFetch(Connect.jdbcConnection,
""+Princess_Leia_SQL);
/* Define dataset libraries */
%let orbit1=obiwan.kenobi.jedi;
%let orbit2=darth.vader.sith;
libname skywalk remote server=orbit1.__20010;
libname moonwalk remote server=orbit2.__20010;
%annomac;
Where Connect is the instance variable of the
SAS/Connect session.
A ShareNet JDBC connection connects to a multi-user
SAS/Share process. Details of obtaining ShareNet JDBC
connection is not explained but exemplified in calling
Note extraction of workdir is helpful,
proc sql noprint;
select max(path) into :workdir
6
NESUG 17
Applications
MrBean (also not shown) which initiates and stores the
connection,
Java charting in ADS pales in comparison to SAS/Graph
with QC and STAT. Being a non-object-oriented drawing
system however, compositing graphs using SAS/Graph
overlay is a very delicate affair. Retaining SAS/Graph for
statistical charting, JfreeChart with CEWolf appears as a
good alternative to ADS APIs for business and composite
charting. It may be wiser too to depend on dedicated IDEs
like Jbuilder or NetBeans.
Object[][] values = JUtil.SqlFetch(
MrBean.JdbcLogin, ""+menu_sql);
Caching the Reports
It may be apparent that some dynamically generated
reports require caching. Presuming that a caching strategy
has been defined and a list of Most Frequently Used
report signatures accumulated. Hence, the usefulness of
RunUrl class when running a nightly scheduled J2EE job
to trigger JSP reports falling into the signature list.
Contacting the Author
Author’s Name
Mailing Address
Email
Comparison of SAS WebTechnologies
SAS/Connect with Text Tag & FileSucker
SAS/WebAF with IOM
SAS/Intrnet
Syloke J Soong
405 Western Ave #193
South Portland
ME 04106
[email protected]
Appendix – Initiating a SAS/Connect Session
A SAS/Connect connection is a telnet conduit for a JSP to
start and communicate with a SAS session on the server,
with the following command:
Submits a SAS script
Y
Y
Y
Produces SAS/Graph files
Y
Y
Y
SAS/Graph files streamed
N
N
Y
directly to JSP
Need to register application
Y
N
N
Derivative JDBC connection
N
N1
Y
SAS session per connection
N
N
Y
1
The method deriving a JDBC connection method from an
IOM connection does not work in AppDev Studio 2.
AppDev Studio 3 allows JDBC Connection to be
specified, which is not derived from the IOM connection.
sas -dmr -comamid tcp -device grlink -noterminal
-nosyntaxcheck
ConnectionInfo class (instantiated by SASConnect.jspf)
contains the telnet command:
package example.sas.net.connect;
public class ConnectInfo
extends java.util.Properties
{
public ConnectInfo() {}
Within SAS8.2 and AppDev Studio (ADS) 2, the author’s
experience with IOM access on SAS/Share librefs has
been unusably slow. Referencing SAS/Share librefs is
necessary for dataset sharing. Providers of non-web EIS
normally require extraction of data cubes from a shared
system for non-shared use. As EIS cubes go by GByte
sizes with latencies too long for ad-hoc generation, a J2EE
installation would need to consider restricting concurrent
use of an IOM dependent URL with the JSP responding,
“The number of accesses has exceeded the number of
cubes available. Please try later.” ADS3 provides
widgets which accepts a JDBC connection. JDBC, unlike
IOM, Intrnet or SAS/Connect cannot be used to trigger
any AF or EIS widgets dependent on SCL.
public void setDefaults()
{
put("prompt1", "login:");
put("prompt2", "Password:");
put("prompt3", "sasuser>");
put("promptTimeout1", "5");
put("promptTimeout2", "5");
put("promptTimeout3", "5");
put("sasPortTagTimeout", "45");
put("response1", "sasuser");
put("response2", "saspw");
put("response3", sasdmr);
put("responseTimeout1", "30");
put("responseTimeout2", "30");
put("userNameResponse", "response1");
put("passwordResponse", "response2");
}
String host;
int port;
static String sasdmr = "sas -dmr -comamid
tcp -device grlink -noterminal -nosyntaxcheck";
}
IOM is advantageous due to dependence on SAS Object
Spawner, which exploits CORBA technology. An Object
Spawner process waits listening for client requests.
Starting a new SAS/Connect connection starts a whole
new SAS session. SAS/Connect Spawner is merely SAS’s
courtesy of yet another telnet server.
7
NESUG 17
Applications
SASConnect.jspf (called by LoadWeekly.jsp) instantiates
the classes performing the connection set-up:
connection.clearListLines();
connection.rsubmit(lines);
connection.clearEditLines();
lines = connection.getListLines();
if (lines.length()>0)
list.add(lines);
<%@page pageEncoding="UTF-8"%>
<jsp:useBean id="Connect"
class="example.sas.net.connect.SASConnection"
scope="session"/>
<%@page import=
"example.sas.net.connect.ConnectionInfo"%>
<%
ConnectionInfo conninf = new ConnectionInfo();
conninf.host = "prodshr";
conninf.port = 23;
conninf.setDefaults();
Connect.setConnectionInfo(conninf);
Connect.connect();
%>
}
status.add("Execution complete.");
}
catch (ConnectException e)
{
status.add(e.getMessage());
}
}
public synchronized void connect()
{
try
{
status.add("Logging onto " + host);
connection = (ConnectClient)
new TelnetConnectClient(connectInfo);
connection.connect(
connectInfo.host, connectInfo.port);
jdbcConnection = connection.getSharenet();
System.out.println(jdbcConnection);
connect = true;
status.add(
"SAS init, connection complete");
}
catch (ConnectException e)
{
status.add(e.getMessage());
System.out.println(e);
}
return;
}
SASConnect.jspf passes ConnectionInfo properties to
SASConnection class. The SASConnection class reads the
telnet command from the properties to perform the telnet.
The SASConnection class is a modification of an example
from SAS Support web site. Tunneled connection, a
requirement for connection in applets, is not in the
modified code.
SASConnection.java:
package example.sas.net.connect;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Vector;
import com.sas.net.connect.ConnectClient;
import com.sas.net.connect.ConnectException;
import com.sas.net.connect.TelnetConnectClient;
public void disconnect()
{
if (connect == true)
{
try
{
connection.disconnect();
connect = false;
}
catch (ConnectException e)
{
connect = false;
}
connection = null;
}
}
public class SASConnection
{
public SASConnection()
{}
public SASConnection(
ConnectionInfo connectinfo)
{
connectInfo = connectinfo;
}
public SASConnection setConnectionInfo(
ConnectionInfo connectinfo)
{
connectInfo = connectinfo;
return this;
}
boolean connect = false;
String host = null;
ConnectionInfo connectInfo;
public synchronized void
submitLines(String lines)
{
try
{
if ((lines!=null)&&(lines.length()>0))
{
status.add(
"Submitting SAS statements ...");
public int port = 23;
public java.sql.Connection jdbcConnection
= null;
public ConnectClient connection = null;
public Vector status = new Vector();
public Vector list = new Vector();
}
8
NESUG 17
Applications
Appendix – RunUrl
URL url = new URL(args[0]);
URLConnection uconn =
url.openConnection();
Object ucont = uconn.getContent();
System.out.println("Content:"+ucont);
RunUrl.java:
package example.net;
}
catch (MalformedURLException e)
{ e.printStackTrace();
}
catch (java.io.IOException e)
{ e.printStackTrace();
}
import java.net.URL;
import java.net.URLConnection;
import java.net.MalformedURLException;
public class RunUrl
{
public RunUrl(){}
}
public static void main(String[] args)
{
try{
}
Appendix - Remote SAS/Graph from JSP through SAS/Connect
Browser
HTTP
Request
HTTP
Response
Tomcat
Embedded
SAS script
JSP
Telnet
SAS script
executes
goption
GIF
GIF displayed
File Stream
Reassembler
JDBC
Dataset containing
GIF Stream
sas -dmr
FileSucker
macro
GIF File
SAS Server
SAS® and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other
countries. ® indicates USA registration. Other brand and product names are registered trademarks or trademarks of their respective companies.
9