Updated: 4/29/2003; 11:30:07 AM.
Java Morning/C# Afternoon
Practical living with two modern languages


by Michael Isbell
        

Tuesday, April 29, 2003

Use PooledExecutor to Prevent Flooding Server with Remote XML-RPC Calls
copyright 2003 by Michael Isbell

THE PROBLEM. For simplicity's sake, let's say there are two sets of widgets on a GUI form:

a) a JList filled with tasks from a todo list. Let's say there are twenty tasks in the list. (All right, so it's a set of one.)

b) a set of JTextField widgets.

This works simply enough: when you pick an item in the JList, the JTextField widgets fill with the task's detail information: due dates, assignments, resources and so on.

Behind the scenes, whenever you pick a task from the list, somewhere along the line an XML-RPC call is being fired off over the Internet to a remote server, returning data into a data structure, which your code then parses to fill the JTextFields.

So what's the problem here? To put it another way,

(a) why is the fact that this program is firing off calls remotely significant,

(b) what problems does it create, and

(c) what do we have to do differently to address those problems?

The answer is easy, but you need a fairly sophisticated understanding of everything that's going on to understand why the solution works. So let's tackle these issues one at a time.

(a) That the system issues remote calls to retrieve its data, rather than local calls, is significant because there's potentially no control over the number of calls that can be fired off to the remote server. The server usually has finite capacity, which, combined with a potentially large number of remote accesses creates the problem that (b) potentially many users could use the application and fire off large numbers of XML-RPC requests over the Internet, swamping the server in what is in effect a built-in DoS attack. This is just from scrolling up and down a JList!

Let's look at this in some detail. Let's start by looking at the way the GUI fills the JTextField widgets from the JList.

When you have a standard JList widget on a GUI form, there are two ways to treat the remote calls: synchronously or asynchronously. In other words there are two ways the list box can behave:

1. When you land on an item, the XML-RPC call is made synchronously as a method call in the valueChanged() method attached to the JList. When this is the case you can't scroll to another item (or do much else) until the XML-RPC call returns with data or times out, since you're code is running on the GUI's single thread. Remember, all code executed in an event listener method is executed on Swing's Event Dispatch Thread.

Let's remember for a moment that an RPC call, any RPC call, is by definition a synchronous call. When the remote request is sent over the socket, the socket sits and waits for a response until it arrives. If this request is made in a single-threaded application, the behavior of the application is that no other instruction will be executed until the data is returned. So, unless you place the remote call in another thread, everything stops until that call receives a response or times out.

2. When you land on an item, the XML-RPC call is made asynchronously, by firing off a separate thread from the valueChanged() method. To the GUI user, this means he can scroll up and down at will.

Having said that, there are then two ways that an asynchronous call can behave.

2i) as the list items are selected one by one as the user rapidly scrolls over them, EVERY SELECTED ITEM fires off an XML-RPC call asynchronously. Therefore, one by one, the calls return data and the code executes to fill the data fields related to selected item. If the user is racing his selector up and down the list box, testing to see how many calls he can fire off before he chokes the system, he'll see wildly erratic behavior: flickering data filling fields faster than the eye can follow. Finally, the data "catches up" with the last selected item in the list.

OR

2ii) as the list items are selected one by one as the user rapidly scrolls over them, ONLY THE FIRST SELECTION fires off an XML-RPC call asynchronously. After that, the listbox "magically" knows the the user is wasting its time, and it waits patiently for the user to stop screwing around. Then, when the user has selected the last item (when he's stopped playing with the list box), then, and only then, does the listbox permit the execution of the SECOND XML-RPC CALL, the one that fills the data fields with information related to the (finally) selected item.

A second reminder about Swing's single-thread architecture: if the Swing GUI code has been written correctly, all the XML-RPC threads paint the returned data using methods that place the Runnable objects containing the paint commands onto the Swing Event Queue. Then, one at a time,each Runnable object is dequeued and the Swing Event Dispatch Thread calls each Runnable object's run() method, which contains the command that actually assigns the data returned by the XML-RPC call to its appropriate widget (usually a widget.set(data) call of some kind). Remember that Swing widgets are NOT thread-safe, so when you work with asynchronous threads you still have to play by Swing's rules.

Again, the behavior of this simple example is significant because of its potential for swamping a server with XML-RPC calls simply by scrolling down a list, an activity you wouldn't think twice about in a local application!

BACK TO THE PROBLEM

Well, in the process of analyzing the significance of the example's behavior (a), we also identified the problem it created (b). So let's move on to look at (c). What do we have to do in our application to achieve the desired behavior? Plainly, the behavior in 2ii is the desired behavior for the application, in two ways: first, we don't flood the client application with bacchanalian quantities of wildly flickering data; second, we don't flood the server on the other end, drowning it in pointless XML-RPC requests, wasting server and network capacity to return data that will never be seen.

Let's walk through the way you want things to work on a detail level. After that, I'll show you how to use PooledExecutor to deal with the uncontrolled message generation problem effectively.

Imagine that the user has selected an item in the JList. Then he presses the down arrow and holds it down as the JList items flash by. Then, just as the selector slams into the bottom of the JList, his finger jumps to the up arrow, jams that key down and holds it there. Once more the items roll by at top speed. At the top of the list once more, the user's finger jumps back to the down arrow, presses it and holds it down and...you get the idea. We have a user who is "testing the system!"

Now, let's restate once more the behavior we'd like to see here.

1. When the user picks the first selection, valueChanged() fires off a thread which retrieves data. 2. When the user starts his high-speed scrolling, the first selection causes valueChanged() to fire off a thread and retrieve data. 3. There after, valueChanged() fires off threads, but they don't do anything. The application knows that we don't really want to run threads or retrieve data, we're just traversing a list. 4. Finally, the user lifts his finger from the key and the selector stops on a final selection. Once more, the thread fired off by JList's valueChanged() takes effect and retrieves data.

How do we create this behavior? In fact, we can't make it work exactly as I've described, but we can come close enough--that is, we can make sure that no more than three threads running XML-RPC requests are ever active: one will be running, one will be queued to run, and one will be blocked, and any other thread that tries to muscle its way into the process will be discarded. We can make this happen by using the PooledExecutor class, part of the util.concurrent package.

It's no secret that Java's implementation of concurrency keywords is inadequate for most sophisticated multi-threaded applications (cf. Holub for a fine critical analysis). Fortunately, Doug Lea, a professor in in Oswego, NY, has written a library of standard concurrency data structures that address a number of the problems and limitations of the Java concurrency keywords (cf. Lea, TS-2012 at java.sun.com/learning). Doug is largely responsible for JSR-166, which addresses many of Java's concurrency problems. In fact util.concurrent is so well done it's being integrated into the J2SDK 1.5 release, the release known as "Tiger." From the time Tiger is released, the tools in util.concurrent tools will be the way concurrency is handled in Java applications.

At the top level of the hierarchy in the util.concurrent package, there are three interfaces:


Sync
supports Semaphore and Mutex locking mechanisms

Queue
supports Putting objects on a channel and Taking objects from a channel
usually implemented as a BoundedBuffer or a QueuedChannel

Executor
runs Runnable objects.
usually implemented as QueuedExecutor, ThreadedExecutor or PooledExecutor.

PooledExecutor implements the Executor interface, so that's what we'll look at for the rest of this paper.

How does Executor work? The idea is, you're abstracting the idea of thread execution. With Java as it exists now, you can only run a Runnable by feeding it to a Thread, as in

Runnable r = new Runnable() {
public void run() {
// run something
}
};
Thread t = new Thread(r);

But with classes that implement the Executor interface, you can feed the Runnable to other mechanisms. You can run a Runnable object by

    * creating a QueuedExecutor object, passing it a util.concurrent.Queue object in the constructor, then executing the runnable with the QueuedObject's execute(Runnable r) method (this gives you a queue and a single thread)

    * creating a ThreadedExecutor object, passing the Runnable object in the constructor, then executing the runnable with the ThreadedObject's execute() method (this gives you a single thread) * creating a PooledExecutor object, passing it a Channel of some kind in the constructor, then executing the runnable with the PooledObject's execute(Runnable r) method (this gives you a queue of some kind and a thread pool whose size and characteristics you determine)

Make no mistake: it's Doug Lea's intention that you never use synchronize, wait or notify again it's also his intention that you never create a new Thread() and start() again.

In our example, we'll feed our thread to a thread pool. Threads in a thread pool can be in one of three states:

executing
queued
blocked

Now let's set up a PooledExecutor object with the policies that will accomplish our goals, then I'll explain how it works in the context of the example.

I create my thread pool so that it will run one thread at a time, and keep one thread queued to run. I do this by passing a BoundedBuffer one byte in size to the PooledExecutor constructor: this is where the thread queued to run will reside. We also pass a second parameter, an integer, to indicate the size of the active Thread pool: i.e., how many threads will be run concurrently by this thread pool? In this case, we want the pool to contain one thread.

So, if we pass the PooledExecutor two threads, one will run, and one will be queued to run. This is the code that does that:

pool = new PooledExecutor(new BoundedBuffer(1), 1); pool.setMinimumPoolSize(1);

The call to setMinimumPoolSize() ensures that the thread pool live thread count will never drop below one; there will always be at least one live thread to run a Runnable object.

Now, what happens when the user starts feeding the thread pool even more threads, as the selector races up and down the JList? We need to set two more policies for the PooledExecutor, like this:

pool.waitWhenBlocked(); pool.discardOldestWhenBlocked();

Now, when another new thread is fed to the pool, and it can neither be executed nor queued, then it will be blocked; and as still more threads are fed, each new thread that hits the pool will cause the older thread, blocked, to be discarded.

Finally, I'll set my threads to stay alive for five seconds, and create a "warm" thread for the thread pool (which, remember, only contains one thread): pool.setKeepAliveTime(1000 * 60 * 5); pool.createThreads(1);

Now this doesn't quite give us our ideal behavior, but it works well enough. We will NEVER have more than three threads in this thread pool: one will be running, one will be blocked, and another will be queued to run. You can throw as many other threads as you want at the PooledExecutor object, but each new thread will simply cause the last blocked thread to be cycled out and discarded, and then the new thread itself will be blocked.

Once no new threads cycle out the blocked thread (after the cursor stops ), then, when the running thread completes, and after the queued thread runs, the blocked thread will itself run. Since it's the last thread, the GUI correctly displays the data for the item in the JList that the selector finally stopped on.

SUMMARY

Clients that issue remote calls for data over the Internet can flood a remote server with requests. It's important for the programmer to write his application so that widgets are smart enough to distinguish traversal of a list from an actual request for data. PooledExecutor in the util.concurrent library enables the application to control the number of remote data requests made in an intelligent manner.
10:40:50 AM    comment []


Friday, March 07, 2003

 


9:47:40 AM    comment []

Sunday, February 16, 2003

Patterns in Java

A quick discussion of the 32 patterns in the Applied Java Patterns book...also discussion of session facade pattern for J2EE..

lots of stuff to write about!!!
1:37:01 PM    comment []


Saturday, February 15, 2003

Add stuff

personal picture, add career blurb, add code section...
5:55:15 PM    comment []


Optional Methods in Java

The Iterator example in the Java Cookbook, the remove() method throws a RuntimeException (which doesn't have to be caught) indicating that the method was not implemented...nice...
5:54:07 PM    comment []


Thursday, February 13, 2003

Doug Lea's concurrent.util package

list each of the packages and their uses

talk about my use of threadpool in the GUI app and its relevance to SOAP and XMLRPC applications
2:59:03 AM    comment []


Ron Hitchens is the God of NIO!

his section on buffers is great...essentially, byte arrays with four characteristics

  • capacity
  • limit
  • position
  • mark

    and excellent first chapter on disks, disk sectors, paging in and out of kernel memory and user memory...very nicely done
    1:36:27 AM    comment []


  • Rusty Elliotte Harold is the God of Java I/O

    His excellent first chapter, where he discusses the significance of bytes in InputStream and Output Stream -- how stream read() returns ints but they're ints in the range of 0-255, which is why you can tell an EOF -1 when it appears. Also, difficulties handling bytes, -- and all integer literals in Java are ints ,even when they're in byte range...talk about some of this...

    3 FACTS THAT PERTAIN:

    1. The byte data type is signed

    2. Java always promotes bytes to ints before working on them individually.

    3. Many of the methods in the stream classes return or accept as arguments ints in the range of an unsigned byte (0-255).

    that means there's a formula you have to apply...
    1:35:03 AM    comment []


    Using an Array for FIFO processing with RemoveRange

    describe useage in the VOIP program...
    1:32:23 AM    comment []


    The Right Way to process arg lists

    from the GREP program

    and BTW, I just learned that GREP stands for G ... Regular Expression Print
    1:31:50 AM    comment []


    JDBC class.forName() is obsolete!

    according to the xxx book on BEA Weblogic and Java...
    1:30:14 AM    comment []


    Sunday, February 09, 2003

    VoIP Regular Expressions Patterns!

    Here are some reaaaaly cooool patterns I wrote using the new J2SE 1.4.1 Regular Expressions package.

    VOIP RECORD TYPES

    private Pattern voipCallHistoryPattern = Pattern.compile("%VOIPAAA-5-VOIP_CALL_HISTORY:");

    private Pattern callControlMaxConnectionsRecordPattern = Pattern.compile("%CALL_CONTROL-6-MAX_CONNECTIONS:");

    private Pattern isdnDisconnectRecordPattern = Pattern.compile("%ISDN-6-DISCONNECT:");

    private Pattern isdnConnectRecordPattern = Pattern.compile("%ISDN-6-CONNECT:");

    private Pattern voipCallLeg1LandlineHalfPattern = Pattern.compile("CallLegType 1");

    private Pattern voipCallLeg2VoipHalfPattern = Pattern.compile("CallLegType 2");

    private Pattern voipConnectionIDPattern = Pattern.compile("ConnectionId.*,\\sSetupTime");

    VOIP RECORD TIMESTAMPS

    private Pattern timestampLoggingMachineReceivedPattern = Pattern.compile("^\\w{3}\\s+\\d+");

    sample: BOF + Feb 4 22:30:02

    private Pattern timestampRouterSentPattern = Pattern.compile("\\w{3}\\s+\\d+\\s\\d{2}:\\d{2}:\\d{2}\\S\\d{3}.*VOIPAAA"); sample: Feb 5 03:58:48.799: %VOIPAAA

    private Pattern timestampCallSetupPattern = Pattern.compile("SetupTime.*\\w{3}\\s+\\d+\\s\\d{4},\\sPeer");

    sample: SetupTime 03:59:09.479 UTC Wed Feb 5 2003 this is the time the call hit the gateway for processing

    private Pattern timestampCallConnectPattern = Pattern.compile("ConnectTime.*\\w{3}\\s+\\d+\\s\\d{4},\\sDis");

    sample: ConnectTime 03:59:09.479 UTC Wed Feb 5 2003

    private Pattern timestampCallDisconnectPattern = Pattern.compile("DisconnectTime.*\\w{3}\\s+\\d+\\s\\d{4},\\sCall");
    4:51:14 PM    comment []


    Saturday, February 08, 2003

    VoIP Billing: Handling that BIG Cisco file

    Working on an interesting problem. The file I have to process is 16 gig. Each record represents half of a phone call; the problem is, the other record can be before OR after its matching record in the file, within a few records (fewer than 10). Now, I can't a) read the file into memory and deal with the records inmem, I can't b) make a sort of the file (because this routine has to be run often and the file could be even bigger, so resources are an unknown), I can't c) make a second file with only type 2 records for lookup, also for resource reasons. What to do.

    worked on a subset of 20000 records to start out with.

    My solution depends upon knowing that, while the records may be out of sequence, they are dependably within a few records of each other. I keep a cache of records I haven't found matches for yet. When I read a new record, I either find a match for it in the cache, or I save it in the cache. This is the driving algorithm:

    if (isVoipCallHistoryRecord(str)) {

       if (isVoipCallLeg1LandlineHalfRecord(str)) {
         callleg1++;
         if (isCallLeg2VoipHalfFoundAlready(str))
           processCall12();
         else
           saveCallLeg1LandlineHalf(str);

    } else {
       if (isVoipCallLeg2VoipHalfRecord(str)) {
         callleg2++;
         if (isCallLeg1LandlineHalfFoundAlready(str))
           processCall21();
         else
           saveCallLeg2VoipHalf(str);
       }
       }
       }

    else {
       otherrecords++;
    }


    If I try to remove the record from cache after I've found it, things slow down too much. If I try to make the cache so big that I never have to remove anything, it's STILL too slow because that huge array has to be searched to find any record . (started arraylist with an initial capacity).

    the removerange(int i, int i) method is what's required here. I create an arraylist of capacity 100 records, then, when the 100th record is added, I remove records 0-90. Thus, I don't remove too often, and I don't remove the last few records, which might still contain type1 records which haven't been found!

    The trick here is to subclass arraylist. I need the removerange method, but it's a protected method! Therefore, it's only available to classes that extend arraylist, not to arraylist itself. A curious design decision...

    These are rough notes for the article I want to write in a bit.
    12:39:23 PM    comment []


    © Copyright 2003 Michael Isbell.
     
    April 2003
    Sun Mon Tue Wed Thu Fri Sat
        1 2 3 4 5
    6 7 8 9 10 11 12
    13 14 15 16 17 18 19
    20 21 22 23 24 25 26
    27 28 29 30      
    Mar   May

    Home

    Click here to visit the Radio UserLand website.

    Subscribe to "Java Morning/C# Afternoon" in Radio UserLand.

    Click to see the XML version of this web page.

    Click here to send an email to the editor of this weblog.