The webAF Middleware Server uses a technique known as funneling, a process that daisy-chains method calls through Java middleware. Using more common Internet terminology, funneling means making multiple hops from the client to reach the server, with each of the hops points having possible computational intelligence.
There are only a few advantages, but they are quite important.
For an example of the memory savings, let us consider an example scenario with
Processes on the Web Server/SAS Server for access will include
For explanation and comparison purposes, two identical web pages are created in webAF with a TreeView containing a LibraryList. One will be funneled, one not. In both cases fixed, costs, such as running a Web Server, will be ignored.
For each method the client sessions create some objects on the server. Every client, regardless of originating browser, only creates a new partition on the server, a new reference at just a few K, and a new object at just a few K. The memory consumption by the above funneled processes on the server will include:
For 10 users this gives a total memory cost of 15 Meg.
Without funneling the same application is significantly more expensive, for example:
SAS Session - 10Meg * 10Sessions (one SAS session per client browser)
For a total cost of 101 Meg, for example, 6 times less memory is used when funneling with 10 users.
And the costs savings scale linearly, for a hundred users against a single server the savings escalate to nearly 1000 Meg.
However, this is not without its disadvantages to be discussed in the next section.
Here are the disadvantages:
The small decrease in performance is based on
In the best case, single client performance is still reduced from 1/100th seconds, 0.01 seconds, for a method call to 1/100 + 1/680 = (100+680)/(100*680) = 0.0114 seconds.
Now, take the same scenario used in the advantages section, with 10 clients making concurrent calls. Since the middleware will have to block and queue every call except one, performance will degrade geometrically. For example, in a scenario with constant server interaction average method response time will be
Funneled:
Number_clients * ((rmi_time + sasConnectForJava_time)/(rmi_time * sasConnectForJava_time)) = 10 * ((100+680)/(100*680)) = 0.114 seconds
Non-Funneled:
sasConnectForJava_time = 1/100 = .01 seconds
For an application that is now 11 times slower.
So, making a worst case performance chart assuming concurrent keystrokes via:
data comp;
users = 1;
do while(users < 10000);
funmem = 14 + users * .6;
normmem = users * 10;
funspd = users * 780 / 68000;
normspd = .01;
space = (normmem - funmem) / funmem;
time = (normspd - funspd) / funspd;
output;
users = users * 10;
end;
run;
proc print;
format space time percent12.2;
run;
| Users | Funneled Memory | Funneled Response | Non-Funneled Memory | Non-Funneled Response | Space/Time Tradeoff Not funneling is: |
| 1 user | 14.6 Meg | 0.0115 secs | 10 Meg | 0.01 secs | 31%smaller/12% faster |
| 10 users | 20.0 Meg | 0.1147 secs | 100 Meg | 0.01 secs | 400%larger/91% faster |
| 100 users | 74.0 Meg | 1.1471 secs | 1000 Meg | 0.01 secs | 1251%larger/99%faster |
| 1000 users | 614 meg | 11.470 secs | 10000 Meg | 0.01 secs | 1528%larger/99.9%faster |
This table only applies to worst case concurrent response time, that is, if the users either pressed a key simultaneously or before the server had serviced the last request queuing up subsequent requests. Memory remains fairly constant. If the requests were staggered then only the 12% response time degradation would be noticed.
In addition to the basic three pieces required for all zero-install client/server Java applications communicating with a SAS server:
Now another piece is added
The implementation of this is currently via RMI for the initial hops with a back-end protocol of either SAS/CONNECT for Java or HTTP-Tunnel, later the initial hops protocol will be changed to be user specifiable.
First the applet is loaded on to the local machine's VM, this is either from the Web server or from the local disk.
Then, in establishing the connection, the webAF system classes (in webAF.jar), note that funneling is specified for this connection to SAS.
Now object creation requests go through the middleware.
Server:

This application will allow you to specify the maximum number of clients allowed per SAS session on any given host. Once the maximum number of clients has been reached for a SAS session a new SAS session will be started for any subsequent clients. Once that SAS session reaches the maximum number a new SAS session will be started, and so on. If a host is not listed in the administration application an unlimited (up to system resources) number of clients will use a single SAS session on that host. To remove a host from the list simply set the Maximum Clients to 0. The webAF Middleware Server will reload these settings automatically once the Apply button has been pressed, making the changes take effect immediately. Note that the administration application writes changes to the <AppDev Studio HOME>/Java/SAS/funnel.cfg file.
Application development within webAF:
With RMI, the protocol used to connect to the Java middleware which then connects to the server, the middleware connection will either use
Currently only JRMP is supported. This imposes extra considerations, for example, when calling a method:
ReturnValueType retval = remoteObject.methodCall(parameter);
there are a few possibilities for parameter, it either subclasses or implements
java.rmi.server.UnicastRemoteObject - call by reference java.io.Serializable - call by value This presents a problem when using funneling as middleware depending on the structure of the application, consider using DataWarehouse.
ReturnValueList = remoteObject.method(updateList1,updateList2)
The ReturnValueList is always OK, it is the output parameter, so call-by-reference or call-by-value is ok.
However updateList1 is not ok, as the method call goes from
Browser RMIProxy - Middleware J2Proxy - SAS
The list is passed by the RMI proxy as call-by-value since HList is a serializable type. The middleware J2 proxy then copies the contents of the lists to the data stream through SAS/CONNECT to SCL. SCL then modifies the contents, which are then stream back to the J2 proxy. The J2 proxy empties its client's list object, the middleware list, and repopulates it with the list from the data stream. The RMI proxy, on the other hand, has already completed its work and does not know that it needs to updates its local list, so it never gets its data. Written as steps the above method call would be:
The updated parameter lists get lost, which is a problem. One circumvention to this problem is to mandate a policy of
outputvalue = remoteobject.method(inputvalue,inputvalue)
rather than
outputvalue = remoteobject.method(updatevalue,updatevalue)
This has the added benefit of improving performance, as the user would then clearly dictate what gets sent over the wire and what does not.