diff -ruN jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/Acceptor.java jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/Acceptor.java
--- jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/Acceptor.java	1970-01-01 01:00:00.000000000 +0100
+++ jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/Acceptor.java	2005-11-17 00:36:59.000000000 +0100
@@ -0,0 +1,132 @@
+/* (C) Thomas Rohwer, distributable under LGPL license. */
+
+
+package org.jboss.invocation.pooled.server;
+
+public abstract class Acceptor implements Runnable {
+	protected volatile boolean shutdown= false;
+
+	protected Object pool= new Object(); // sync
+	protected int evictNum;
+
+	protected Thread[] acceptThreads;
+
+	public void run() {
+		while (!shutdown) {
+			Object socket= null;
+			socket= accept();
+			if (socket==null) continue;
+
+			synchronized(pool) {
+				while (!shutdown) {
+					Worker worker= idleRemove();
+					if (worker!=null) {
+						prepareWorker(worker, socket);
+						worker.newwork();
+						runningAdd(worker);
+						break;
+					}
+					if (createNewWorker()) {
+						worker= createWorker(socket);
+						runningAdd(worker);
+						break;
+					}
+					
+					worker= runningRemove();
+					// System.out.println("evict:"+worker);
+					if (worker!=null) {
+						evictNum++;
+						worker.evict();
+					}
+					try{
+						pool.wait();
+					} catch(InterruptedException e) {
+					}
+				}
+			}
+		}
+	}
+	
+	
+	public void returnIdle(Worker worker) {
+		synchronized(pool) {
+			if (!runningRemove(worker)) {
+				evictNum--;
+			}
+			if (shutdown) {
+				worker.shutdown();
+			} else {
+				idleAdd(worker);
+				worker.waiting();
+			}
+			pool.notify();
+		}
+	}
+	
+	public void start(Thread[] threads) {
+		acceptThreads= threads;
+		for (int i= 0; i<threads.length; i++) {
+			threads[i].start();
+		}
+	}
+	
+	public void shutdown() {
+		shutdown= true;
+		doShutdown();
+		synchronized(pool) {
+			// stop acceptor threads
+			pool.notifyAll();
+			// stop all server threads
+			while (true) {
+				Worker worker= runningRemove();
+				if (worker==null) break;
+				worker.shutdown();
+				worker.evict();
+				evictNum++;
+			}
+
+			while (true) {
+				Worker worker= idleRemove();
+				if (worker==null) break;
+				worker.shutdown();
+				worker.newwork();
+			}
+		}
+	}
+	
+	public void waitShutdown() {
+		synchronized(pool) {
+			while (evictNum>0) {
+				try{
+					pool.wait();
+				} catch(InterruptedException e) {
+				}
+			}
+		}
+	}
+	
+	// abstract methods
+	
+	// returns null, if no idle workers exist
+	protected abstract Worker idleRemove();
+
+	protected abstract void idleAdd(Worker worker);
+
+	protected abstract void runningAdd(Worker worker);
+
+	// returns true, if worker was contained in running
+	protected abstract boolean runningRemove(Worker worker);
+
+	protected abstract Worker runningRemove();
+
+	protected abstract boolean createNewWorker();
+	
+	
+	protected abstract Object accept();
+	
+	protected abstract void doShutdown();
+
+	protected abstract void prepareWorker(Worker worker, Object datum);
+
+	protected abstract Worker createWorker(Object datum);
+}
diff -ruN jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/LRUPool.java jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/LRUPool.java
--- jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/LRUPool.java	2005-07-14 02:25:30.000000000 +0200
+++ jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/LRUPool.java	2005-11-17 00:37:09.000000000 +0100
@@ -1,39 +1,60 @@
-/*
- * JBoss, Home of Professional Open Source
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-
 package org.jboss.invocation.pooled.server;
 
+import java.util.*;
+
+
+public class LRUPool {
+	protected Map index= new HashMap();
+	protected Entry first= null;
+	protected Entry last= null;
+	
+	public LRUPool() {
+		// at the end we keep an empty entry to simplify logic
+		first= new Entry();
+		last= first;
+	}
+
+	public void add(Object o) {
+		if (index.containsKey(o)) return; 
+		index.put(o, last);
+		last.data= o;
+		last.next= new Entry();
+		last.next.prev= last;
+		last= last.next;
+	}
+	
+	public boolean remove(Object o) {
+		Entry entry= (Entry)index.remove(o);
+		if (entry==null) return false;
+		removeEntry(entry);
+		return true;
+	}	
+	
+	public Object removeFirst() {
+		if (first==last) return null;
+		Entry entry= first;
+		index.remove(entry.data);
+		removeEntry(entry);
+		return entry.data;
+	}
+	
+	public int size() {
+		return index.size();
+	}
 
-import org.jboss.util.LRUCachePolicy;
+	protected void removeEntry(Entry entry) {
+		if (entry.prev!=null) {
+			entry.prev.next= entry.next;
+		} else {
+			first= first.next;
+		}
+		// entry cannot be last
+		entry.next.prev= entry.prev;
+	}
 
-/**
- * This class is an extention of LRUCachePolicy.  On a entry removal
- * it makes sure to call shutdown on the pooled ServerThread
- *
- * @author    <a href="mailto:bill@jboss.org">Bill Burke</a>
- * @version $Revision: 1.3.6.1 $
- */
-public class LRUPool extends LRUCachePolicy
-{
-   public LRUPool(int min, int max)
-   {
-      super(min, max);
-   }
-   protected void entryRemoved(LRUCachePolicy.LRUCacheEntry entry) 
-   {
-      ServerThread thread = (ServerThread)entry.m_object;
-      thread.evict();
-   }
-
-   public void evict()
-   {
-      // the entry will be removed by ageOut
-      ServerThread thread = (ServerThread)m_list.m_tail.m_object;
-      thread.evict();
-   }
-   
+	protected static class Entry {
+		public Entry next;
+		public Entry prev;
+		public Object data;
+	}
 }
diff -ruN jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/PooledInvoker.java jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/PooledInvoker.java
--- jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/PooledInvoker.java	2005-07-14 03:03:06.000000000 +0200
+++ jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/PooledInvoker.java	2005-11-17 00:36:52.000000000 +0100
@@ -5,8 +5,12 @@
  * See terms of license at gnu.org.
  */
 
+/* original authors: Bill Burke, Scott Stark. This file has been adapted by Thomas Rohwer. */
+
+
 package org.jboss.invocation.pooled.server;
 
+import java.io.IOException;
 import java.net.InetAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
@@ -61,7 +65,7 @@
  * @jmx:mbean extends="org.jboss.system.ServiceMBean"
  */
 public class PooledInvoker extends ServiceMBeanSupport
-   implements PooledInvokerMBean, Runnable
+   implements PooledInvokerMBean
 {
 
    /**
@@ -126,9 +130,115 @@
    protected int numAcceptThreads = 1;
    protected Thread[] acceptThreads;
 
-   protected LRUPool clientpool;
-   protected LinkedList threadpool;
-   protected boolean running = true;
+	protected AcceptorImpl acceptor;
+
+	protected class AcceptorImpl extends Acceptor {
+		// protected LinkedList running= new LinkedList();
+		protected LRUPool running= new LRUPool();
+		protected LinkedList idle= new LinkedList();
+	
+		// returns null, if no idle workers exist
+		protected Worker idleRemove() {
+			if (idle.isEmpty()) return null;
+			return (Worker)idle.removeFirst();
+		}
+		protected void idleAdd(Worker worker) {
+			idle.add(worker);
+		}
+
+		protected void runningAdd(Worker worker) {
+			running.add(worker);
+		}
+		// returns true, if worker was contained in running
+		protected boolean runningRemove(Worker worker) {
+			return running.remove(worker);
+		}
+		protected Worker runningRemove() {
+			return (Worker)running.removeFirst();	
+		}
+		protected boolean createNewWorker() {
+			return running.size()+evictNum<maxPoolSize;
+		}
+		
+		/* debug
+		public void returnIdle(Worker worker) {
+			synchronized(pool) {
+				System.out.println("return idle1:");
+				System.out.println("evictNum:"+evictNum);
+				System.out.println("running:"+running.size());
+				System.out.println("idle:"+idle.size());
+				super.returnIdle(worker);
+				System.out.println("return idle2:");
+				System.out.println("evictNum:"+evictNum);
+				System.out.println("running:"+running.size());
+				System.out.println("idle:"+idle.size());
+			}
+		}
+		*/
+		
+		public void run() {
+			// while (!shutdown) {
+				try{
+					super.run();
+				} catch(Throwable e) {
+					log.error("Error in accept", e);
+				}
+			// }
+		}		
+	
+		protected Object accept() {
+			Socket socket= null;
+			try{
+				socket= serverSocket.accept();
+                if (trace) log.trace("Accepted: " + socket);
+			} catch(IOException e) {
+				
+			}
+			return socket;
+		}
+	
+		protected void doShutdown() {
+			/* debug
+			synchronized(pool) {
+				System.out.println("evictNum:"+evictNum);
+				System.out.println("running:"+running.size());
+				System.out.println("idle:"+idle.size());
+			}
+			*/
+			try{
+				serverSocket.close();
+			} catch(Exception e) {
+			}
+		}
+	
+		protected void prepareWorker(Worker worker, Object datum) {
+			Socket socket= (Socket)datum;
+			((ServerThread)worker).setSocket(socket, timeout);
+		}
+		protected Worker createWorker(Object datum) {
+			Socket socket= (Socket)datum;
+			ServerThread worker= new ServerThread(PooledInvoker.this);
+			worker.setSocket(socket, timeout);
+			worker.start();
+			return worker;
+		}
+		
+		protected int getRunningNum() {
+			synchronized(pool) {
+				return running.size();
+			}
+		}
+		protected int getIdleNum() {
+			synchronized(pool) {
+				return idle.size();
+			}
+		}
+	}
+	
+	public Acceptor getAcceptor() {
+		return acceptor;
+	}
+
    /** The logging trace level flag */
    protected boolean trace = false;
    /**
@@ -197,9 +307,6 @@
       */
       clientConnectAddress = ServerConfigUtil.fixRemoteAddress(clientConnectAddress);
 
-      clientpool = new LRUPool(2, maxPoolSize);
-      clientpool.create();
-      threadpool = new LinkedList();
        try
        {
            serverSocket = new ServerSocket(serverBindPort, backlog, bindAddress);
@@ -222,81 +329,13 @@
       log.debug("Bound invoker for JMX node");
       ctx.close();
 
-      acceptThreads = new Thread[numAcceptThreads];
-      for (int i = 0; i < numAcceptThreads; i++)
-      {
-         String name = "PooledInvokerAcceptor#"+i+"-"+serverBindPort;
-         acceptThreads[i] = new Thread(this, name);
-         acceptThreads[i].start();
-      }
-   }
-
-   public void run()
-   {
-      while (running)
-      {
-         try
-         {
-            Socket socket = serverSocket.accept();
-            if( trace )
-               log.trace("Accepted: "+socket);
-            ServerThread thread = null;
-            boolean newThread = false;
-            
-            while (thread == null)
-            {
-               synchronized(threadpool)
-               {
-                  if (threadpool.size() > 0)
-                  {
-                     thread = (ServerThread)threadpool.removeFirst();
-                  }
-               }
-               if (thread == null)
-               {
-                  synchronized(clientpool)
-                  {
-                     if (clientpool.size() < maxPoolSize) 
-                     {
-                        thread = new ServerThread(socket, this, clientpool, threadpool, timeout);
-                        newThread = true;
-                     }
-                     if (thread == null)
-                     {
-                        clientpool.evict();
-                        if( trace )
-                           log.trace("Waiting for a thread...");
-                        clientpool.wait();
-                        if( trace )
-                           log.trace("Notified of available thread");
-                     }
-                  }
-               }
-            }
-            synchronized(clientpool)
-            {
-               clientpool.insert(thread, thread);
-            }
-            
-            if (newThread)
-            {
-               if( trace )
-                  log.trace("Created a new thread, t="+thread);
-               thread.start();
-            }
-            else
-            {
-               if( trace )
-                  log.trace("Reusing thread t="+thread);
-               thread.wakeup(socket, timeout);
-            }
-         }
-         catch (Throwable ex)
-         {
-            if (running)
-               log.error("Failed to accept socket connection", ex);
-         }
+      acceptor= new AcceptorImpl();
+	  Thread[] acceptThreads = new Thread[numAcceptThreads];
+	  for (int i = 0; i < numAcceptThreads; i++) {
+	     String name = "PooledInvokerAcceptor#" + i + "-" + serverBindPort;
+         acceptThreads[i] = new Thread(acceptor, name);
       }
+	  acceptor.start(acceptThreads);
    }
 
    /**
@@ -304,30 +343,9 @@
     */
    public void stopService() throws Exception
    {
-      running = false;
-      maxPoolSize = 0; // so ServerThreads don't reinsert themselves
-      for (int i = 0; i < acceptThreads.length; i++)
-      {
-         try
-         {
-            acceptThreads[i].interrupt();
-         }
-         catch (Exception ignored){}
-      }
-      clientpool.flush();
-      for (int i = 0; i < threadpool.size(); i++)
-      {
-         ServerThread thread = (ServerThread)threadpool.removeFirst();
-         thread.shutdown();
-      }
-
-      try
-      {
-         serverSocket.close();
-      }
-      catch(Exception e)
-      {         
-      }
+      acceptor.shutdown();
+      // don't wait until threads terminate
+      // acceptor.waitShutdown();
    }
 
    protected void destroyService() throws Exception
@@ -475,7 +493,11 @@
     */
    public int getCurrentClientPoolSize()
    {
-      return clientpool.size();
+		if (acceptor!=null) {
+			return acceptor.getRunningNum();
+		} else {
+			return 0;
+		}
    }
 
    /**
@@ -485,7 +507,11 @@
     */
    public int getCurrentThreadPoolSize()
    {
-      return threadpool.size();
+		if (acceptor!=null) {
+			return acceptor.getIdleNum();
+		} else {
+			return 0;
+		}
    }
 
    /**
@@ -883,4 +909,3 @@
       }
    }
 }
-// vim:expandtab:tabstop=3:shiftwidth=3
diff -ruN jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/PooledInvokerMBean.java jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/PooledInvokerMBean.java
--- jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/PooledInvokerMBean.java	2005-07-14 03:03:06.000000000 +0200
+++ jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/PooledInvokerMBean.java	2005-11-17 00:36:56.000000000 +0100
@@ -4,6 +4,7 @@
  * Distributable under LGPL license.
  * See terms of license at gnu.org.
  */
+
 package org.jboss.invocation.pooled.server;
 
 import javax.management.ObjectName;
diff -ruN jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/ServerThread.java jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/ServerThread.java
--- jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/ServerThread.java	2005-07-14 02:25:14.000000000 +0200
+++ jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/ServerThread.java	2005-11-17 00:37:30.000000000 +0100
@@ -1,18 +1,21 @@
-/*
- * JBoss, Home of Professional Open Source
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
+/*******************************************************************************
+ * * JBoss: The OpenSource J2EE WebOS * * Distributable under LGPL license. *
+ * See terms of license at gnu.org. * *
+ ******************************************************************************/
+
+/* original author: Bill Burke. This file has been adapted by Thomas Rohwer. */
+
 
 package org.jboss.invocation.pooled.server;
 
+import java.io.IOException;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.InterruptedIOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.net.Socket;
+import java.net.SocketException;
 import java.util.LinkedList;
 
 import org.jboss.invocation.Invocation;
@@ -21,289 +24,185 @@
 import org.jboss.logging.Logger;
 
 /**
- * This Thread object hold a single Socket connection to a client
- * and is kept alive until a timeout happens, or it is aged out of the
- * PooledInvoker's LRU cache.
- *
- * There is also a separate thread pool that is used if the client disconnects.
- * This thread/object is re-used in that scenario and that scenario only.
- *
- * This class will demarshal then delegate to PooledInvoker for invocation.
- *
- *
- * *NOTES* ObjectStreams were found to be better performing than the Custom marshalling
- * done by the TrunkInvoker.
- *
- * @author    <a href="mailto:bill@jboss.org">Bill Burke</a>
- * @version $Revision: 1.6.6.2 $
+ * This Thread object hold a single Socket connection to a client and is kept
+ * alive until it is removed to accept a new connection.
+ * This class will demarshal then delegate to PooledInvoker for
+ * invocation. *NOTES* ObjectStreams were found to be better performing than the
+ * Custom marshalling done by the TrunkInvoker.
+ * 
+ * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
+ * @version $Revision$
  */
-public class ServerThread extends Thread
-{
-   final static private Logger log = Logger.getLogger(ServerThread.class);
-
-   protected ObjectInputStream in;
-   protected ObjectOutputStream out;
-   protected Socket socket;
-   protected PooledInvoker invoker;
-   protected LRUPool clientpool;
-   protected LinkedList threadpool;
-   protected volatile boolean running = true;
-   protected volatile boolean handlingResponse = true; // start off as true so that nobody can interrupt us
-   protected volatile boolean shutdown = false;
-   protected static int id = 0;
-
-   public static synchronized int nextID()
-   {
-      int nextID = id ++;
-      return nextID;
-   }
-
-   public ServerThread(Socket socket, PooledInvoker invoker, LRUPool clientpool,
-      LinkedList threadpool, int timeout) throws Exception
-   {
-      super("PooledInvokerThread-" + socket.getInetAddress().getHostAddress()+"-"+nextID());
-      this.socket = socket;
-      this.invoker = invoker;
-      this.clientpool = clientpool;
-      this.threadpool = threadpool;
-      socket.setSoTimeout(timeout);
-   }
-
-   public void shutdown()
-   {
-      shutdown = true;
-      running = false;
-      // This is a race and there is a chance
-      // that a invocation is going on at the time
-      // of the interrupt.  But I see no way right
-      // now to protect for this.
-
-      // NOTE ALSO!:
-      // Shutdown should never be synchronized.
-      // We don't want to hold up accept() thread! (via LRUpool)
-      if (!handlingResponse)
-      {
-         try
-         {
-            this.interrupt();
-            Thread.interrupted(); // clear
-         }
-         catch (Exception ignored) {}
-      }
-      
-   }
-
-   public void evict()
-   {
-      running = false;
-      // This is a race and there is a chance
-      // that a invocation is going on at the time
-      // of the interrupt.  But I see no way right
-      // now to protect for this.
-      // There may not be a problem because interrupt only effects
-      // threads blocking on IO.
-      
-
-      // NOTE ALSO!:
-      // Shutdown should never be synchronized.
-      // We don't want to hold up accept() thread! (via LRUpool)
-      if (!handlingResponse)
-      {
-         try
-         {
-            this.interrupt();
-            Thread.interrupted(); // clear
-         }
-         catch (Exception ignored) {}
-      }
-   }
-
-
-   public synchronized void wakeup(Socket socket, int timeout) throws Exception
-   {
-      this.socket = socket;
-      String name = "PooledInvokerThread-" + socket.getInetAddress().getHostAddress()+"-"+nextID();
-      super.setName(name);
-      socket.setSoTimeout(timeout);
-      running = true;
-      handlingResponse = true;
-      this.notify();
-   }
-
-   public void run()
-   {
-      try
-      {
-         while (true)
-         {
-            dorun();
-            //System.out.println("finished....");
-            if (shutdown)
-            {
-               //System.out.println("doing shutdown");
-               synchronized (clientpool)
-               {
-                  clientpool.remove(this);
-               }
-               return; // exit thread
-            }
-            else
-            {
-               //System.out.println("save thread");
-               synchronized (this)
-               {
-                  //System.out.println("synch on client pool");
-                  synchronized(clientpool)
-                  {
-                     //System.out.println("synch on thread pool");
-                     synchronized(threadpool)
-                     {
-                        //System.out.println("removing myself from the pool: " + clientpool.size());
-                        clientpool.remove(this);
-                        //System.out.println("adding myself to threadpool");
-                        threadpool.add(this);
-                        Thread.interrupted(); // clear any interruption so that we can be pooled.
-                        clientpool.notify();
-                     }
-                  }
-                  log.debug("begin thread wait");
-                  this.wait();
-                  log.debug("WAKEUP in SERVER THREAD");
-               }
-            }
-         }
-      }
-      catch (Exception ignored) 
-      {
-         log.debug("Exiting run on exception", ignored);
-      }
-   }
-
-   protected void acknowledge() throws Exception
-   {
-      //System.out.println("****acknowledge " + Thread.currentThread());
-      // Perform acknowledgement to convince client
-      // that the socket is still active
-      byte ACK = in.readByte();
-      //System.out.println("****acknowledge read byte" + Thread.currentThread());
-
-      // HERE IS THE RACE between ACK received and handlingResponse = true
-      // We can't synchronize because readByte blocks and client is expecting
-      // a response and we don't want to hang client.
-      // see shutdown and evict for more details
-      // There may not be a problem because interrupt only effects
-      // threads blocking on IO. and this thread will just continue.
-      handlingResponse = true;
-      
-      out.writeByte(ACK);
-      out.flush();
-   }
-
-   protected void processInvocation() throws Exception
-   {
-      handlingResponse = true;
-      // Ok, now read invocation and invoke
-      Invocation invocation = (Invocation)in.readObject();
-      in.readObject(); // for stupid ObjectInputStream reset
-      Object response = null;
-      try
-      {
-          // Make absolutely sure thread interrupted is cleared.
-         boolean interrupted = Thread.interrupted();
-         response = invoker.invoke(invocation);
-      }
-      catch (Exception ex)
-      {
-         response = ex;
-      }
-      Thread.interrupted(); // clear interrupted state so we don't fail on socket writes
-      out.writeObject(response);
-      out.reset();
-      // to make sure stream gets reset
-      // Stupid ObjectInputStream holds object graph
-      // can only be set by the client/server sending a TC_RESET
-      out.writeObject(Boolean.TRUE); 
-      out.flush();
-      out.reset();
-      handlingResponse = false;
-   }
-
-   /**
-    * This is needed because Object*Streams leak
-    */
-   protected void dorun()
-   {
-      log.debug("beginning dorun");
-      running = true;
-      handlingResponse = true;
-      try
-      {
-         BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
-         out = new OptimizedObjectOutputStream(bos);
-         out.flush();
-         BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
-         in = new OptimizedObjectInputStream(bis);
-      }
-      catch (Exception e)
-      {
-         log.error("Failed to initialize", e);
-      }
-
-      // Always do first one without an ACK because its not needed
-      try
-      {
-         processInvocation();
-      }
-      catch (Exception ex)
-      {
-         running = false;
-      }
-
-      // Re-use loop
-      while (running)
-      {
-         try
-         {
-            acknowledge();
-            processInvocation();
-         }
-         catch (InterruptedIOException e)
-         {
-            log.debug("socket timed out", e);
-            running = false;
-         }
-         catch (InterruptedException e)
-         {
-            log.debug("interrupted", e);
-         }
-         catch (Exception ex)
-         {
-            log.debug("failed", ex);
-            running = false;
-         }
-         // clear any interruption so that thread can be pooled.
-         Thread.interrupted();
-      }
-      //System.out.println("finished loop:" + Thread.currentThread());
-      // Ok, we've been shutdown.  Do appropriate cleanups.
-      try
-      {
-         if (in != null) in.close();
-         if (out != null) out.close();
-      }
-      catch (Exception ex)
-      {
-      }
-      try
-      {
-         socket.close();
-      }
-      catch (Exception ex)
-      {
-         log.error("Failed cleanup", ex);
-      }
-      socket = null;
-      in = null;
-      out = null;
-   }
+
+public class ServerThread extends Worker {
+    final static private Logger log = Logger.getLogger(ServerThread.class);
+
+    protected ObjectInputStream in;
+    protected ObjectOutputStream out;
+    protected Socket socket;
+    protected PooledInvoker invoker;
+	protected Thread thread;
+	protected final Object streamLock= new Object();
+
+    protected static int id = 0;
+
+    public static synchronized int nextID() {
+        int nextID = id++;
+        return nextID;
+    }
+
+    public ServerThread(PooledInvoker invoker) {
+		super(invoker.getAcceptor());
+        this.invoker = invoker;
+		thread= new Thread(this);
+    }
+
+
+    public void run() {
+        try {
+			super.run();
+        } catch (Exception ignored) {
+            log.debug("Exiting run on exception", ignored);
+        }
+    }
+
+	public void start() {
+		thread.start();	
+	}
+
+
+	protected void prepareWork() {
+		try{
+			openStreams();
+			doCall();
+		} catch(Throwable e) {
+			terminate();
+			log.error("error during prepareWork", e);
+		}
+	}
+	
+	protected void checkWork() {
+		try{
+			readCheck();
+		} catch(Throwable e) {
+			terminate();
+			log.error("error during checkWork", e);
+		}
+	}
+	
+	protected void doWork() {
+		try{
+			writeCheck();
+			doCall();
+		} catch(Throwable e) {
+			terminate();
+			log.error("error during doWork", e);
+		}
+	}
+
+	// specific methods
+		
+    public void setSocket(Socket socket, int timeout) {
+        this.socket = socket;
+        String name = "PooledInvokerThread-" + socket.getInetAddress().getHostAddress() + "-" + nextID();
+        thread.setName(name);
+		try{
+	        socket.setSoTimeout(timeout);
+		} catch(SocketException e) {
+            log.debug("Failed to set socket timeout", e);
+		}
+    }
+
+	protected void openStreams() {
+		try {
+			BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
+			out = new OptimizedObjectOutputStream(bos);
+			out.flush();
+			BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
+			in = new OptimizedObjectInputStream(bis);
+        } catch (Exception e) {
+            log.error("Failed to initialize", e);
+			terminate();
+        }
+	}
+	
+	protected void doCall() {
+		try{
+			// Ok, now read invocation and invoke
+			Invocation invocation = (Invocation) in.readObject();
+			in.readObject(); // for stupid ObjectInputStream reset
+			Object response = null;
+			try {
+				response = invoker.invoke(invocation);
+			} catch (Exception ex) {
+				response = ex;
+			}
+			Thread.interrupted(); // clear interrupted flag that might have been set during the invoked call
+
+			out.writeObject(response);
+			out.reset();
+			// to make sure stream gets reset
+			// Stupid ObjectInputStream holds object graph
+			// can only be set by the client/server sending a TC_RESET
+			out.writeObject(Boolean.TRUE);
+			out.flush();
+			out.reset();
+		} catch(Exception e) {
+			terminate();
+		}
+	}
+
+	protected byte ACK;
+	protected void readCheck() {
+		// Perform acknowledgement to convince client
+		// that the socket is still active
+		try{
+			// System.out.println("wait1 "+socket);
+        	ACK= in.readByte();
+		} catch(IOException e) {
+			terminate();
+		}
+		// System.out.println("wait2 "+socket);
+	}
+
+	protected void writeCheck() {
+		try{
+			out.writeByte(ACK);
+			out.flush();
+		} catch(IOException e) {
+			terminate();
+		}
+	}
+
+	protected void closeSocket() {
+		try{
+			socket.close();
+		} catch(IOException e) {
+		}
+	}
+
+    protected void finishWork() {
+		try {
+			if (in!=null) in.close();
+			if (out!=null) out.close();
+		} catch (Exception ex) {
+		}
+		try {
+			socket.close();
+		} catch (Exception ex) {
+			log.error("Failed cleanup", ex);
+		}
+		socket = null;
+		in = null;
+		out = null;
+	}
+
+	// ensured here, that is can only be called while socket is not null
+	protected void specialInterrupt() {
+		// System.out.println("interrupt "+socket);
+		try {
+			socket.close();
+		} catch (Exception ex) {
+		}
+	}
 }
diff -ruN jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/Worker.java jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/Worker.java
--- jboss-4.0.3-src/server/src/main/org/jboss/invocation/pooled/server/Worker.java	1970-01-01 01:00:00.000000000 +0100
+++ jboss-4.0.3-patched/server/src/main/org/jboss/invocation/pooled/server/Worker.java	2005-11-17 00:37:20.000000000 +0100
@@ -0,0 +1,85 @@
+/* (C) Thomas Rohwer, distributable under LGPL license. */
+
+
+package org.jboss.invocation.pooled.server;
+
+public abstract class Worker implements Runnable {
+	protected volatile boolean running= true;
+	protected volatile boolean waiting= false;
+	protected volatile boolean maybreak= false;
+	protected volatile boolean shutdown= false;
+	
+	protected Acceptor acceptor;
+	
+	public Worker(Acceptor acceptor) {
+		this.acceptor= acceptor;
+	}
+
+	// only call this while worker is listed as running
+	public void evict() {
+		running= false;
+		if (maybreak) specialInterrupt();
+	}
+	
+	public void shutdown() {
+		shutdown= true;
+	}
+	
+	public void waiting() {
+		waiting= true;
+		running= true;
+	}
+	
+	public void newwork() {
+		waiting= false;
+		synchronized(this) {
+			this.notify();
+		}
+	}
+
+	public void run() {
+		do {
+			prepareWork();
+			while (running) {
+				maybreak= true;
+				checkWork();
+				maybreak= false;
+				if (!running) break;
+				doWork();
+			}
+			finishWork();
+			acceptor.returnIdle(this);
+
+			// wait for new work (or shutdown)
+			synchronized(this) {
+				while (waiting) {
+					try{
+						this.wait();
+					} catch(InterruptedException e) {
+					}
+				}
+			}
+		} while (!shutdown);
+		acceptor.returnIdle(this); // to correctly decrease evictNum
+	}
+
+
+	// callback for abstract methods
+
+	protected void terminate() {
+		running= false;
+	}
+
+
+	// abstract methods
+
+	protected abstract void specialInterrupt();
+
+	protected abstract void prepareWork();
+	
+	protected abstract void finishWork();
+	
+	protected abstract void checkWork();
+	
+	protected abstract void doWork();
+}
