[Swift-commit] Cog update

swift at ci.uchicago.edu swift at ci.uchicago.edu
Sat Jan 28 20:50:18 CST 2012


------------------------------------------------------------------------
r3355 | hategan | 2012-01-28 20:48:37 -0600 (Sat, 28 Jan 2012) | 1 line

merged 0.93 to trunk
------------------------------------------------------------------------
Index: lib/log4j.LICENSE
===================================================================
--- lib/log4j.LICENSE	(revision 0)
+++ lib/log4j.LICENSE	(revision 3355)
@@ -0,0 +1,49 @@
+/*
+ * ============================================================================
+ *                   The Apache Software License, Version 1.1
+ * ============================================================================
+ * 
+ *    Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ * 
+ * 1. Redistributions of  source code must  retain the above copyright  notice,
+ *    this list of conditions and the following disclaimer.
+ * 
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 
+ * 3. The end-user documentation included with the redistribution, if any, must
+ *    include  the following  acknowledgment:  "This product includes  software
+ *    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
+ *    Alternately, this  acknowledgment may  appear in the software itself,  if
+ *    and wherever such third-party acknowledgments normally appear.
+ * 
+ * 4. The names "log4j" and  "Apache Software Foundation"  must not be used to
+ *    endorse  or promote  products derived  from this  software without  prior
+ *    written permission. For written permission, please contact
+ *    apache at apache.org.
+ * 
+ * 5. Products  derived from this software may not  be called "Apache", nor may
+ *    "Apache" appear  in their name,  without prior written permission  of the
+ *    Apache Software Foundation.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
+ * APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
+ * ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
+ * (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * This software  consists of voluntary contributions made  by many individuals
+ * on  behalf of the Apache Software  Foundation.  For more  information on the 
+ * Apache Software Foundation, please see <http://www.apache.org/>.
+ *
+ */
+
Index: lib/log4j-1.2.8.LICENSE
===================================================================
--- lib/log4j-1.2.8.LICENSE	(revision 3354)
+++ lib/log4j-1.2.8.LICENSE	(working copy)
@@ -1,49 +0,0 @@
-/*
- * ============================================================================
- *                   The Apache Software License, Version 1.1
- * ============================================================================
- * 
- *    Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without modifica-
- * tion, are permitted provided that the following conditions are met:
- * 
- * 1. Redistributions of  source code must  retain the above copyright  notice,
- *    this list of conditions and the following disclaimer.
- * 
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 
- * 3. The end-user documentation included with the redistribution, if any, must
- *    include  the following  acknowledgment:  "This product includes  software
- *    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
- *    Alternately, this  acknowledgment may  appear in the software itself,  if
- *    and wherever such third-party acknowledgments normally appear.
- * 
- * 4. The names "log4j" and  "Apache Software Foundation"  must not be used to
- *    endorse  or promote  products derived  from this  software without  prior
- *    written permission. For written permission, please contact
- *    apache at apache.org.
- * 
- * 5. Products  derived from this software may not  be called "Apache", nor may
- *    "Apache" appear  in their name,  without prior written permission  of the
- *    Apache Software Foundation.
- * 
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
- * APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
- * DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
- * ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
- * (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- * 
- * This software  consists of voluntary contributions made  by many individuals
- * on  behalf of the Apache Software  Foundation.  For more  information on the 
- * Apache Software Foundation, please see <http://www.apache.org/>.
- *
- */
-
Index: lib/backport-util-concurrent.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: lib/log4j-1.2.8.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: modules/provider-localscheduler/.classpath
===================================================================
--- modules/provider-localscheduler/.classpath	(revision 3354)
+++ modules/provider-localscheduler/.classpath	(working copy)
@@ -10,6 +10,7 @@
 		</accessrules>
 	</classpathentry>
 	<classpathentry combineaccessrules="false" kind="src" path="/jglobus"/>
-	<classpathentry kind="lib" path="/util/lib/log4j-1.2.8.jar"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/util"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
 	<classpathentry kind="output" path=".build"/>
 </classpath>
Index: modules/provider-localscheduler/src/org/globus/cog/abstraction/impl/scheduler/pbs/PBSExecutor.java
===================================================================
--- modules/provider-localscheduler/src/org/globus/cog/abstraction/impl/scheduler/pbs/PBSExecutor.java	(revision 3354)
+++ modules/provider-localscheduler/src/org/globus/cog/abstraction/impl/scheduler/pbs/PBSExecutor.java	(working copy)
@@ -31,6 +31,7 @@
 public class PBSExecutor extends AbstractExecutor {
 	public static final Logger logger = Logger.getLogger(PBSExecutor.class);
 
+
 	/**
 	   Number of program invocations
 	 */
@@ -60,7 +61,6 @@
 	/** 
 	    The job name is limited to 15 characters: 
 		http://doesciencegrid.org/public/pbs/qsub.html
-		This limit is enforced on Cray machines
 	 */
 	protected void validate(Task task) {
 		String name = task.getName();
@@ -76,10 +76,7 @@
             }
 		}
 		else if (name.length() > 15) {
-			String shorter = name.substring(0, 15);
-			logger.debug("PBS name: for: " + name + 
-			             " is: " + shorter);
-		    task.setName(shorter);
+		    task.setName(name.substring(0, 15));
 		}
 	}
 	
@@ -327,6 +324,7 @@
 		writer.write("#CoG   on date: " + new Date() + "\n\n");
 	}
 	
+	
 	private String makeList(Collection<String> names) {
         StringBuilder sb = new StringBuilder();
         Iterator<String> i = names.iterator();
Index: modules/provider-localscheduler/src/org/globus/cog/abstraction/impl/scheduler/sge/SGEExecutor.java
===================================================================
--- modules/provider-localscheduler/src/org/globus/cog/abstraction/impl/scheduler/sge/SGEExecutor.java	(revision 3354)
+++ modules/provider-localscheduler/src/org/globus/cog/abstraction/impl/scheduler/sge/SGEExecutor.java	(working copy)
@@ -185,7 +185,8 @@
     /**
      * @see AbstractExecutor#start()
      */
-    public void start() throws AuthorizationException, IOException, ProcessException {
+    public void start() throws AuthorizationException,
+           IOException, ProcessException {
     	try {
     		Thread.sleep(Integer.valueOf(getSGEProperties().getSubmissionDelay()));
     	}
Index: modules/provider-localscheduler/etc/provider-sge.properties
===================================================================
--- modules/provider-localscheduler/etc/provider-sge.properties	(revision 3354)
+++ modules/provider-localscheduler/etc/provider-sge.properties	(working copy)
@@ -13,14 +13,13 @@
 #
 # The path to qstat. The default assumes that qstat is in PATH
 #
-qstat=qstat
+qstat=qstat -xml
 
 #
 # The path to qdel. The default assumes that qdel is in PATH
 #
 qdel=qdel
 
-
 #
 # If the jobType attribute is specified, then the SGE provider
 # will look for a property named "wrapper.<jobType>" and prepend
@@ -36,6 +35,10 @@
 # a default parallel environment. It can be overriden using
 # the "pe" job attribute
 #
-
 parallel.environment=1way
 
+# Some systems (notably Ranger) may not adequately handle
+# a rapid submission of jobs. Use this setting to introduce
+# a delay. The value is in milliseconds (1000ms = 1s)
+#
+submission.delay=1000
Index: modules/provider-local/.classpath
===================================================================
--- modules/provider-local/.classpath	(revision 3354)
+++ modules/provider-local/.classpath	(working copy)
@@ -10,7 +10,6 @@
 		</accessrules>
 	</classpathentry>
 	<classpathentry combineaccessrules="false" kind="src" path="/jglobus"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/backport-util-concurrent"/>
-	<classpathentry kind="lib" path="/util/lib/log4j-1.2.8.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
 	<classpathentry kind="output" path=".build"/>
 </classpath>
Index: modules/provider-local/.project
===================================================================
--- modules/provider-local/.project	(revision 3354)
+++ modules/provider-local/.project	(working copy)
@@ -7,6 +7,11 @@
 		<project>jglobus</project>
 	</projects>
 	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
 	</buildSpec>
 	<natures>
 		<nature>org.eclipse.jdt.core.javanature</nature>
Index: modules/provider-local/src/org/globus/cog/abstraction/impl/file/local/FileResourceImpl.java
===================================================================
--- modules/provider-local/src/org/globus/cog/abstraction/impl/file/local/FileResourceImpl.java	(revision 3354)
+++ modules/provider-local/src/org/globus/cog/abstraction/impl/file/local/FileResourceImpl.java	(working copy)
@@ -230,6 +230,9 @@
             if (dst.getCanonicalPath().equals(src.getCanonicalPath())) {
                 return;
             }
+            
+            checkParameters(remote, local, src, dst);
+            
             FileInputStream remoteStream = null;
             FileOutputStream localStream = null;
             try {
@@ -239,9 +242,16 @@
                 
                 long crt = 0;
                 long total = Math.min(src.length(), remote.getLength());
+                if (logger.isDebugEnabled()) {
+                    logger.debug(src + ": srclen = " + src.length() 
+                        + ", len = " + remote.getLength() + ", total = " + total);
+                }
                 byte[] buf = new byte[16384];
                 do {
-                    int read = remoteStream.read(buf, 0, Math.min(buf.length, (int) (total - crt)));
+                    if (logger.isDebugEnabled()) {
+                        logger.debug(src + ": crt = " + crt + ", total - crt = " + (total - crt));
+                    }
+                    int read = remoteStream.read(buf, 0, (int) Math.min(buf.length, total - crt));
                     localStream.write(buf, 0, read);
                     crt += read;
                     if (progressMonitor != null) {
@@ -263,6 +273,14 @@
         }
     }
 
+    private void checkParameters(FileFragment srcf, FileFragment dstf, File src, File dst) throws FileResourceException {
+        long srcLen = src.length();
+        if (srcf.getOffset() > srcLen) {
+            throw new FileResourceException("Requested file offset (" 
+                + srcf.getOffset() + ") is larger than the file size (" + srcLen + ")");
+        }
+    }
+
     public void putFile(FileFragment local, FileFragment remote,
             ProgressMonitor progressMonitor) throws FileResourceException {
         getFile(local, remote, progressMonitor);
@@ -439,12 +457,10 @@
         return true;
     }
 
-    @Override
     public boolean supportsPartialTransfers() {
         return true;
     }
 
-    @Override
     public boolean supportsThirdPartyTransfers() {
         return false;
     }
Index: modules/provider-local/src/org/globus/cog/abstraction/impl/execution/local/JobSubmissionTaskHandler.java
===================================================================
--- modules/provider-local/src/org/globus/cog/abstraction/impl/execution/local/JobSubmissionTaskHandler.java	(revision 3354)
+++ modules/provider-local/src/org/globus/cog/abstraction/impl/execution/local/JobSubmissionTaskHandler.java	(working copy)
@@ -219,7 +219,7 @@
             int exitCode = p.waitFor();
 
             if (logger.isDebugEnabled()) {
-                logger.debug("Exit code was " + exitCode);
+                logger.debug("Application " + spec.getExecutable() + " failed with an exit code of " + exitCode);
             }
             
             /*
@@ -343,6 +343,7 @@
 
         String srcScheme = defaultToLocal(suri.getScheme());
         String dstScheme = defaultToLocal(duri.getScheme());
+
         Service ss = new ServiceImpl(srcScheme, getServiceContact(suri), null);
         Service ds = new ServiceImpl(dstScheme, getServiceContact(duri), null);
         
@@ -607,7 +608,7 @@
                     int avail = sp.is.available();
                     if (avail > 0) {
                         any = true;
-                        int len = sp.is.read(buf);
+                        int len = sp.is.read(buf, 0, Math.min(avail, BUFFER_SIZE));
                         sp.os.write(buf, 0, len);
                     }
                 }
Index: modules/provider-gt2/.classpath
===================================================================
--- modules/provider-gt2/.classpath	(revision 3354)
+++ modules/provider-gt2/.classpath	(working copy)
@@ -11,7 +11,6 @@
 	</classpathentry>
 	<classpathentry combineaccessrules="false" kind="src" path="/util"/>
 	<classpathentry combineaccessrules="false" exported="true" kind="src" path="/jglobus"/>
-	<classpathentry exported="true" kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j-1.2.8"/>
-	<classpathentry kind="lib" path="/util/lib/log4j-1.2.8.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
 	<classpathentry kind="output" path=".build"/>
 </classpath>
Index: modules/provider-gt2/src/org/globus/cog/abstraction/impl/file/gridftp/old/FileResourceImpl.java
===================================================================
--- modules/provider-gt2/src/org/globus/cog/abstraction/impl/file/gridftp/old/FileResourceImpl.java	(revision 3354)
+++ modules/provider-gt2/src/org/globus/cog/abstraction/impl/file/gridftp/old/FileResourceImpl.java	(working copy)
@@ -755,12 +755,10 @@
         return true;
     }
 
-    @Override
     public boolean supportsPartialTransfers() {
         return true;
     }
 
-    @Override
     public boolean supportsThirdPartyTransfers() {
         return true;
     }
@@ -849,4 +847,4 @@
         }
         return NF.format(dv) + " " + U[index];
     }    
-}
\ No newline at end of file
+}
Index: modules/provider-gt2/src/org/globus/cog/abstraction/impl/file/ftp/FileResourceImpl.java
===================================================================
--- modules/provider-gt2/src/org/globus/cog/abstraction/impl/file/ftp/FileResourceImpl.java	(revision 3354)
+++ modules/provider-gt2/src/org/globus/cog/abstraction/impl/file/ftp/FileResourceImpl.java	(working copy)
@@ -563,12 +563,10 @@
         return true;
     }
 
-    @Override
     public boolean supportsPartialTransfers() {
         return false;
     }
 
-    @Override
     public boolean supportsThirdPartyTransfers() {
         return false;
     }
Index: modules/provider-gt4_0_0/.classpath
===================================================================
--- modules/provider-gt4_0_0/.classpath	(revision 3354)
+++ modules/provider-gt4_0_0/.classpath	(working copy)
@@ -46,6 +46,6 @@
 	<classpathentry exported="true" kind="lib" path="lib/cog-provider-clref-gt4_0_0.jar"/>
 	<classpathentry exported="true" kind="lib" path="lib/concurrent.jar"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/jglobus"/>
-	<classpathentry kind="lib" path="/util/lib/log4j-1.2.8.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
 	<classpathentry kind="output" path=".build"/>
 </classpath>
Index: modules/provider-ssh/.classpath
===================================================================
--- modules/provider-ssh/.classpath	(revision 3354)
+++ modules/provider-ssh/.classpath	(working copy)
@@ -11,8 +11,7 @@
 	</classpathentry>
 	<classpathentry kind="lib" path="lib/j2ssh-common-0.2.2.jar"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/jglobus"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j-1.2.8"/>
-	<classpathentry kind="lib" path="/util/lib/log4j-1.2.8.jar"/>
 	<classpathentry kind="lib" path="lib/j2ssh-core-0.2.2-patch-b.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
 	<classpathentry kind="output" path=".build"/>
 </classpath>
Index: modules/provider-ssh/src/org/globus/cog/abstraction/impl/ssh/file/FileResourceImpl.java
===================================================================
--- modules/provider-ssh/src/org/globus/cog/abstraction/impl/ssh/file/FileResourceImpl.java	(revision 3354)
+++ modules/provider-ssh/src/org/globus/cog/abstraction/impl/ssh/file/FileResourceImpl.java	(working copy)
@@ -386,12 +386,10 @@
         throw new TaskSubmissionException("Not implemented");
     }
 
-    @Override
     public boolean supportsPartialTransfers() {
         return false;
     }
 
-    @Override
     public boolean supportsThirdPartyTransfers() {
         return false;
     }
Index: modules/provider-coaster/.classpath
===================================================================
--- modules/provider-coaster/.classpath	(revision 3354)
+++ modules/provider-coaster/.classpath	(working copy)
@@ -11,9 +11,8 @@
 	<classpathentry combineaccessrules="false" kind="src" path="/karajan"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/jglobus"/>
-	<classpathentry exported="true" kind="con" path="org.eclipse.jdt.USER_LIBRARY/backport-util-concurrent"/>
-	<classpathentry kind="lib" path="/util/lib/backport-util-concurrent.jar"/>
 	<classpathentry kind="src" path="/abstraction-provider-local"/>
 	<classpathentry kind="src" path="/util"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
 	<classpathentry kind="output" path=".build"/>
 </classpath>
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/CoasterPersistentService.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/CoasterPersistentService.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/CoasterPersistentService.java	(working copy)
@@ -69,7 +69,7 @@
         ap.addFlag("local", "Binds the service to the loopback interface");
         ap.addFlag("passive",
             "Initialize the passive worker service and " +
-                    "set the passive worker manager to be the default");
+                    "set the passive worker manager to be the default (otherwise the block allocator will be used)");
         ap.addFlag("help", "Displays usage information");
         ap.addAlias("help", "h");
         try {
@@ -145,7 +145,12 @@
             writePorts(s, portFile, localPortFile);
             
             s.setIgnoreIdleTime(true);
-            s.setDefaultQP("passive");
+            if (ap.isPresent("passive")) {
+                s.setDefaultQP("passive");
+            }
+            else {
+                s.setDefaultQP("block");
+            }
             s.start();
             System.out.println("Started coaster service: " + s);
             s.waitFor();
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/local/LocalService.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/local/LocalService.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/local/LocalService.java	(working copy)
@@ -19,6 +19,7 @@
 import org.apache.log4j.Logger;
 import org.globus.cog.abstraction.coaster.service.Registering;
 import org.globus.cog.abstraction.impl.common.AbstractionFactory;
+import org.globus.cog.abstraction.impl.common.execution.JobException;
 import org.globus.cog.abstraction.impl.common.task.TaskSubmissionException;
 import org.globus.cog.abstraction.interfaces.Service;
 import org.globus.cog.abstraction.interfaces.Status;
@@ -117,7 +118,7 @@
                     throw new TaskSubmissionException("Task ended before registration was received"
                             + (s.getMessage() == null ? ". " : ": " + s.getMessage())
                             + out("STDOUT", t.getStdOutput()) + out("STDERR", t.getStdError()),
-                        s.getException());
+                        s.getException() instanceof JobException ? null : s.getException());
                 }
             }
             return services.get(id);
@@ -126,7 +127,7 @@
 
     private String out(String name, String value) {
         if (value != null) {
-            return "\n" + name + ": " + value;
+            return "\n" + value;
         }
         else {
             return "";
@@ -146,7 +147,7 @@
     }
 
     public String registrationReceived(String id, String url, KarajanChannel channel, 
-            Map<String, String> options) {
+    		Map<String, String> options) {
         if (logger.isDebugEnabled()) {
             logger.debug("Received registration from service " + id + ": " + url);
         }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/TCPBufferManager.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/TCPBufferManager.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/TCPBufferManager.java	(working copy)
@@ -79,7 +79,9 @@
         int crt = crtSocketBuffSz / BUFFER_SIZE_GRANULARITY;
         int old = crt;
         
-        logger.debug("crt: " + crt + ", #sockets: " + sockets.size() + ", min: " + min + ", max: " + max);
+        if (logger.isDebugEnabled()) {
+            logger.debug("crt: " + crt + ", #sockets: " + sockets.size() + ", min: " + min + ", max: " + max);
+        }
         if (sockets.size() == 0) {
             logger.debug("No sockets");
             return;
@@ -91,7 +93,9 @@
                 if (crtSocketBuffSz < MIN_BUFFER_SIZE) {
                     crtSocketBuffSz = MIN_BUFFER_SIZE;
                 }
-                logger.debug("Adjusting buffer size to " + crtSocketBuffSz);
+                if (logger.isInfoEnabled()) {
+                    logger.info("Adjusting buffer size to " + crtSocketBuffSz);
+                }
                 updateBufferSizes();
             }
         }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/LocalTCPService.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/LocalTCPService.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/LocalTCPService.java	(working copy)
@@ -51,7 +51,7 @@
     }
 
     public String registrationReceived(String blockid, String url, 
-            KarajanChannel channel, Map<String, String> options) throws ChannelException {
+            KarajanChannel channel) throws ChannelException {
         if (logger.isInfoEnabled()) {
             logger.info("Received registration: blockid = " +
                         blockid + ", url = " + url);
@@ -61,7 +61,7 @@
         String wid = registrationManager.nextId(blockid);
         cc.getChannelID().setRemoteID(wid);
         ChannelManager.getManager().registerChannel(cc.getChannelID(), channel);
-        registrationManager.registrationReceived(blockid, wid, url, cc, options);
+        registrationManager.registrationReceived(blockid, wid, url, cc);
         return wid;
     }
 
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/Registering.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/Registering.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/Registering.java	(working copy)
@@ -15,8 +15,7 @@
 import org.globus.cog.karajan.workflow.service.channels.KarajanChannel;
 
 public interface Registering {
-    String registrationReceived(String id, String url, KarajanChannel channel, 
-            Map<String, String> options) throws ChannelException;
+    String registrationReceived(String id, String url, KarajanChannel channel, Map<String, String> options) throws ChannelException;
 
     void unregister(String id);
 }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/ServiceConfigurationHandler.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/ServiceConfigurationHandler.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/ServiceConfigurationHandler.java	(working copy)
@@ -26,8 +26,6 @@
         Settings settings =
                 ((CoasterService) getChannel().getChannelContext().getService()).getJobQueue().getSettings();
 
-        logger.debug(settings);
-
         try {
             List<byte[]> l = getInDataChunks();
             if (l != null) {
@@ -37,6 +35,7 @@
                     settings.set(p[0], p[1]);
                 }
             }
+            logger.debug(settings);
             sendReply("OK");
         }
         catch (Exception e) {
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/TimeInterval.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/TimeInterval.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/TimeInterval.java	(working copy)
@@ -72,7 +72,7 @@
         return ms;
     }
     
-    public static TimeInterval fromSeconds(int seconds) {
+    public static TimeInterval fromSeconds(long seconds) {
         return new TimeInterval(seconds * 1000);
     }
     
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/BlockTask.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/BlockTask.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/BlockTask.java	(working copy)
@@ -41,7 +41,11 @@
         setSpecification(spec);
         setName("B" + block.getId());
         setAttribute(spec, "maxwalltime", WallTime.format((int) block.getWalltime().getSeconds()));
+        setAttribute(spec, "jobsPerNode", settings.getJobsPerNode());
+        setAttribute(spec, "coresPerNode", settings.getCoresPerNode());
+        
         int count = block.getWorkerCount() / settings.getJobsPerNode();
+        
         if (count > 1) {
             setAttribute(spec, "jobType", "multiple");
         }
@@ -51,8 +55,7 @@
         for (String name : settings.getAttributeNames()) {
         	setAttribute(spec, name, settings.getAttribute(name));
         }
-        setAttribute(spec, "providerAttributes", settings.getProviderAttributes());
-        // logger.trace("providerAttributes: " + settings.getProviderAttributes());
+        
         String libraryPath = settings.getLdLibraryPath();
         if (libraryPath != null)
             spec.addEnvironmentVariable("LD_LIBRARY_PATH",
@@ -91,6 +94,7 @@
             js.setExecutable("/usr/bin/perl");
             js.addArgument(script);
         }
+        
         // Cobalt on Intrepid, if no directory is specified, assumes $CWD for the
         // job directory.
         // If $CWD happens to be /scratch/something it has a filter in place
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/JobSet.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/JobSet.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/JobSet.java	(working copy)
@@ -10,9 +10,10 @@
 package org.globus.cog.abstraction.coaster.service.job.manager;
 
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Set;
 
-public class JobSet {
+public class JobSet implements Iterable<Job> {
     private Set<Job> jobs;
     private Metric metric;
     
@@ -56,4 +57,8 @@
         
         return sum;
     }
+
+    public Iterator<Job> iterator() {
+        return jobs.iterator();
+    }
 }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/BlockQueueProcessor.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/BlockQueueProcessor.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/BlockQueueProcessor.java	(working copy)
@@ -21,6 +21,7 @@
 import org.globus.cog.abstraction.coaster.service.CoasterService;
 import org.globus.cog.abstraction.coaster.service.RegistrationManager;
 import org.globus.cog.abstraction.impl.common.AbstractionFactory;
+import org.globus.cog.abstraction.impl.common.execution.WallTime;
 import org.globus.cog.abstraction.interfaces.ExecutionService;
 import org.globus.cog.abstraction.interfaces.Task;
 import org.globus.cog.karajan.workflow.service.channels.ChannelContext;
@@ -160,18 +161,32 @@
     public void enqueue1(Task t) {
         synchronized (incoming) {
             Job j = new Job(t);
-            if (logger.isDebugEnabled()) {
-                logger.debug("Got job with walltime = " + j.getMaxWallTime());
+            if (checkJob(j)) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Got job with walltime = " + j.getMaxWallTime());
+                }
+                if (planning) {
+                    incoming.add(j);
+                }
+                else {
+                    queue(j);
+                }
             }
-            if (planning) {
-                incoming.add(j);
-            }
-            else {
-                queue(j);
-            }
         }
     }
 
+    private boolean checkJob(Job job) {
+        if (job.getMaxWallTime().getSeconds() > settings.getMaxtime() - settings.getReserve().getSeconds()) {
+            job.fail("Job walltime > maxTime - reserve (" + 
+                    WallTime.format("hms", job.getMaxWallTime().getSeconds()) + " > " + 
+                    WallTime.format("hms", settings.getMaxtime() - settings.getReserve().getSeconds()) + ")", null);
+            return false;
+        }
+        else {
+            return true;
+        }
+    }
+
     public void enqueue(List<Job> jobs) {
         synchronized (incoming) {
             incoming.addAll(jobs);
@@ -179,8 +194,8 @@
     }
 
     private void queue(Job job) {
-        synchronized (queued) {
-            queued.add(job);
+    	synchronized (queued) {
+    	    queued.add(job);
             queued.notify();
         }
     }
@@ -245,7 +260,7 @@
     }
 
     private Set<Job> queueToExistingBlocks() {
-        double runningSize = running.getSizeLeft();
+        double runningSize = getRunningSizeLeft();
         Set<Job> remove = new HashSet<Job>();
         for (Job j : holding) {
             if (allocsize - queued.getJSize() - runningSize > metric.getSize(j) && fits(j)) {
@@ -261,7 +276,7 @@
 
     private void requeueNonFitting() {
         int count = 0;
-        double runningSize = running.getSizeLeft();
+        double runningSize = getRunningSizeLeft();
         logger.debug("allocsize = " + allocsize +
                      ", queuedsize = " + queued.getJSize() +
                      ", running = " + runningSize +
@@ -270,8 +285,13 @@
             Job j = queued.removeOne(TimeInterval.FOREVER,
                                      Integer.MAX_VALUE);
             if (j == null) {
-                CoasterService.error(19, "queued size > 0 but no job dequeued. Queued: " + queued,
-                    new Throwable());
+                if (queued.getJSize() > 0) {
+                    CoasterService.error(19, "queuedsize > 0 but no job dequeued. Queued: " + queued,
+                        new Throwable());
+                }
+                else if (allocsize - getRunningSizeLeft() < 0) {
+                    warnAboutWalltimes(running);
+                }
             }
             holding.add(j);
             count++;
@@ -280,7 +300,28 @@
             logger.info("Requeued " + count + " non-fitting jobs");
         }
     }
+    
+    private void warnAboutWalltimes(Iterable<Job> set) {
+        synchronized(set) {
+            for (Job r : set) {
+                if (r.getMaxWallTime().isLessThan(Time.now().subtract(r.getStartTime()))) {
+                    Task t = r.getTask();
+                    if (t.getAttribute("#warnedAboutWalltime") == null) {
+                        logger.warn("The following job exceeded its walltime: " + 
+                            t.getSpecification());
+                        t.setAttribute("#warnedAboutWalltime", Boolean.TRUE);
+                    }
+                }
+            }
+        }
+    }
 
+    private double getRunningSizeLeft() {
+        synchronized(running) {
+            return running.getSizeLeft();
+        }
+    }
+
     private void computeSums() {
         sums = new ArrayList<Integer>(holding.size());
         sums.add(0);
@@ -614,13 +655,17 @@
     public Job request(TimeInterval ti, int cpus) {
         Job job = queued.removeOne(ti, cpus);
         if (job != null) {
-            running.add(job);
+            synchronized(running) {
+                running.add(job);
+            }
         }
         return job;
     }
 
     public void jobTerminated(Job job) {
-        running.remove(job);
+        synchronized(running) {
+            running.remove(job);
+        }
     }
 
     /**
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/Settings.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/Settings.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/Settings.java	(working copy)
@@ -50,13 +50,19 @@
                        "workerLoggingDirectory",
                        "ldLibraryPath", "workerCopies",
                        "directory", "useHashBang",
-                       "providerAttributes", "parallelism" };
+                       "parallelism",
+                       "coresPerNode"};
 
     /**
      * The maximum number of blocks that can be active at one time
      */
     private int slots = 20;
     private int jobsPerNode = 1;
+    
+    /**
+     * TODO: clarify what this does
+     */
+    private String coresPerNode = "1";
 
     /**
      * How many nodes to allocate at once
@@ -113,7 +119,7 @@
     private SecurityContext securityContext;
 
     private boolean remoteMonitorEnabled;
-
+    
 	/**
 	 * Adjusts the metric used for block sizes.
 	 *
@@ -155,10 +161,13 @@
 
     private String useHashBang = null;
 
-    private String providerAttributes = null;
-
     private final Map<String, String> attributes;
 
+    /**
+     * A pass-through setting for SGE, parallel environment
+    */
+    private String pe;
+
     public Settings() {
         hook = new Hook();
         callbackURIs = new TreeSet<URI>();
@@ -304,14 +313,6 @@
         return workerLoggingDirectory;
     }
 
-    public String getProviderAttributes() {
-        return providerAttributes;
-    }
-
-    public void setProviderAttributes(String options) {
-        providerAttributes = options;
-    }
-
     /**
      * The following values are considered valid:
      * <dl>
@@ -466,6 +467,13 @@
         this.parallelism = parallelism;
     }
 
+    public String getCoresPerNode() {
+        return coresPerNode;
+    }
+    
+    public void setCoresPerNode(String coresPerNode) {
+        this.coresPerNode=coresPerNode;
+    }
     public String getHookClass() {
         return hookClass;
     }
@@ -519,7 +527,7 @@
     public void setUseHashBang(String uhb) {
         this.useHashBang = uhb;
     }
-
+    
     public void setAttribute(String name, String value) {
     	attributes.put(name, value);
     }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/Cpu.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/Cpu.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/Cpu.java	(working copy)
@@ -20,9 +20,12 @@
 import org.globus.cog.abstraction.interfaces.Status;
 import org.globus.cog.abstraction.interfaces.StatusListener;
 import org.globus.cog.abstraction.interfaces.Task;
+import org.globus.cog.karajan.workflow.service.channels.ChannelListener;
+import org.globus.cog.karajan.workflow.service.channels.ChannelManager;
 import org.globus.cog.karajan.workflow.service.channels.KarajanChannel;
 import org.globus.cog.karajan.workflow.service.commands.Command;
 import org.globus.cog.karajan.workflow.service.commands.Command.Callback;
+import org.globus.cog.karajan.workflow.service.commands.HeartBeatCommand;
 
 public class Cpu implements Comparable<Cpu>, Callback, StatusListener {
     public static final Logger logger = Logger.getLogger(Cpu.class);
@@ -264,10 +267,12 @@
     }
 
     public void shutdown() {
-		if (shutdown) {
-			return;
-		}
-		shutdown = true;
+        synchronized(this) {
+            if (shutdown) {
+                return;
+            }
+            shutdown = true;
+        }
 		Block block = node.getBlock();
         done.clear();
         if (running != null) {
Index: modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/Block.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/Block.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/coaster/service/job/manager/Block.java	(working copy)
@@ -194,7 +194,7 @@
     public void shutdownIfEmpty(Cpu cpu) {
         synchronized (scpus) {
             if (scpus.isEmpty()) {
-                if (logger.isInfoEnabled()) {
+                if (logger.isInfoEnabled() && !shutdown) {
                     logger.info(this + ": all cpus are clear");
                 }
                 shutdown(false);
@@ -269,9 +269,16 @@
                 }
 				if (!failed) {
 					if (count < workers || now) {
+					    if (logger.isInfoEnabled()) {
+					        logger.info("Adding short shutdown watchdog: count = " + 
+					            count + ", workers = " + workers + ", now = " + now);
+					    }
 	                    addForcedShutdownWatchdog(100);
     	            }
 					else {
+					    if (logger.isInfoEnabled()) {
+					        logger.info("Adding normal shutdown watchdog");
+					    }
 	   					addForcedShutdownWatchdog(SHUTDOWN_WATCHDOG_DELAY);
 					}
 				}
@@ -304,18 +311,16 @@
     }
 
     public void forceShutdown() {
-        if (task != null) {
-            try {
-                getSubmitter().cancel(this);
-            }
-            catch (Exception e) {
-                if (failed)
-                    logger.debug("Failed to shut down block: " +
-                                this + " " + e.getMessage());
-                else
+        synchronized(cpus) {
+            if (task != null) {
+                try {
+                    getSubmitter().cancel(this);
+                }
+                catch (Exception e) {
                     logger.warn("Failed to shut down block: " + this, e);
+                }
+                bqp.blockTaskFinished(this);
             }
-            bqp.blockTaskFinished(this);
         }
     }
 
@@ -375,7 +380,7 @@
             }
         }
     }
-
+    
     private int seq;
 
     public String nextId() {
@@ -414,9 +419,8 @@
                     }
                     bqp.blockTaskFinished(this);
                     running = false;
+                    task = null;
                 }
-                logger.info(id + " stdout: " + prettifyOut(task.getStdOutput()));
-                logger.info(id + " stderr: " + prettifyOut(task.getStdError()));
             }
             else if (s.getStatusCode() == Status.ACTIVE) {
                 starttime = Time.now();
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/PutFileHandler.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/PutFileHandler.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/PutFileHandler.java	(working copy)
@@ -10,22 +10,30 @@
 package org.globus.cog.abstraction.impl.file.coaster.handlers;
 
 import java.io.IOException;
+import java.util.Arrays;
 
 import org.apache.log4j.Logger;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers.Direction;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.ThrottleManager;
 import org.globus.cog.abstraction.impl.file.coaster.handlers.providers.IOHandle;
 import org.globus.cog.abstraction.impl.file.coaster.handlers.providers.IOProvider;
 import org.globus.cog.abstraction.impl.file.coaster.handlers.providers.IOProviderFactory;
 import org.globus.cog.abstraction.impl.file.coaster.handlers.providers.IOWriter;
 import org.globus.cog.abstraction.impl.file.coaster.handlers.providers.WriteIOCallback;
 import org.globus.cog.karajan.workflow.service.ProtocolException;
+import org.globus.cog.karajan.workflow.service.channels.KarajanChannel;
 
 public class PutFileHandler extends CoasterFileRequestHandler implements WriteIOCallback {
     public static final Logger logger = Logger.getLogger(PutFileHandler.class);
+    
+    public static final byte[] STOP = "STOP".getBytes();
+    public static final byte[] CONTINUE = "CONTINUE".getBytes();
 
     private long len = -1;
     private String src, dst;
     private IOProvider provider;
     private IOWriter writer;
+    private boolean done, suspended;
 
     public void requestComplete() throws ProtocolException {
         if (writer != null && provider.isDirect()) {
@@ -39,6 +47,10 @@
     }
 
     protected void addInData(boolean fin, boolean err, byte[] data) {
+    	if (logger.isDebugEnabled()) {
+    		logger.debug(this + " got data, fin = " + fin + 
+    				", err = " + err + ", sz = " + data.length);
+    	}
         try {
             if (err) {
                 super.addInData(fin, err, data);
@@ -46,22 +58,25 @@
             else if (len == -1) {
                 len = unpackLong(data);
                 if (logger.isDebugEnabled()) {
-                    logger.debug(dst + " Size: " + len);
+                    logger.debug(this + " " + dst + " Size: " + len);
                 }
             }
             else if (src == null) {
                 src = new String(data);
                 if (logger.isInfoEnabled()) {
-                    logger.info("Source: " + src);
+                    logger.info(this + " source: " + src);
                 }
             }
             else if (dst == null) {
                 dst = new String(data);
                 if (logger.isInfoEnabled()) {
-                    logger.info("Destination: " + dst);
+                    logger.info(this + " destination: " + dst);
                 }
                 provider = IOProviderFactory.getDefault().instance(getProtocol(dst));
                 writer = provider.push(src, dst, this);
+                if (!provider.isDirect()) {
+                    writer.setUpThrottling();
+                }
                 writer.setLength(len);
             }
             else {
@@ -89,9 +104,49 @@
             }
         }
     }
+    
+    public void suspend() {
+        synchronized(this) {
+            if (done) {
+                return;
+            }
+        }
+        int tag = getId();
+        if (logger.isDebugEnabled()) {
+            logger.debug(this + " suspending");
+        }
+        suspended = true;
+        getChannel().sendTaggedReply(tag, STOP, KarajanChannel.SIGNAL_FLAG);
+        writer.suspend();
+    }
+    
+    public void resume() {
+        synchronized(this) {
+            if (done) {
+                return;
+            }
+            setLastTime(System.currentTimeMillis());
+            suspended = false;
+        }
+        int tag = getId();
+        if (logger.isDebugEnabled()) {
+            logger.debug(this + " resuming");
+        }
+        getChannel().sendTaggedReply(tag, CONTINUE, KarajanChannel.SIGNAL_FLAG);
+        writer.resume();
+    }
 
     public void done(IOHandle op) {
+        synchronized(this) {
+            done = true;
+        }
+        if (!provider.isDirect()) {
+            writer.cancelThrottling();
+        }
         try {
+        	if (logger.isInfoEnabled()) {
+        		logger.info(this + " Transfer done");
+        	}
             sendReply("OK");
         }
         catch (ProtocolException e) {
@@ -101,12 +156,19 @@
 
     public void error(IOHandle op, Exception e) {
         try {
+        	logger.warn("Failed to write file data", e);
             sendError("Failed to write file data: " + e.getMessage());
         }
         catch (ProtocolException ee) {
             logger.warn("Failed to send reply", ee);
         }
     }
+    
+    public void info(String s) {
+    	if (logger.isInfoEnabled()) {
+    		logger.info(this + " -> " + s);
+    	}
+    }
 
     public void sendError(String error, Throwable e) throws ProtocolException {
         if (provider != null && writer != null) {
@@ -130,6 +192,33 @@
                 logger.info("Failed to close output stream", e);
             }
         }
+        ThrottleManager.getDefault(Direction.OUT).unregister(this);
         super.errorReceived(msg, t);
     }
+
+    @Override
+    public void handleSignal(byte[] data) {
+        if (Arrays.equals(data, STOP)) {
+        	suspended = true;
+        }
+        else if (Arrays.equals(data, CONTINUE)) {
+        	synchronized(this) {
+        	    setLastTime(System.currentTimeMillis());
+        	    suspended = false;
+        	}
+        }
+        else {
+        	logger.warn("Unhandled signal: " + String.valueOf(data));
+        }
+    }
+
+    @Override
+    public synchronized long getLastTime() {
+        if (suspended) {
+        	return Long.MAX_VALUE;
+        }
+        else {
+        	return super.getLastTime();
+        }
+    }
 }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/GetFileHandler.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/GetFileHandler.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/GetFileHandler.java	(working copy)
@@ -24,6 +24,8 @@
 
 public class GetFileHandler extends CoasterFileRequestHandler implements SendCallback, ReadIOCallback {
     public static final Logger logger = Logger.getLogger(GetFileHandler.class);
+    
+    public static final String QUEUED = "QUEUED";
 
     // private long size;
     // private Exception ex;
@@ -34,6 +36,9 @@
     public void requestComplete() throws ProtocolException {
         String src = getInDataAsString(0);
         try {
+            if (logger.isInfoEnabled()) {
+                logger.info(this + " request complete");
+            }
             provider = IOProviderFactory.getDefault().instance(getProtocol(src));
             sendReply();
         }
@@ -64,6 +69,10 @@
     }
 
     public void dataSent() {
+        if (logger.isDebugEnabled()) {
+            logger.debug(this + " data sent");
+        }
+    	setLastTime(System.currentTimeMillis());
         reader.dataSent();
     }
 
@@ -71,22 +80,48 @@
         if (!lengthSent) {
             throw new RuntimeException("No length provided");
         }
+        if (logger.isDebugEnabled()) {
+            logger.debug(this + " sending " + data.limit());
+        }
         getChannel().sendTaggedReply(getId(), data, last, false, this);
     }
 
+    public void queued() {
+        if (logger.isInfoEnabled()) {
+            logger.info(this + " sending queued signal");
+        }
+        getChannel().sendTaggedReply(getId(), QUEUED.getBytes(), KarajanChannel.SIGNAL_FLAG, null);
+    }
+
+    public void info(String msg) {
+        if (logger.isInfoEnabled()) {
+            logger.info(this + " -> " + msg);
+        }
+    }
+
     public void done(IOHandle op) {
+        if (logger.isInfoEnabled()) {
+            logger.info(this + " read done");
+        }
         if (!provider.isDirect()) {
-            getChannel().sendTaggedReply(getId(), "OK".getBytes(), true, false, null);
+            getChannel().sendTaggedReply(getId(), "OK".getBytes(), true, false);
             reader.close();
         }
     }
 
     public void error(IOHandle op, Exception e) {
-        getChannel().sendTaggedReply(getId(), e.getMessage().getBytes(), true, true);
+        getChannel().sendTaggedReply(getId(), e.getMessage() != null ? e.getMessage().getBytes() : e.toString().getBytes(), 
+        		KarajanChannel.FINAL_FLAG + KarajanChannel.ERROR_FLAG);
     }
 
     public void length(long len) {
         if (provider.isDirect()) {
+            if (lengthSent) {
+                logger.warn("length() called twice", new Throwable("xz0001"));
+            }
+            if (logger.isInfoEnabled()) {
+                logger.info(this + " sending length: " + len + ", " + System.identityHashCode(this));
+            }
             lengthSent = true;
             getChannel().sendTaggedReply(getId(), pack(len), len == 0, false);
         }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/WriteIOCallback.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/WriteIOCallback.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/WriteIOCallback.java	(working copy)
@@ -11,4 +11,10 @@
 
 public interface WriteIOCallback extends IOCallback {
 
+    void info(String valueOf);
+
+    void suspend();
+
+    void resume();
+
 }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/ReadIOCallback.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/ReadIOCallback.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/ReadIOCallback.java	(working copy)
@@ -15,4 +15,8 @@
     void length(long len);
     
     void data(IOHandle handle, ByteBuffer data, boolean last);
+
+    void info(String msg);
+
+    void queued();
 }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/CoGResourceIOProvider.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/CoGResourceIOProvider.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/CoGResourceIOProvider.java	(working copy)
@@ -18,8 +18,10 @@
 
 import org.apache.log4j.Logger;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers.Direction;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.ReadBuffer;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.ReadBufferCallback;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.ThrottleManager;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.WriteBuffer;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.WriteBufferCallback;
 import org.globus.cog.abstraction.impl.file.coaster.handlers.CoasterFileRequestHandler;
@@ -44,6 +46,8 @@
     }
 
     private static class Writer implements IOWriter, WriteBufferCallback, Abortable {
+        private static final Direction BUFDIR = Direction.OUT;
+        
         private File f;
         private long len, crt;
         private WriteIOCallback cb;
@@ -71,7 +75,7 @@
                 cb.done(this);
             }
             else {
-                buf = Buffers.newWriteBuffer(new FileOutputStream(f).getChannel(), this);
+                buf = Buffers.newWriteBuffer(Buffers.getBuffers(BUFDIR), new FileOutputStream(f).getChannel(), this);
             }
         }
 
@@ -103,6 +107,28 @@
             buf.close();
             f.delete();
         }
+
+        /**
+         * Used to notify upstream handler that the transfer
+         * has been suspended and that what otherwise would be
+         * timeouts are benign
+         */
+        public void suspend() {
+        }
+
+        /**
+         * The opposite of suspend()
+         */
+        public void resume() {
+        }
+        
+        public void setUpThrottling() {
+            Buffers.getBuffers(BUFDIR).getThrottleManager().register(cb);
+        }
+
+        public void cancelThrottling() {
+            ThrottleManager.getDefault(BUFDIR).unregister(cb);
+        }
     }
 
     private static class Reader implements IOReader, ReadBufferCallback {
@@ -125,7 +151,7 @@
             cb.length(f.length());
             try {
                 synchronized (this) {
-                    rbuf = Buffers.newReadBuffer(fc, f.length(), this);
+                    rbuf = Buffers.newReadBuffer(Buffers.getBuffers(Direction.IN), fc, f.length(), this);
                 }
             }
             catch (InterruptedException e) {
@@ -144,6 +170,10 @@
             }
         }
 
+        public void queued() {
+            cb.queued();
+        }
+
         private synchronized void closeBuffer() {
             try {
                 rbuf.close();
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/IOWriter.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/IOWriter.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/IOWriter.java	(working copy)
@@ -17,4 +17,12 @@
     void close() throws IOException;
 
     void write(boolean last, byte[] data) throws IOException;
+
+    void suspend();
+
+    void resume();
+
+    void setUpThrottling();
+
+    void cancelThrottling();
 }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/ProxyIOProvider.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/ProxyIOProvider.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/ProxyIOProvider.java	(working copy)
@@ -14,15 +14,22 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.ByteBuffer;
+import java.util.Arrays;
 import java.util.LinkedList;
+import java.util.List;
 
 import org.apache.log4j.Logger;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers.Allocation;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers.Direction;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.ReadBuffer;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.ReadBufferCallback;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.ThrottleManager;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.WriteBuffer;
 import org.globus.cog.abstraction.impl.file.coaster.commands.GetFileCommand;
 import org.globus.cog.abstraction.impl.file.coaster.commands.PutFileCommand;
+import org.globus.cog.abstraction.impl.file.coaster.handlers.GetFileHandler;
+import org.globus.cog.abstraction.impl.file.coaster.handlers.PutFileHandler;
 import org.globus.cog.karajan.workflow.service.ProtocolException;
 import org.globus.cog.karajan.workflow.service.channels.ChannelException;
 import org.globus.cog.karajan.workflow.service.channels.ChannelManager;
@@ -53,10 +60,20 @@
     }
 
     private static class Writer implements IOWriter, Callback {
+        /**
+         * Reverse the buffer direction compared to the local IO provider.
+         * The actual label on the set of buffers is not relevant as long as
+         * they are different on one JVM instance. But when using proxy mode
+         * in local:local (i.e. both service and client in the same JVM) this
+         * avoids a deadlock.
+         */
+        private static Direction BUFDIR = Direction.IN;
+        
         private CustomPutFileCmd cmd;
         private WriteIOCallback cb;
         private KarajanChannel channel;
         private String src, dst;
+        private boolean done, suspended;
 
         public Writer(String src, String dst, WriteIOCallback cb) throws IOException {
             this.cb = cb;
@@ -80,6 +97,16 @@
                 cmd = new CustomPutFileCmd(src, "file://localhost/" + uri.getPath().substring(1), len, this);
                 channel = ChannelManager.getManager().reserveChannel("id://" + uri.getHost(), null);
                 cmd.executeAsync(channel, this);
+                cb.info(String.valueOf(cmd.getId()));
+                synchronized(this) {
+                    if (!suspended) {
+                        return;
+                    }
+                }
+                if (logger.isInfoEnabled()) {
+                    logger.info(cmd.getId() + " suspended before. Sending signal.");
+                }
+                cmd.suspend();
             }
             catch (Exception e) {
                 throw new IOException(e.getMessage());
@@ -88,6 +115,7 @@
 
         public void write(boolean last, byte[] data) throws IOException {
             try {
+                done = last;
                 cmd.getBuffer().queue(last, ByteBuffer.wrap(data));
             }
             catch (InterruptedException e) {
@@ -106,11 +134,44 @@
         public void abort() throws IOException {
             close();
         }
+
+        public void suspend() {
+            if (!done) {
+                synchronized(this) {
+                    if (cmd == null) {
+                        suspended = true;
+                        return;
+                    }
+                }
+                cmd.suspend();
+            }
+        }
+
+        public void resume() {
+            if (!done) {
+                synchronized(this) {
+                    if (cmd == null) {
+                        suspended = false;
+                        return;
+                    }
+                }
+                cmd.resume();
+            }
+        }
+
+        public void setUpThrottling() {
+            Buffers.getBuffers(BUFDIR).getThrottleManager().register(cb);
+        }
+
+        public void cancelThrottling() {
+            ThrottleManager.getDefault(BUFDIR).unregister(cb);
+        }
     }
 
     private static class CustomPutFileCmd extends PutFileCommand {
         private CReadBuffer buffer;
         private Writer handle;
+        private boolean suspended;
 
         public CustomPutFileCmd(String local, String remote, long length, Writer handle) throws IOException,
                 InterruptedException {
@@ -118,8 +179,31 @@
             this.handle = handle;
         }
 
+        public void resume() {
+            synchronized(this) {
+                setLastTime(System.currentTimeMillis());
+                suspended = false;
+            }
+            getChannel().sendTaggedData(getId(), KarajanChannel.SIGNAL_FLAG, PutFileHandler.CONTINUE);
+        }
+
+        public void suspend() {
+            suspended = true;
+            getChannel().sendTaggedData(getId(), KarajanChannel.SIGNAL_FLAG, PutFileHandler.STOP);
+        }
+        
+        @Override
+        public synchronized long getLastTime() {
+            if (suspended) {
+                return Long.MAX_VALUE;
+            }
+            else {
+                return super.getLastTime();
+            }
+        }
+
         protected ReadBuffer createBuffer() throws FileNotFoundException, InterruptedException {
-            return buffer = new CReadBuffer(Buffers.getDefault(), this);
+            return buffer = new CReadBuffer(Buffers.getBuffers(Direction.IN), this);
         }
 
         public CReadBuffer getBuffer() {
@@ -137,6 +221,7 @@
     }
 
     private static class CReadBuffer extends ReadBuffer {
+        
         // private Exception error;
         // private BlockingQueue queue;
         // private boolean seenLast;
@@ -153,17 +238,20 @@
             getCallback().error(true, e);
         }
 
-        public synchronized void queue(boolean last, ByteBuffer buf) throws InterruptedException {
-            while (crt >= Buffers.ENTRIES_PER_STREAM) {
-                wait();
+        public void queue(boolean last, ByteBuffer buf) throws InterruptedException {
+            if (logger.isDebugEnabled()) {
+                logger.debug(getCallback() + " got data");
             }
-            crt++;
-            alloc.add(buffers.request(1));
-            /* if (last) {
-                seenLast = true;
-            } */
+            Buffers.Allocation a = buffers.request(1);
+            synchronized(this) {
+                crt++;
+                alloc.add(a);
+                /* if (last) {
+                    seenLast = true;
+                } */
+            }
             getCallback().dataRead(last, buf);
-        }
+        }    
 
         public synchronized void freeFirst() {
             buffers.free(alloc.removeFirst());
@@ -174,7 +262,7 @@
         protected void deallocateBuffers() {
         }
 
-        public void doStuff(boolean last, ByteBuffer b) {
+        public void doStuff(boolean last, ByteBuffer b, Buffers.Allocation alloc) {
             // not used
         }
     }
@@ -223,6 +311,7 @@
                 logger.debug("Sending proxy get");
                 cmd.executeAsync(channel, this);
                 logger.debug("Proxy get sent");
+                cb.info(String.valueOf(cmd.getId()));
             }
             catch (ProtocolException e) {
                 logger.warn("Error requesting file from " + channel, e);
@@ -249,6 +338,7 @@
         }
 
         public void dataSent() {
+            cmd.cwb.releaseOne();
         }
 
         public void errorReceived(Command cmd, String msg, Exception t) {
@@ -273,6 +363,7 @@
     private static class CustomGetFileCmd extends GetFileCommand {
         private final ReadIOCallback cb;
         private final Reader handle;
+        public CWriteBuffer cwb; 
 
         public CustomGetFileCmd(String src, String dst, Reader handle) throws IOException {
             super(src, dst, null);
@@ -284,24 +375,42 @@
         }
 
         protected WriteBuffer createWriteBuffer() throws IOException {
-            return new CWriteBuffer(Buffers.getDefault(), this);
+            return cwb = new CWriteBuffer(Buffers.getBuffers(Direction.OUT), this);
         }
 
         protected void setLen(long len) {
             super.setLen(len);
             cb.length(len);
         }
+        
+            @Override
+        public void handleSignal(byte[] data) {
+            if (Arrays.equals(GetFileHandler.QUEUED.getBytes(), data)) {
+                setQueued(true);
+                cb.queued();
+            }
+        }
     }
 
     private static class CWriteBuffer extends WriteBuffer {
         private final CustomGetFileCmd cmd;
+        public List<Allocation> alloc;
 
         protected CWriteBuffer(Buffers buffers, CustomGetFileCmd cmd) {
             super(buffers);
             this.cmd = cmd;
+            alloc = new LinkedList<Allocation>();
         }
 
-        public void doStuff(boolean last, ByteBuffer b) {
+        public void releaseOne() {
+            Allocation a;
+            synchronized(alloc) {
+                a = alloc.remove(0);
+            }
+            buffers.free(a);
+        }
+
+        public void doStuff(boolean last, ByteBuffer b, Buffers.Allocation alloc) {
             try {
                 cmd.cb.data(cmd.handle, b, last);
             }
@@ -309,6 +418,14 @@
                 e.printStackTrace();
             }
         }
+        
+        public void write(boolean last, byte[] data) throws InterruptedException {
+            Allocation a = buffers.request(1);
+            synchronized(alloc) {
+                alloc.add(a);
+            }
+            buffers.queueRequest(last, ByteBuffer.wrap(data), this);
+        }
 
         private byte[] getByteArray(ByteBuffer b) {
             if (b.hasArray()) {
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/LocalCopyIOProvider.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/LocalCopyIOProvider.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/LocalCopyIOProvider.java	(working copy)
@@ -110,5 +110,21 @@
             super(src, getPath(dest), cb);
             start();
         }
+
+        public void suspend() {
+            // not used
+        }
+
+        public void resume() {
+            // not used
+        }
+
+        public void setUpThrottling() {
+            // not used
+        }
+
+        public void cancelThrottling() {
+            // not used
+        }
     }
 }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/LocalIOProvider.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/LocalIOProvider.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/handlers/providers/LocalIOProvider.java	(working copy)
@@ -22,8 +22,10 @@
 import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.ReadBuffer;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.ReadBufferCallback;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.ThrottleManager;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.WriteBuffer;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.WriteBufferCallback;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers.Direction;
 import org.globus.cog.abstraction.impl.file.coaster.handlers.CoasterFileRequestHandler;
 
 public class LocalIOProvider implements IOProvider {
@@ -55,6 +57,8 @@
     }
 
     private static class Writer implements IOWriter, WriteBufferCallback, Abortable {
+        private static Direction BUFDIR = Direction.OUT;
+        
         private File f;
         private long len, crt;
         private WriteIOCallback cb;
@@ -86,7 +90,7 @@
                         throw new IOException("Failed to create directory " + p.getAbsolutePath());
                     }
                 }
-                buf = Buffers.newWriteBuffer(new FileOutputStream(f).getChannel(), this);
+                buf = Buffers.newWriteBuffer(Buffers.getBuffers(Direction.OUT), new FileOutputStream(f).getChannel(), this);
             }
         }
 
@@ -118,6 +122,22 @@
             buf.close();
             f.delete();
         }
+
+        public void suspend() {
+            // not used
+        }
+
+        public void resume() {
+            // not used
+        }
+        
+        public void setUpThrottling() {
+            Buffers.getBuffers(BUFDIR).getThrottleManager().register(cb);
+        }
+
+        public void cancelThrottling() {
+            ThrottleManager.getDefault(BUFDIR).unregister(cb);
+        }
     }
 
     private static class Reader implements IOReader, ReadBufferCallback {
@@ -142,7 +162,7 @@
             cb.length(f.length());
             try {
                 synchronized(this) {
-                    rbuf = Buffers.newReadBuffer(fc, f.length(), this);
+                    rbuf = Buffers.newReadBuffer(Buffers.getBuffers(Direction.IN), fc, f.length(), this);
                 }
             }
             catch (InterruptedException e) {
@@ -151,6 +171,7 @@
         }
 
         public synchronized void dataSent() {
+            logger.debug("Data sent");
             rbuf.freeFirst();
         }
 
@@ -161,6 +182,10 @@
             }
         }
 
+        public void queued() {
+            cb.queued();
+        }
+
         private synchronized void closeBuffer() {
             try {
                 rbuf.close();
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/commands/PutFileCommand.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/commands/PutFileCommand.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/commands/PutFileCommand.java	(working copy)
@@ -19,6 +19,7 @@
 import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.ReadBuffer;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.ReadBufferCallback;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers.Direction;
 import org.globus.cog.karajan.workflow.service.ProtocolException;
 import org.globus.cog.karajan.workflow.service.channels.KarajanChannel;
 import org.globus.cog.karajan.workflow.service.commands.Command;
@@ -27,6 +28,7 @@
     public static final Logger logger = Logger.getLogger(PutFileCommand.class);
 
     public static final String NAME = "PUT";
+    public static final String QUEUED = "QUEUED";
 
     private String dest;
     private long size;
@@ -34,6 +36,7 @@
     private ReadBuffer rbuf;
     // private Exception ex;
     private String src;
+    private boolean done;
     
     public PutFileCommand(String src, String dest) throws IOException, InterruptedException {
         this(src, dest, new File(src).length());
@@ -50,7 +53,7 @@
     }
 
     protected ReadBuffer createBuffer() throws FileNotFoundException, InterruptedException {
-        return Buffers.newReadBuffer(new FileInputStream(src).getChannel(), size, this);
+        return Buffers.newReadBuffer(Buffers.getBuffers(Direction.OUT), new FileInputStream(src).getChannel(), size, this);
     }
 
     public void send() throws ProtocolException {
@@ -61,27 +64,46 @@
         if (channel == null) {
             throw new ProtocolException("Unregistered command");
         }
+        
+        long now = System.currentTimeMillis();
+        setSendReqTime(now);
+        setLastTime(now);
 
+        if (logger.isDebugEnabled()) {
+            logger.debug(this + ", src: " + src + ", dest: " + dest + ", size: " + size);
+        }
         channel.sendTaggedData(getId(), false, getOutCmd().getBytes());
         channel.sendTaggedData(getId(), false, pack(size));
         channel.sendTaggedData(getId(), false, src.getBytes());
         channel.sendTaggedData(getId(), size == 0, dest.getBytes());
         if (logger.isInfoEnabled()) {
-            logger.info("Sending data");
+            logger.info(this + " sending data");
         }
     }
 
     public void dataSent() {
+        super.dataSent();
+        if (logger.isDebugEnabled()) {
+            logger.debug(this + " data sent");
+        }
         rbuf.freeFirst();
     }
 
     public void dataRead(boolean last, ByteBuffer buf) {
+        if (logger.isDebugEnabled()) {
+            logger.debug(this + " data read, last = " + last);
+        }
         getChannel().sendTaggedData(getId(), last ? KarajanChannel.FINAL_FLAG : 0, buf, this);
         if (last) {
+            done = true;
             closeBuffer();
         }
     }
 
+    public void queued() {
+        getChannel().sendTaggedData(getId(), KarajanChannel.SIGNAL_FLAG, QUEUED.getBytes());
+    }
+
     private void closeBuffer() {
     	try {
             rbuf.close();
@@ -95,4 +117,8 @@
         getChannel().sendTaggedReply(getId(), e.getMessage().getBytes(), true, true, null);
         closeBuffer();
     }
+
+    public String toString() {
+        return super.toString() + (done ? " (d)" : " (t)");
+    }
 }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/commands/GetFileCommand.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/commands/GetFileCommand.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/commands/GetFileCommand.java	(working copy)
@@ -11,11 +11,14 @@
 
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.Arrays;
 
 import org.apache.log4j.Logger;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers.Direction;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.WriteBuffer;
 import org.globus.cog.abstraction.impl.file.coaster.buffers.WriteBufferCallback;
-import org.globus.cog.abstraction.impl.file.coaster.buffers.WriteBuffer;
+import org.globus.cog.abstraction.impl.file.coaster.handlers.GetFileHandler;
 import org.globus.cog.abstraction.interfaces.ProgressMonitor;
 import org.globus.cog.karajan.workflow.service.commands.Command;
 
@@ -25,6 +28,7 @@
     private long len = -1;
     private WriteBuffer wt;
     private String dst;
+    private boolean queued;
     // private ProgressMonitor pm;
     
     public GetFileCommand(String src, String dst, ProgressMonitor pm)
@@ -41,10 +45,11 @@
     }
 
     protected WriteBuffer createWriteBuffer() throws IOException {
-        return Buffers.newWriteBuffer(new FileOutputStream(dst).getChannel(), this);
+        return Buffers.newWriteBuffer(Buffers.getBuffers(Direction.IN), new FileOutputStream(dst).getChannel(), this);
     }
     
     protected void addInData(boolean fin, boolean err, byte[] data) {
+        queued = false;
         if (err) {
             super.addInData(fin, err, data);
         }
@@ -84,6 +89,30 @@
     	}
     }
 
+    @Override
+    public void handleSignal(byte[] data) {
+        if (Arrays.equals(GetFileHandler.QUEUED.getBytes(), data)) {
+            setQueued(true);
+        }
+    }
+    
+    protected void setQueued(boolean queued) {
+        if (logger.isInfoEnabled()) {
+            logger.info(this + " queued");
+        }
+        this.queued = queued;
+    }
+
+    @Override
+    public long getLastTime() {
+        if (queued) {
+            return Long.MAX_VALUE;
+        }
+        else {
+            return super.getLastTime();
+        }
+    }
+
     public void error(boolean last, Exception e) {
     	this.errorReceived("Failed to write file data", e);
     }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/ThrottleManager.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/ThrottleManager.java	(revision 0)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/ThrottleManager.java	(revision 3355)
@@ -0,0 +1,140 @@
+//----------------------------------------------------------------------
+//This code is developed as part of the Java CoG Kit project
+//The terms of the license can be found at http://www.cogkit.org/license
+//This message may not be removed or altered.
+//----------------------------------------------------------------------
+
+/*
+ * Created on Oct 14, 2011
+ */
+package org.globus.cog.abstraction.impl.file.coaster.buffers;
+
+import java.util.Stack;
+
+import org.apache.log4j.Logger;
+import org.globus.cog.abstraction.impl.file.coaster.buffers.Buffers.Direction;
+import org.globus.cog.abstraction.impl.file.coaster.handlers.PutFileHandler;
+import org.globus.cog.abstraction.impl.file.coaster.handlers.providers.WriteIOCallback;
+import org.globus.cog.karajan.workflow.service.channels.PerformanceDiagnosticInputStream;
+
+public class ThrottleManager {
+    public static final Logger logger = Logger.getLogger(ThrottleManager.class);
+    
+    private static final ThrottleManager IN = new ThrottleManager(Direction.IN);
+    private static final ThrottleManager OUT = new ThrottleManager(Direction.OUT);
+    
+    public static final int MAX_CONCURRENT_TRANSFERS = 256;
+    public static final double LAMBDA = 3;
+    
+    public static final long MIN_UPDATE_INTERVAL = 500; //ms
+        
+    /*
+     * lambda = 0.2 gives a nice curve like this:
+     * http://www.wolframalpha.com/input/?i=plot+256*exp%28-0.2*%28x%2F%28512-x%2B0.0001%29%29%29%2C+x+%3D+0+to+512
+     */
+    
+    public static ThrottleManager getDefault(Direction dir) {
+        switch (dir) {
+            case IN: return IN;
+            case OUT: return OUT;
+            default: return null;
+        }
+    }
+    
+    private Stack<WriteIOCallback> active, suspended;
+    private long lastTime;
+    private int lastMaxTransfers;
+    private Direction dir;
+    
+    public ThrottleManager(Direction dir) {
+        this.dir = dir;
+        active = new Stack<WriteIOCallback>();
+        suspended = new Stack<WriteIOCallback>();
+        lastTime = System.currentTimeMillis();
+        lastMaxTransfers = MAX_CONCURRENT_TRANSFERS;
+    }
+
+    public void register(WriteIOCallback cb) {
+        synchronized(this) {
+            if (active.size() > lastMaxTransfers) {
+                cb.suspend();
+                // put this at the bottom of the stack, so earlier transfers
+                // get priority
+                suspended.insertElementAt(cb, 0);
+            }
+            else {
+                active.push(cb);
+            }
+        }
+    }
+    
+    public void unregister(WriteIOCallback cb) {
+        synchronized(this) {
+            if (!active.remove(cb)) {
+                suspended.remove(cb);
+            }
+        }
+    }
+    
+    public void update(int maxBuffers, int crtBuffers) {
+        long now = System.currentTimeMillis();
+        if (now - lastTime < MIN_UPDATE_INTERVAL) {
+        	return;
+        }
+        lastTime = now;
+        int allowed = allowedTransfers(maxBuffers, crtBuffers);
+        log(allowed, maxBuffers, crtBuffers);
+        // Use stacks because of the assumption that it's better to have
+        // some transfers prioritized 
+        synchronized(this) {
+            while (active.size() > allowed) {
+                suspendOne();
+            }
+            while (active.size() < allowed && !suspended.isEmpty()) {
+                resumeOne();
+            }
+        }
+    }
+    
+    private void log(int allowed, int maxBuffers, int crtBuffers) {
+        if (logger.isInfoEnabled()) {
+            logger.info(dir + " maxBuffers=" + maxBuffers + ", crtBuffers=" + crtBuffers + 
+                ", allowedTransfers=" + allowed + ", active=" + active.size() + 
+                ", suspended=" + suspended.size());
+            Runtime r = Runtime.getRuntime();
+            if (dir == Direction.OUT) {
+                logger.info("mem=" + PerformanceDiagnosticInputStream.units(r.totalMemory() - r.freeMemory()) + 
+                    "B, heap=" + PerformanceDiagnosticInputStream.units(r.totalMemory()) + 
+                    "B, maxHeap=" + PerformanceDiagnosticInputStream.units(r.maxMemory()) + "B");
+            }
+        }
+    }
+    
+    private void suspendOne() {
+        WriteIOCallback h = active.pop();
+        h.suspend();
+        suspended.push(h);
+    }
+    
+    private void resumeOne() {
+        WriteIOCallback h = suspended.pop();
+        h.resume();
+        active.push(h);
+    }
+
+    private int allowedTransfers(int maxBuffers, int crtBuffers) {
+        // 0 when crtBuffers = maxBuffers
+        // MAX_CONCURRENT_TRANSFERS when crtBuffers = 0
+        // some smooth function in between
+        // the 0.0001 is there to approximate +inf when maxBuffers = crtBuffers.
+        if (maxBuffers < crtBuffers) {
+            return 0;
+        }
+        int allowed = (int) Math.round(MAX_CONCURRENT_TRANSFERS * 
+            Math.exp(-LAMBDA * (crtBuffers/(maxBuffers - crtBuffers + 0.0001))));
+        if (allowed < 0) {
+        	allowed = 0;
+        }
+        return allowed;
+    }
+}
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/InputStreamReadBuffer.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/InputStreamReadBuffer.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/InputStreamReadBuffer.java	(working copy)
@@ -25,14 +25,14 @@
         init();
     }
 
-    public void doStuff(boolean last, ByteBuffer b) {
+    public void doStuff(boolean last, ByteBuffer b, Buffers.Allocation alloc) {
         if (read >= size) {
             return;
         }
+        if (alloc != null) {
+            bufferCreated(alloc);
+        }
         try {
-            if (b == null) {
-                b = allocateOneBuffer();
-            }
             if (b.hasArray()) {
                 int len = is.read(b.array());
                 b.limit(len);
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/OutputStreamWriteBuffer.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/OutputStreamWriteBuffer.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/OutputStreamWriteBuffer.java	(working copy)
@@ -24,7 +24,7 @@
         this.cb = cb;
     }
 
-    public void doStuff(boolean last, ByteBuffer b) {
+    public void doStuff(boolean last, ByteBuffer b, Buffers.Allocation alloc) {
         try {
             os.write(toByteArray(b));
             b.rewind();
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/ReadBufferCallback.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/ReadBufferCallback.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/ReadBufferCallback.java	(working copy)
@@ -15,4 +15,6 @@
     void dataRead(boolean last, ByteBuffer buf);
     
     void error(boolean last, Exception e);
+
+    void queued();
 }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/NIOChannelWriteBuffer.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/NIOChannelWriteBuffer.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/NIOChannelWriteBuffer.java	(working copy)
@@ -24,14 +24,14 @@
         this.cb = cb;
     }
 
-    public void doStuff(boolean last, ByteBuffer b) {
+    public void doStuff(boolean last, ByteBuffer b, Buffers.Allocation alloc) {
         try {
             channel.write(b);
             b.rewind();
-            cb.done(last);
             if (last) {
                 channel.close();
             }
+            cb.done(last);
         }
         catch (IOException e) {
             cb.error(last, e);
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/NIOChannelReadBuffer.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/NIOChannelReadBuffer.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/NIOChannelReadBuffer.java	(working copy)
@@ -13,7 +13,11 @@
 import java.nio.ByteBuffer;
 import java.nio.channels.ScatteringByteChannel;
 
+import org.apache.log4j.Logger;
+
 public class NIOChannelReadBuffer extends ReadBuffer {
+    public static final Logger logger = Logger.getLogger(NIOChannelReadBuffer.class);
+    
     private ScatteringByteChannel channel;
     private long crt;
     private Exception ex;
@@ -25,14 +29,20 @@
         init();
     }
 
-    public void doStuff(boolean last, ByteBuffer b) {
+    public void doStuff(boolean last, ByteBuffer b, Buffers.Allocation alloc) {
         if (read >= size) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Transfer done. De-allocating one unused buffer");
+            }
+            if (alloc != null) {
+                buffers.free(alloc);
+            }
             return;
         }
+        if (alloc != null) {
+            bufferCreated(alloc);
+        }
         try {
-            if (b == null) {
-                b = allocateOneBuffer();
-            }
             channel.read(b);
             b.limit(b.position());
             b.rewind();
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/ReadBuffer.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/ReadBuffer.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/ReadBuffer.java	(working copy)
@@ -11,17 +11,23 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.LinkedList;
+import java.util.List;
 
+import org.apache.log4j.Logger;
 
 
+
 public abstract class ReadBuffer extends Buffer {
+    public static final Logger logger = Logger.getLogger(ReadBuffer.class);
+    
     private final ReadBufferCallback cb;
     protected LinkedList<ByteBuffer> full;
     protected LinkedList<ByteBuffer> empty;
     protected long read;
     protected long size;
-    protected Buffers.Allocation alloc;
+    protected List<Buffers.Allocation> allocs;
 
     protected ReadBuffer(Buffers buffers, ReadBufferCallback cb, long size) {
         super(buffers);
@@ -36,21 +42,25 @@
     protected void init() throws InterruptedException {
         full = new LinkedList<ByteBuffer>();
         empty = new LinkedList<ByteBuffer>();
-        for (int i = 0; i < Buffers.ENTRIES_PER_STREAM; i++) {
+        allocs = new ArrayList<Buffers.Allocation>();
+        
+        int nbuf = Math.min((int) (size / Buffers.ENTRY_SIZE) + 1, Buffers.ENTRIES_PER_STREAM);
+        
+        if (logger.isInfoEnabled()) {
+            logger.info("Will ask for " + nbuf + " buffers for a size of " + size);
+        }
+        
+        for (int i = 0; i < nbuf; i++) {
             // these will be allocated when the first read happens,
             // which also happens to happen in the I/O thread
             empty.add(null);
         }
-        requestFill();
+        if (requestFill() == nbuf) {
+            // all buffers are queued
+            cb.queued();
+        }
     }
     
-    protected ByteBuffer allocateOneBuffer() throws InterruptedException {
-        if (alloc == null) {
-            alloc = buffers.request(Buffers.ENTRIES_PER_STREAM);
-        }
-        return ByteBuffer.allocate(Buffers.ENTRY_SIZE);
-    }
-
     public void freeFirst() {
         ByteBuffer b;
         synchronized (this) {
@@ -61,16 +71,20 @@
         requestFill();
     }
 
-    protected void requestFill() {
+    protected int requestFill() {
+        int queued = 0;
         synchronized (empty) {
             while (!empty.isEmpty() && read < size) {
                 ByteBuffer buf = empty.removeFirst();
                 if (buf != null) {
                     buf.clear();
                 }
-                buffers.queueRequest(false, buf, this);
+                if (buffers.queueRequest(false, buf, this)) {
+                    queued++;
+                }
             }
         }
+        return queued;
     }
 
     public void error(ByteBuffer buf, Exception e) {
@@ -88,8 +102,20 @@
         getCallback().dataRead(read == size, buf);
     }
     
+    protected void bufferCreated(Buffers.Allocation a) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("buffer created");
+        }
+        allocs.add(a);
+    }
+    
     protected void deallocateBuffers() {
-        buffers.free(alloc);
+        if (logger.isInfoEnabled()) {
+            logger.info("De-allocating " + allocs.size() + " buffers");
+        }
+        for (Buffers.Allocation a : allocs) {
+            buffers.free(a);
+        }
     }
     
     public void close() throws IOException {
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/Buffer.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/Buffer.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/Buffer.java	(working copy)
@@ -18,7 +18,7 @@
     	this.buffers = buffers;
     }
 
-    public abstract void doStuff(boolean last, ByteBuffer b);
+    public abstract void doStuff(boolean last, ByteBuffer b, Buffers.Allocation alloc);
 
     public void close() throws IOException {
     }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/Buffers.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/Buffers.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/file/coaster/buffers/Buffers.java	(working copy)
@@ -23,24 +23,51 @@
 
     public static final int ENTRY_SIZE = 32768;
     public static final int ENTRIES_PER_STREAM = 16;
-    public static final int MAX_ENTRIES = 256;
+    public static final int MAX_ENTRIES = 512; // 16 MB
     public static final int PERFORMANCE_LOGGING_INTERVAL = 10000;
+    
+    public static enum Direction {
+        IN("I"), OUT("O");
+        
+        private String name;
+        
+        Direction(String name) {
+            this.name = name;
+        }
 
-    private static final Buffers INSTANCE = new Buffers();
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
 
+    private static final Buffers OUTB = new Buffers(Direction.OUT);
+    private static final Buffers INB = new Buffers(Direction.IN);
+
     private LinkedList<Entry> queue;
+    private LinkedList<Entry> waiting;
+    
     private Object sizeLock = new Object();
     private int crt;
     private long lastTime, bufTime;
     private double avgBuffersUsed;
     private int maxBuffersUsed, minBuffersUsed;
+    private Direction dir;
+    private ThrottleManager throttleManager;
     
-    public Buffers() {
+    public Buffers(Direction dir) {
+        this.dir = dir;
+        this.throttleManager = ThrottleManager.getDefault(dir);
         queue = new LinkedList<Entry>();
+        waiting = new LinkedList<Entry>();
         setName("I/O Queue");
         setDaemon(true);
         start();
     }
+    
+    public ThrottleManager getThrottleManager() {
+        return throttleManager;
+    }
 
     public synchronized void start() {
         super.start();
@@ -55,42 +82,41 @@
         maxBuffersUsed = 0;
     }
 
-    public static ReadBuffer newReadBuffer(ScatteringByteChannel channel, long size,
+    public static ReadBuffer newReadBuffer(Buffers buffers, ScatteringByteChannel channel, long size,
             ReadBufferCallback cb) throws InterruptedException {
-        return new NIOChannelReadBuffer(INSTANCE, channel, size, cb);
+        return new NIOChannelReadBuffer(buffers, channel, size, cb);
     }
 
-    public static ReadBuffer newReadBuffer(InputStream is, long size, ReadBufferCallback cb)
+    public static ReadBuffer newReadBuffer(Buffers buffers, InputStream is, long size, ReadBufferCallback cb)
             throws InterruptedException {
-        return new InputStreamReadBuffer(INSTANCE, is, size, cb);
+        return new InputStreamReadBuffer(buffers, is, size, cb);
     }
 
-    public static WriteBuffer newWriteBuffer(GatheringByteChannel channel, WriteBufferCallback cb) {
-        return new NIOChannelWriteBuffer(INSTANCE, channel, cb);
+    public static WriteBuffer newWriteBuffer(Buffers buffers, GatheringByteChannel channel, WriteBufferCallback cb) {
+        return new NIOChannelWriteBuffer(buffers, channel, cb);
     }
 
-    public static WriteBuffer newWriteBuffer(OutputStream os, WriteBufferCallback cb) {
-        return new OutputStreamWriteBuffer(INSTANCE, os, cb);
+    public static WriteBuffer newWriteBuffer(Buffers buffers, OutputStream os, WriteBufferCallback cb) {
+        return new OutputStreamWriteBuffer(buffers, os, cb);
     }
 
-    public synchronized void queueRequest(boolean last, ByteBuffer buf, Buffer buffer) {
-        queue.add(new Entry(last, buf, buffer));
-        notify();
-    }
-
-    public Allocation request(int count) throws InterruptedException {
-        synchronized (sizeLock) {
-            while (crt + count > MAX_ENTRIES) {
-                sizeLock.wait(1000);
+    public synchronized boolean queueRequest(boolean last, ByteBuffer buf, Buffer buffer) {
+        Entry e = new Entry(last, buf, buffer);
+        if (buf == null && crt > MAX_ENTRIES) {
+            waiting.add(e);
+            return true;
+        }
+        else {
+            queue.add(e);
+            if (buf == null) {
+            	// not a pre-allocated buffer
+            	crt++;
             }
-            updateBuffersUsed();
-            crt += count;
-            Allocation a = new Allocation(count);
-            assert(!a.free);
-            return a;
+            notify();
+            return false;
         }
     }
-
+    
     private void updateBuffersUsed() {
         long time = System.currentTimeMillis();
         avgBuffersUsed += (time - bufTime) * crt;
@@ -102,41 +128,71 @@
         }
         bufTime = time;
     }
-
-    public void free(Allocation alloc) {
-        synchronized (sizeLock) {
-            if (alloc == null) {
-                throw new IllegalArgumentException("Null alloc");
+    
+    public synchronized Allocation request(int count) throws InterruptedException {
+        if (logger.isDebugEnabled()) {
+            logger.debug(dir + " request " + count + ", crt = " + crt + ", max = " + MAX_ENTRIES);
+        }
+        while (crt >= MAX_ENTRIES) {
+            if (logger.isDebugEnabled()) {
+                logger.debug(dir + " max buffers reached. Waiting...");
             }
-            if (alloc.free) {
-                logger.warn("Trying to release buffer allocation twice", new Exception());
-                return;
-            }
-            updateBuffersUsed();
-            crt -= alloc.count;
-            alloc.free();
-            sizeLock.notify();
+            wait();
         }
+        updateBuffersUsed();
+        crt += count;
+        return new Allocation(count);
     }
+    
+    public synchronized void free(Allocation alloc) {
+        if (alloc == null) {
+            throw new IllegalArgumentException("Null alloc");
+        }
+        if (alloc.free) {
+            logger.warn(dir + " trying to release buffer allocation twice", new Exception());
+            return;
+        }
+        if (logger.isDebugEnabled()) {
+            logger.debug(dir + " free " + alloc.count + ", crt = " + crt + ", max = " + MAX_ENTRIES);
+        }
+        updateBuffersUsed();
+        crt -= alloc.count;
+        alloc.free();
+        queueWaiting();
+        notify();
+    }
 
+    private void queueWaiting() {
+        while (!waiting.isEmpty() && crt < MAX_ENTRIES) {
+            queue.add(waiting.removeFirst());
+            crt++;
+        }
+    }
+
     public void run() {
         try {
             while (true) {
                 Entry e;
                 synchronized (this) {
                     while (queue.isEmpty()) {
-                        this.wait();
+                        this.wait(ThrottleManager.MIN_UPDATE_INTERVAL);
+                        throttleManager.update(MAX_ENTRIES, crt);
                     }
                     e = queue.removeFirst();
                 }
                 try {
-                    e.buffer.doStuff(e.last, e.buf);
+                    if (e.buf == null) {
+                        e.buffer.doStuff(e.last, ByteBuffer.allocate(ENTRY_SIZE), new Allocation(1));
+                    }
+                    else {
+                        e.buffer.doStuff(e.last, e.buf, null);
+                    }
                     if (logger.isInfoEnabled()) {
                         long time = System.currentTimeMillis();
                         long dif = time - lastTime;
                         if (dif > PERFORMANCE_LOGGING_INTERVAL) {
                             int avgbuf = (int) (avgBuffersUsed / dif);
-                            logger.info("elapsedTime=" + dif + ", buffersUsed[min,avg,max]="
+                            logger.info(dir + " elapsedTime=" + dif + ", buffersUsed[min,avg,max]="
                                     + minBuffersUsed + ", " + avgbuf + ", " + maxBuffersUsed);
                             resetCounters();
                         }
@@ -176,7 +232,11 @@
         }
     }
 
-    public static Buffers getDefault() {
-        return INSTANCE;
+    public static Buffers getBuffers(Direction dir) {
+        switch(dir) {
+            case IN: return INB;
+            case OUT: return OUTB;
+            default: return null;
+        }
     }
 }
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/JobSubmissionTaskHandler.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/JobSubmissionTaskHandler.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/JobSubmissionTaskHandler.java	(working copy)
@@ -7,7 +7,9 @@
 package org.globus.cog.abstraction.impl.execution.coaster;
 
 import java.io.IOException;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Random;
 import java.util.Set;
 
@@ -45,6 +47,9 @@
     private static Logger logger = Logger.getLogger(JobSubmissionTaskHandler.class);
 
     private static Set<Object> configured, configuring;
+    
+    private static Map<Service, TaskSubmissionException> checkedServices = 
+        new HashMap<Service, TaskSubmissionException>();
 
     static {
         configured = new HashSet<Object>();
@@ -94,6 +99,7 @@
     public void submit(Task task) throws IllegalSpecException, InvalidSecurityContextException,
             InvalidServiceContactException, TaskSubmissionException {
         checkAndSetTask(task);
+        validateTaskSettings();
         task.setStatus(Status.SUBMITTING);
         try {
             KarajanChannel channel = getChannel(task);
@@ -257,7 +263,118 @@
     public void setAutostart(boolean autostart) {
         this.autostart = autostart;
     }
+    
+    private void validateTaskSettings() throws TaskSubmissionException {
+        synchronized (JobSubmissionTaskHandler.class) {
+            Task task = getTask();
+            Service s = task.getService(0);
+            TaskSubmissionException e = checkedServices.get(s);
+            if (e != null) {
+                throw e;
+            }
+            else if (!checkedServices.containsKey(s)) {
+                try {
+                    validateTask();
+                }
+                catch (IllegalArgumentException ee) {
+                    e = new TaskSubmissionException(ee.getMessage());
+                    checkedServices.put(task.getService(0), e);
+                    throw e;
+                }
+            }
+        }
+    }
+    
+    private void validateTask() {
+        checkPositiveInt("slots", 0);
+        checkPositiveInt("maxNodes", 0);
+        checkPositiveInt("maxTime", 0);
+        Integer nodeGranularity = getInt("nodeGranularity");
+        Integer maxNodes = getInt("maxNodes");
+        if (nodeGranularity != null && maxNodes != null) {
+            if (nodeGranularity > maxNodes) {
+                throw new IllegalArgumentException("nodeGranularity > maxNodes (" + 
+                    nodeGranularity + " > " + maxNodes + ")");
+            }
+        }
+        
+        checkGreaterOrEqualThan("lowOverallocation", 1);
+        checkGreaterThan("highOverallocation", 1);
+        
+        checkGreaterThan("allocationStepSize", 0);
+        checkLessOrEqualThan("allocationStepSize", 1);
+        
+        checkGreaterOrEqualThan("spread", 0);
+        checkLessOrEqualThan("spread", 1);
+        
+        checkGreaterOrEqualThan("parallelism", 0);
+        checkLessOrEqualThan("parallelism", 1);
+    }
 
+    private void checkPositiveInt(String name, int i) {
+        Integer v = getInt(name);
+        if (v != null && v <= 0) {
+            throw new IllegalArgumentException(name + " must be > 0 (currently " + v + ")");
+        }
+    }
+
+    private void checkGreaterOrEqualThan(String name, double d) {
+        Double v = getDouble(name);
+        if (v != null && v < d) {
+            throw new IllegalArgumentException(name + " must be >= " + d + " (currently " + v + ")");
+        }
+    }
+
+    private void checkLessOrEqualThan(String name, double d) {
+        Double v = getDouble(name);
+        if (v != null && v > d) {
+            throw new IllegalArgumentException(name + " must be <= " + d + " (currently " + v + ")");
+        }
+    }
+
+    private void checkLessThan(String name, double d) {
+        Double v = getDouble(name);
+        if (v != null && v >= d) {
+            throw new IllegalArgumentException(name + " must be < " + d + " (currently " + v + ")");
+        }
+    }
+
+    private void checkGreaterThan(String name, double d) {
+        Double v = getDouble(name);
+        if (v != null && v <= d) {
+            throw new IllegalArgumentException(name + " must be > " + d + " (currently " + v + ")");
+        }
+    }
+
+    private Double getDouble(String name) {
+        Object v = ((JobSpecification) getTask().getSpecification()).getAttribute(name.toLowerCase());
+        if (v == null) {
+            return null;
+        }
+        else if (v instanceof String) {
+            return Double.parseDouble((String) v);
+        }
+        else if (v instanceof Number) {
+            return ((Number) v).doubleValue();
+        }
+        else {
+            throw new IllegalArgumentException("Invalid valid for " + name + ": " + v + "; must be a floating point number.");
+        }
+    }
+
+    private Integer getInt(String name) {
+        Object v = ((JobSpecification) getTask().getSpecification()).getAttribute(name.toLowerCase());
+        if (v == null) {
+            return null;
+        }
+        else if (v instanceof String) {
+            return Integer.parseInt((String) v);
+        }
+        else {
+            throw new IllegalArgumentException("Invalid valid for " + name + ": " + v + "; must be an integer.");
+        }
+    }
+
     private static Task submitTask() throws Exception {
         Task t = new TaskImpl();
         t.setType(Task.JOB_SUBMISSION);
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/PackageList.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/PackageList.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/PackageList.java	(working copy)
@@ -26,7 +26,6 @@
         if (!dir.exists()) {
             throw new RuntimeException(dir + " does not exist");
         }
-        add("backport-util-concurrent.jar");
         add("cog-abstraction-common-*.jar");
         add("cog-jglobus-*.jar");
         add("cog-karajan-*.jar");
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/ServiceManager.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/ServiceManager.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/ServiceManager.java	(working copy)
@@ -226,6 +226,9 @@
             String msg =
                     "Coaster service ended. Reason: " + s.getMessage() + "\n\tstdout: "
                             + t.getStdOutput() + "\n\tstderr: " + t.getStdError();
+            if (logger.isInfoEnabled()) {
+                logger.info(msg);
+            }
             NotificationManager.getDefault().serviceTaskEnded(contact, msg);
             try {
                 if (url != null) {
@@ -431,7 +434,7 @@
                             ChannelManager.getManager().reserveChannel(url, (GSSCredential) cred);
                     logger.debug("Got channel " + channel);
                     ServiceShutdownCommand ssc = new ServiceShutdownCommand();
-                    ssc.setReplyTimeout(10000);
+                    ssc.setTimeout(10000);
                     ssc.setMaxRetries(0);
                     ssc.executeAsync(channel, this);
                     ChannelManager.getManager().releaseChannel(channel);
Index: modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/bootstrap/Bootstrap.java
===================================================================
--- modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/bootstrap/Bootstrap.java	(revision 3354)
+++ modules/provider-coaster/src/org/globus/cog/abstraction/impl/execution/coaster/bootstrap/Bootstrap.java	(working copy)
@@ -17,7 +17,6 @@
 import java.lang.reflect.Method;
 import java.net.URL;
 import java.net.URLClassLoader;
-import java.security.MessageDigest;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -25,10 +24,7 @@
 import java.util.Iterator;
 import java.util.List;
 
-import org.globus.cog.abstraction.impl.execution.coaster.BootstrapService;
-import org.globus.cog.abstraction.impl.execution.coaster.ServiceManager;
 
-
 public class Bootstrap {
 
     public static final boolean fork = true;
@@ -53,14 +49,14 @@
     private String serviceURL;
     private String registrationURL;
     private String serviceId;
-    private List list;
+    private List<String[]> list;
 
     public Bootstrap(String serviceURL,
             String registrationURL, String serviceId) {
         this.serviceURL = serviceURL;
         this.registrationURL = registrationURL;
         this.serviceId = serviceId;
-        list = new ArrayList();
+        list = new ArrayList<String[]>();
         logger = new Logger(serviceId);
     }
 
@@ -105,9 +101,9 @@
         if (!CACHE_DIR.mkdirs() && !CACHE_DIR.exists()) {
             error("Could not create jar cache directory");
         }
-        Iterator i = list.iterator();
+        Iterator<String[]> i = list.iterator();
         while (i.hasNext()) {
-            String[] jar = (String[]) i.next();
+            String[] jar = i.next();
             File f = new File(CACHE_DIR, buildName(jar));
             if (!f.exists()) {
                 download(CACHE_DIR, jar[0], jar[1]);
@@ -155,9 +151,9 @@
 
     private void arrangeJars() {
         String[] coasterJar = null;
-        Iterator i = list.iterator();
+        Iterator<String[]> i = list.iterator();
         while (i.hasNext()) {
-            coasterJar = (String[]) i.next();
+            coasterJar = i.next();
             if (coasterJar[0].indexOf("provider-coaster") != -1) {
                 i.remove();
                 break;
@@ -173,23 +169,24 @@
         logger.log("Forking service");
         StringBuffer sb = new StringBuffer();
         arrangeJars();
-        Iterator i = list.iterator();
+        Iterator<String[]> i = list.iterator();
         while (i.hasNext()) {
             sb.append(CACHE_DIR.getAbsolutePath());
             sb.append('/');
-            sb.append(buildName((String[]) i.next()));
+            sb.append(buildName(i.next()));
             if (i.hasNext()) {
                 sb.append(':');
             }
         }
         String java = System.getProperty("java");
-        List args = new ArrayList();
+        List<String> args = new ArrayList<String>();
         args.add("nice");
         args.add("-n");
         args.add("2");
         args.add(java);
         addDebuggingOptions(args);
         args.add("-Xmx256M");
+        args.add("-Dtcp.channel.log.io.performance=true");
         //args.add("-agentlib:hprof=file=c.hprof");
         addProperties(args);
         args.add("-cp");
@@ -198,8 +195,7 @@
         args.add(registrationURL);
         args.add(serviceId);
         logger.log("Args: " + args);
-        Process p = Runtime.getRuntime().exec(
-                (String[]) args.toArray(new String[0]));
+        Process p = Runtime.getRuntime().exec(args.toArray(new String[0]));
         StringBuffer out = new StringBuffer(), err = new StringBuffer();
         logger.log("Starting stdout consumer");
         consumeOutput(p.getInputStream(), out);
@@ -223,13 +219,13 @@
         }
     }
 
-    private void addDebuggingOptions(List args) {
+    private void addDebuggingOptions(List<String> args) {
         //args.add("-Xdebug");
         //args.add("-Xrunjdwp:transport=dt_socket,address=8788,server=y,suspend=y");
         //args.add("-Xrunjdwp:transport=dt_socket,address=8788,server=y,suspend=n");
     }
 
-    private void addProperties(List args) {
+    private void addProperties(List<String> args) {
         addProperty(args, "X509_USER_PROXY");
         addProperty(args, "GLOBUS_HOSTNAME");
         addProperty(args, "GLOBUS_TCP_PORT_RANGE");
@@ -237,7 +233,7 @@
         args.add("-Djava.security.egd=file:///dev/urandom");
     }
 
-    private void addProperty(List args, String name) {
+    private void addProperty(List<String> args, String name) {
         String value = System.getProperty(name);
         if (value != null && !value.equals("")) {
             args.add("-D" + name + "=" + value);
@@ -266,14 +262,14 @@
         URL[] urls = new URL[list.size()];
         for (int i = 0; i < list.size(); i++) {
             urls[i] = new URL("file://" + CACHE_DIR.getAbsolutePath() + "/"
-                    + buildName((String[]) list.get(i)));
+                    + buildName(list.get(i)));
             System.err.println(urls[i]);
         }
         ClassLoader cl = new URLClassLoader(urls, Bootstrap.class
                 .getClassLoader());
-        Class cls = cl.loadClass(SERVICE_CLASS);
+        Class<?> cls = cl.loadClass(SERVICE_CLASS);
 
-        Method m = cls.getMethod("main", new Class[] { String[].class });
+        Method m = cls.getMethod("main", new Class<?>[] { String[].class });
         m.invoke(null, new Object[] { new String[] { registrationURL,
                 serviceId } });
     }
Index: modules/provider-coaster/resources/worker.pl
===================================================================
--- modules/provider-coaster/resources/worker.pl	(revision 3354)
+++ modules/provider-coaster/resources/worker.pl	(working copy)
@@ -15,6 +15,7 @@
 use File::Path;
 use File::Copy;
 use Getopt::Std;
+use FileHandle;
 use Cwd;
 use POSIX ":sys_wait_h";
 use strict;
@@ -63,6 +64,14 @@
 	YIELD => 1,
 };
 
+use constant {
+	PUT_START => 0,
+	PUT_CMD_SENT => 1,
+	PUT_SIZE_SENT => 2,
+	PUT_LNAME_SENT => 3,
+	PUT_SENDING_DATA => 4,
+};
+
 my $LOGLEVEL = NONE;
 
 my @LEVELS = ("TRACE", "DEBUG", "INFO ", "WARN ", "ERROR");
@@ -80,7 +89,8 @@
 	REPLY_FLAG => 0x00000001,
 	FINAL_FLAG => 0x00000002,
 	ERROR_FLAG => 0x00000004,
-	PROGRESSIVE_FLAG => 0x00000008
+	PROGRESSIVE_FLAG => 0x00000008,
+	SIGNAL_FLAG => 0x00000010,
 };
 
 use constant {
@@ -91,12 +101,13 @@
 	STAGEOUT => 0x11,
 };
 
-my $TAG = 0;
+my $TAG = int(rand(10000));
 use constant RETRIES => 3;
 use constant REPLYTIMEOUT => 180;
 use constant MAXFRAGS => 16;
 # TODO: Make this configurable (#537)
 use constant MAX_RECONNECT_ATTEMPTS => 3;
+use constant NEVER => 9999999999;
 
 use constant JOB_CHECK_SKIP => 32;
 
@@ -165,6 +176,55 @@
 # REPLIES stores the state of (outgoing) commands for which replies are expected
 my %REPLIES  = ();
 
+my %SUSPENDED_TRANSFERS = ();
+
+# the structure of the above maps is (fields marked with "*" are optional):
+#	tag: [state, time]
+#
+#	state: {} - valid keys:
+#		tag: the current command/request tag
+#		dataIn: proc(state, tag, timeout, err, fin, msg) - invoked when data is received
+#		nextData: proc(state) - invoked to get the next data chunk
+#								returns: (flags, data, yieldFlag), where
+#									flags: the protocol flags to send (e.g. err, fin)
+#									data: the actual data
+#									yieldFlag: if CONTINUE then it instructs the sending procedure
+#												to loop sending data until YIELD is returned  
+#
+#		dataSent: proc(state, tag) - invoked when all data was sent
+#		PUT file specific state:
+#			state: a numeric state number:
+#				0 - new request
+#				1 - command sent
+#				2 - file size sent
+#				3 - local name sent (i.e. sending data)
+#			size: file size
+#			lname: local file name
+#			rname: remote file name
+#			sent: total bytes sent from this file
+#			bindex: block index - multiple I/O buffer size worth of data are sent
+#								  before yielding to other commands/requests (up to IOBLOCKSZ).
+#								  This number counts how many buffer sizes in the current block have
+#								  been sent so far.
+#			handle: file handle
+#
+#		GET file specific state:
+#			state:
+#				0 - new request
+#				1 - size received
+#			handle: file handle
+#			size: file size
+#			lname: local file name
+#
+#		state when sending array data:
+#			index: the current index in the data array
+#			data: an array containing the data chunks
+#			
+#		 
+#	
+#	time:  last communication time (used to determine timeouts)
+#
+
 my $LOG = logfilename($LOGDIR, $BLOCKID);
 
 my %HANDLERS = (
@@ -246,11 +306,10 @@
 }
 
 sub timestring() {
-	# TODO: Make this choice configurable (#541)
-	my $t = sprintf("%.3f", time());
-	# my @d = localtime(time());
-	# my $t = sprintf("%i/%02i/%02i %02i:%02i",
-	# 				$d[5]+1900, $d[4]+1, $d[3], $d[2], $d[1]);
+	#my $t = sprintf("%.3f", time());
+	my $now = time();
+	my @d = localtime($now);
+        my $t = sprintf("%i/%02i/%02i %02i:%02i:%02i.%03i", $d[5]+1900, $d[4]+1, $d[3], $d[2], $d[1], $d[0], ($now*1000) % 1000);
 	return $t;
 }
 
@@ -333,9 +392,7 @@
 			print "LOG: $LOG\n";
 		}
 		open(LOG, ">>$LOG") or die "Failed to open log file ($LOG): $!";
-		my $b = select(LOG);
-		$| = 1;
-		select($b);
+		LOG->autoflush(1);
 		my $date = localtime;
 		wlog INFO, "$BLOCKID Logging started: $date\n";
 	}
@@ -383,7 +440,7 @@
 sub sendm {
 	my ($tag, $flags, $msg) = @_;
 	my $len = length($msg);
-	my $buf = pack("VVV", $tag, $flags, $len);
+	my $buf = pack("VVVVV", $tag, $flags, $len, ($tag ^ $flags ^ $len), 0);
 	$buf = $buf.$msg;
 
 	wlog(DEBUG, "OUT: len=$len, tag=$tag, flags=$flags\n");
@@ -401,24 +458,28 @@
 	my $flg2;
 	my $msg;
 	my $yield;
-	if (defined($$data{"tag"})) {
-		$tag = $$data{"tag"};
-	}
+	
 	do {
 		($flg2, $msg, $yield) = $$data{"nextData"}($data);
-		sendm($tag, $flg | $flg2, $msg);
+		if (defined($msg)) {
+			sendm($tag, $flg | $flg2, $msg);
+		}
 	} while (($flg2 & FINAL_FLAG) == 0 && !$yield);
 
 	if (($flg2 & FINAL_FLAG) == 0) {
 		# final flag not set; put it back in the queue
-		wlog DEBUG, "$tag yielding\n";
-		$$data{"tag"} = $tag;
+		wlog TRACE, "$tag yielding\n";
+		
+		# update last time
+		my $record = $REPLIES{$tag};
+		$$record[1] = time();
+		
 		queueCmdCustomDataHandling($REPLIES{$tag}, $data);
 	}
 	else {
 		if (exists($REPLIES{$tag})) {
 			my $record = $REPLIES{$tag};
-			my ($cont, $start) = ($$record[0], $$record[1]);
+			my ($cont, $lastTime) = ($$record[0], $$record[1]);
 			if (defined($$cont{"dataSent"})) {
 				$$cont{"dataSent"}($cont, $tag);
 			}
@@ -451,38 +512,48 @@
 	my ($state) = @_;
 
 	my $s = $$state{"state"};
-	if ($s == 0) {
+	
+	my $tag = $$state{"tag"};
+	
+	wlog TRACE, "$tag nextFileData state=$s\n";
+	
+	if ($s == PUT_START) {
 		$$state{"state"} = $s + 1;
 		return (0, $$state{"cmd"}, CONTINUE);
 	}
-	elsif ($s == 1) {
+	elsif ($s == PUT_CMD_SENT) {
 		$$state{"state"} = $s + 1;
 		return (0, pack("VV", $$state{"size"}, 0), CONTINUE);
 	}
-	elsif ($s == 2) {
+	elsif ($s == PUT_SIZE_SENT) {
 		$$state{"state"} = $s + 1;
 		return (0, $$state{"lname"}, CONTINUE);
 	}
-	elsif ($s == 3) {
+	elsif ($s == PUT_LNAME_SENT) {
 		$$state{"state"} = $s + 1;
 		$$state{"sent"} = 0;
 		$$state{"bindex"} = 0;
 		return ($$state{"size"} == 0 ? FINAL_FLAG : 0, $$state{"rname"}, CONTINUE);
 	}
-	else {
+	elsif ($s == PUT_SENDING_DATA) {
+		if (defined $SUSPENDED_TRANSFERS{"$tag"}) {
+			wlog TRACE, "$tag Transfer suspendend; yielding\n";
+			return (0, undef, YIELD);
+		}
+	
 		my $handle = $$state{"handle"};
 		my $buffer;
 		my $sz = read($handle, $buffer, IOBUFSZ);
 		if (!defined $sz) {
-			wlog INFO, "Failed to read data from file: $!\n";
+			wlog INFO, "$tag Failed to read data from file: $!\n";
 			return (FINAL_FLAG + ERROR_FLAG, "$!", CONTINUE);
 		}
 		elsif ($sz == 0 && $$state{"sent"} < $$state{"size"}) {
-			wlog INFO, "File size mismatch. $$state{'size'} vs. $$state{'sent'}\n";
+			wlog INFO, "$tag File size mismatch. $$state{'size'} vs. $$state{'sent'}\n";
 			return (FINAL_FLAG + ERROR_FLAG, "File size mismatch. Expected $$state{'size'}, got $$state{'sent'}", CONTINUE);
 		}
 		$$state{"sent"} += $sz;
-		wlog DEBUG, "size: $$state{'size'}, sent: $$state{'sent'}\n";
+		wlog DEBUG, "$tag size: $$state{'size'}, sent: $$state{'sent'}\n";
 		if ($$state{"sent"} == $$state{"size"}) {
 			close $handle;
 		}
@@ -523,6 +594,8 @@
 	if (!defined $ctag) {
 		$ctag =  $TAG++;
 		registerCmd($ctag, $cont);
+		# make the tag accessible to the data generator
+		$$state{"tag"} = $ctag;
 	}
 	sendFrags($ctag, 0, $state);
 	return $ctag;
@@ -537,6 +610,7 @@
 sub queueCmd {
 	my @cmd = @_;
 	my $cont = shift(@cmd);
+	# $cont is the continuation (what gets called when a reply is received)
 	push @CMDQ, [$cont, arrayData(@cmd)];
 }
 
@@ -559,13 +633,23 @@
 	my ($data) = @_;
 
 	my $lendata = length($data);
-	if ($lendata < 12) {
-		wlog WARN, "Received faulty message (length < 12: $lendata)\n";
-		die "Received faulty message (length < 12: $lendata)";
+	if ($lendata < 20) {
+		wlog WARN, "Received faulty message (length < 20: $lendata)\n";
+		die "Received faulty message (length < 20: $lendata)";
 	}
 	my $tag = unpack("V", substr($data, 0, 4));
 	my $flg = unpack("V", substr($data, 4, 4));
 	my $len = unpack("V", substr($data, 8, 4));
+	my $hcsum = unpack("V", substr($data, 12, 4));
+	my $csum = unpack("V", substr($data, 16, 4));
+	
+	my $chcsum = ($tag ^ $flg ^ $len);
+	
+	if ($chcsum != $hcsum) {
+		wlog WARN, "Header checksum failed. Computed checksum: $chcsum, checksum: $hcsum\n";
+		return;
+	}
+	
 	my $msg;
 	my $frag;
 	my $alen = 0;
@@ -574,7 +658,7 @@
 		$alen = $alen + length($frag);
 		$msg = $msg.$frag;
 	}
-
+	
 	my $actuallen = length($msg);
 	wlog(TRACE, " IN: len=$len, actuallen=$actuallen, tag=$tag, flags=$flg, $msg\n");
 	if ($len != $actuallen) {
@@ -584,7 +668,7 @@
 }
 
 sub processRequest {
-	my ($state, $tag, $timeout, $err, $fin, $msg) = @_;
+	my ($state, $tag, $timeout, $flags, $msg) = @_;
 
 	my $request = $$state{"request"};
 	if (!defined($request)) {
@@ -596,7 +680,7 @@
 	if ($timeout) {
 		sendError($tag, ("Timed out waiting for all fragments"));
 	}
-	elsif (!$fin) {
+	elsif (!($flags & FINAL_FLAG)) {
 		return;
 	}
 	else {
@@ -617,15 +701,17 @@
 
 
 	my $reply = $flg & REPLY_FLAG;
-	my ($record, $cont, $start);
+	my ($record, $cont, $lastTime);
 
 	if ($reply) {
 		if (exists($REPLIES{$tag})) {
 			$record = $REPLIES{$tag};
-			($cont, $start) = ($$record[0], $$record[1]);
+			($cont, $lastTime) = ($$record[0], $$record[1]);
+			# update last time 
+			$$record[1] = time();
 		}
 		else {
-			wlog(WARN, "received reply to unregistered command (tag=$tag). Discarding.\n");
+			wlog(WARN, "received reply to unregistered command (tag=$tag, msg=$msg). Discarding.\n");
 			return;
 		}
 	}
@@ -635,7 +721,7 @@
 			wlog DEBUG, "New request ($tag)\n";
 		}
 		$record = $REQUESTS{$tag};
-		($cont, $start) = ($$record[0], $$record[1]);
+		($cont, $lastTime) = ($$record[0], $$record[1]);
 	}
 
 	my $fin = $flg & FINAL_FLAG;
@@ -658,7 +744,7 @@
 		wlog DEBUG, "Fin flag set\n";
 	}
 
-	$$cont{"dataIn"}($cont, $tag, 0, $err, $fin, $msg);
+	$$cont{"dataIn"}($cont, $tag, 0, $flg, $msg);
 
 	return 1;
 }
@@ -702,10 +788,10 @@
 sub recvOne {
 	my $buf;
 	$SOCK->blocking(0);
-	$SOCK->recv($buf, 12 - length($DATA));
+	$SOCK->recv($buf, 20 - length($DATA));
 	if (length($buf) > 0) {
 		$DATA = $DATA . $buf;
-		if (length($DATA) == 12) {
+		if (length($DATA) == 20) {
 			# wlog DEBUG, "Received " . unpackData($DATA) . "\n";
 			eval { process(unpackData($DATA)); } || (wlog ERROR, "Failed to process data: $@\n" && die "Failed to process data: $@");
 			$DATA = "";
@@ -744,7 +830,7 @@
 	# things may be added to it while stuff is being sent
 	my $sz = scalar(@CMDQ);
 	for (my $i = 0; $i < $sz; $i++)  {
-		$cmd = pop(@CMDQ);
+		$cmd = shift(@CMDQ);
 		sendCmdInt(@$cmd);
 	}
 	checkJobs();
@@ -774,12 +860,12 @@
 }
 
 sub registerCBDataIn {
-	my ($state, $tag, $timeout, $err, $fin, $reply) = @_;
+	my ($state, $tag, $timeout, $flags, $reply) = @_;
 
 	if ($timeout) {
 		die "Failed to register (timeout)\n";
 	}
-	elsif ($err) {
+	elsif ($flags & ERROR_FLAG) {
 		die "Failed to register (service returned error: ".join("\n", $reply).")";
 	}
 	else {
@@ -795,7 +881,7 @@
 }
 
 sub heartbeatCBDataIn {
-	my ($state, $tag, $timeout, $err, $fin, $reply) = @_;
+	my ($state, $tag, $timeout, $flags, $reply) = @_;
 
 	if ($timeout) {
 		if (time() - $LAST_HEARTBEAT > 2 * HEARTBEAT_INTERVAL) {
@@ -803,7 +889,7 @@
 			die "Lost heartbeat\n";
 		}
 	}
-	elsif ($err) {
+	elsif ($flags & ERROR_FLAG) {
 		wlog WARN, "Heartbeat failed: $reply\n";
 		die "Heartbeat failed: $reply\n";
 	}
@@ -851,7 +937,8 @@
 sub heartbeat {
 	my ($tag, $timeout, $msgs) = @_;
 	$LAST_HEARTBEAT = time();
-	sendReply($tag, ("OK"));
+	my $msg = int(time() * 1000);
+	sendReply($tag, ("$msg"));
 }
 
 sub workershellcmd {
@@ -928,8 +1015,8 @@
 		mkfdir($jobid, $dst);
 		# don't try open(DESC, ...) (as I did). It will use the same reference
 		# and concurrent operations will fail.
-		my $desc;
-		if (!open($desc, ">", "$dst")) {
+		my $handle;
+		if (!open($handle, ">", "$dst")) {
 			die "Failed to open $dst: $!";
 		}
 		else {
@@ -939,7 +1026,7 @@
 				"dataIn" => \&getFileCBDataIn,
 				"state" => 0,
 				"lfile" => $dst,
-				"desc" => $desc
+				"handle" => $handle
 			};
 		}
 	}
@@ -953,11 +1040,12 @@
 }
 
 sub getFileCBDataInIndirect {
-	my ($state, $tag, $timeout, $err, $fin, $reply) = @_;
+	my ($state, $tag, $timeout, $flags, $reply) = @_;
 
 	my $jobid = $$state{"jobid"};
-	wlog DEBUG, "$jobid getFileCBDataInIndirect jobid: $jobid, tag: $tag, err: $err, fin: $fin\n";
-	if ($err) {
+	wlog DEBUG, "$jobid getFileCBDataInIndirect jobid: $jobid, tag: $tag, flags: $flags\n";
+	if ($flags & ERROR_FLAG) {
+		wlog DEBUG, "$jobid getFileCBDataInIndirect error: $reply\n";
 		queueCmd((nullCB(), "JOBSTATUS", $jobid, FAILED, "520", "Error staging in file: $reply"));
 		delete($JOBDATA{$jobid});
 		return;
@@ -967,19 +1055,28 @@
 		delete($JOBDATA{$jobid});
 		return;
 	}
-	if ($fin) {
+	if ($flags & FINAL_FLAG) {
 		stagein($jobid);
 	}
 }
 
 
 sub getFileCBDataIn {
-	my ($state, $tag, $timeout, $err, $fin, $reply) = @_;
+	my ($state, $tag, $timeout, $flags, $reply) = @_;
 
 	my $s = $$state{"state"};
 	my $jobid = $$state{"jobid"};
-	wlog DEBUG, "$jobid getFileCBDataIn jobid: $jobid, state: $s, tag: $tag, err: $err, fin: $fin\n";
-	if ($err) {
+	my $len = length($reply);
+	wlog DEBUG, "$jobid getFileCBDataIn jobid: $jobid, state: $s, tag: $tag, flags: $flags, len: $len\n";
+	
+	if ($flags & SIGNAL_FLAG) {
+		if ($reply eq "QUEUED") {
+			$REPLIES{$tag}[1] = NEVER;
+			wlog DEBUG, "$jobid transfer queued\n";
+		}
+		return;
+	}
+	elsif ($flags & ERROR_FLAG) {
 		wlog DEBUG, "$jobid getFileCBDataIn FAILED 520 Error staging in file: $reply\n";
 		queueCmd((nullCB(), "JOBSTATUS", $jobid, FAILED, "520", "Error staging in file: $reply"));
 		delete($JOBDATA{$jobid});
@@ -993,21 +1090,22 @@
 	elsif ($s == 0) {
 		$$state{"state"} = 1;
 		$$state{"size"} = unpack("V", $reply);
+		wlog DEBUG, "$tag $jobid got file size: $$state{'size'}\n";
 		my $lfile = $$state{"lfile"};
 	}
 	else {
-		my $desc = $$state{"desc"};
-		if (!(print {$desc} $reply)) {
-			close $desc;
-			wlog DEBUG, "$jobid Could not write to file: $!. Descriptor was $desc; lfile: $$state{'lfile'}\n";
+		my $handle = $$state{"handle"};
+		if (!(print {$handle} $reply)) {
+			close $handle;
+			wlog DEBUG, "$jobid Could not write to file: $!. Descriptor was $handle; lfile: $$state{'lfile'}\n";
 			queueCmd((nullCB(), "JOBSTATUS", $jobid, FAILED, "522", "Could not write to file: $!"));
 			delete($JOBDATA{$jobid});
 			return;
 		}
 	}
-	if ($fin) {
-		my $desc = $$state{"desc"};
-		close $desc;
+	if ($flags & FINAL_FLAG) {
+		my $handle = $$state{"handle"};
+		close $handle;
 		wlog DEBUG, "$jobid Closed $$state{'lfile'}\n";
 		if ($PINNED_READY) {
 			completePinnedFile($jobid);
@@ -1220,6 +1318,13 @@
 			wlog DEBUG, "$jobid Staging out $lfile (mode = $mode).\n";
 			my ($protocol, $host, $path) = urisplit($rfile);
 			if ($protocol eq "file" || $protocol eq "proxy") {
+				# make sure we keep track of the total number of actual stageouts
+				if (!defined $JOBDATA{$jobid}{"stageoutCount"}) {
+					$JOBDATA{$jobid}{"stageoutCount"} = 0;
+				}
+				$JOBDATA{$jobid}{"stageoutCount"} += 1;
+				wlog DEBUG, "$jobid Stagecount is $JOBDATA{$jobid}{stageoutCount}\n"; 
+				
 				queueCmdCustomDataHandling(putFileCB($jobid), fileData("PUT", $lfile, $rfile));
 			}
 			elsif ($protocol eq "sfs") {
@@ -1254,16 +1359,34 @@
 	}
 }
 
+sub sendStatus {
+	my ($jobid) = @_;
+	
+	my $ec = $JOBDATA{$jobid}{"exitcode"};
+	
+	if ($ec == 0) {
+		queueCmd((nullCB(), "JOBSTATUS", $jobid, COMPLETED, "0", ""));
+	}
+	else {
+		queueCmd((nullCB(), "JOBSTATUS", $jobid, FAILED, "$ec", "Job failed with an exit code of $ec"));
+	}
+}
+
 sub cleanup {
 	my ($jobid) = @_;
 
 	my $ec = $JOBDATA{$jobid}{"exitcode"};
 	if (ASYNC) {
-		if ($ec == 0) {
-			queueCmd((nullCB(), "JOBSTATUS", $jobid, COMPLETED, "0", ""));
+		if (!defined($JOBDATA{$jobid}{"stageoutCount"}) || ($JOBDATA{$jobid}{"stageoutCount"} == 0)) {
+			# there were no stageouts. Notification can be sent now
+			wlog DEBUG, "$jobid There were no stageouts. Sending notification immediately\n";
+			sendStatus($jobid);
 		}
 		else {
-			queueCmd((nullCB(), "JOBSTATUS", $jobid, FAILED, "$ec", "Job failed with an exit code of $ec"));
+			# there were stageouts. Wait until all are acknowledged
+			# as done by the client. And we keep track of the
+			# count of stageouts that weren't acknowledged in 
+			# $JOBDATA{$jobid}{"stageoutCount"}
 		}
 	}
 
@@ -1282,7 +1405,7 @@
 				chop $c;
 			}
 			wlog DEBUG, "$jobid Removing $c\n";
-			rmtree($c, {safe => 1, verbose => 0});
+			rmtree($c, 0, 0);
 			wlog DEBUG, "$jobid Removed $c\n";
 		}
 	}
@@ -1311,35 +1434,52 @@
 	my ($state, $tag) = @_;
 
 	if (ASYNC) {
-		wlog DEBUG, "putFileCBDataSent\n";
+		wlog DEBUG, "$tag putFileCBDataSent\n";
 		my $jobid = $$state{"jobid"};
-		if ($jobid != -1) {
-			wlog DEBUG, "Data sent, async is on. Staging out next file\n";
+		if ($jobid != -1) { 
+			wlog DEBUG, "$tag Data sent, async is on. Staging out next file\n";
 			stageout($jobid);
 		}
 	}
 }
 
 sub putFileCBDataIn {
-	my ($state, $tag, $timeout, $err, $fin, $reply) = @_;
+	my ($state, $tag, $timeout, $flags, $reply) = @_;
 
-	wlog DEBUG, "putFileCBDataIn: $reply\n";
+	wlog DEBUG, "$tag putFileCBDataIn msg=$reply\n";
 
 	my $jobid = $$state{"jobid"};
 
-	if ($err || $timeout) {
+	if (($flags & ERROR_FLAG) || $timeout) {
 		if ($JOBDATA{$jobid}) {
-			wlog DEBUG, "Stage out failed ($reply)\n";
+			wlog DEBUG, "$tag Stage out failed ($reply)\n";
 			queueCmd((nullCB(), "JOBSTATUS", $jobid, FAILED, "515", "Stage out failed ($reply)"));
 			delete($JOBDATA{$jobid});
 		}
 		return;
 	}
+	elsif ($reply eq "STOP") {
+		$SUSPENDED_TRANSFERS{"$tag"} = 1; 
+		wlog DEBUG, "$tag Got stop request. Suspending transfer.\n";
+	}
+	elsif ($reply eq "CONTINUE") {
+		delete $SUSPENDED_TRANSFERS{"$tag"};
+		wlog DEBUG, "$tag Got continue request. Resuming transfer.\n";
+	}
 	elsif ($jobid != -1) {
+		# OK reply from client
 		if (!ASYNC) {
-			wlog DEBUG, "Stageout done; staging out next file\n";
+			wlog DEBUG, "$tag Stageout done; staging out next file\n";
 			stageout($jobid);
 		}
+		else {
+			wlog DEBUG, "$jobid Stageout done; stagecount is $JOBDATA{$jobid}{stageoutCount}\n";
+			$JOBDATA{$jobid}{"stageoutCount"} -= 1;
+			if ($JOBDATA{$jobid}{"stageoutCount"} == 0) {
+				wlog DEBUG, "$jobid All stageouts acknowledged. Sending notification\n";
+				sendStatus($jobid);
+			}
+		}
 	}
 }
 
@@ -1492,21 +1632,24 @@
 	}
 	$JOBDATA{$JOBID}{"jobslot"} = $JOBSLOT;
 
-	pipe(PARENT_R, CHILD_W);
+	my ($PARENT_R, $CHILD_W);
+	pipe($PARENT_R, $CHILD_W);
+	
 	$pid = fork();
+
 	if (defined($pid)) {
 		if ($pid == 0) {
-			close PARENT_R;
-			runjob(\*CHILD_W, $JOB, $JOBARGS, $JOBENV, $JOBSLOT, $WORKERPID);
-			close CHILD_W;
+			close $PARENT_R;
+			runjob($CHILD_W, $JOB, $JOBARGS, $JOBENV, $JOBSLOT, $WORKERPID);
+			close $CHILD_W;
 		}
 		else {
 			wlog DEBUG, "$JOBID Forked process $pid. Waiting for its completion\n";
-			close CHILD_W;
+			close $CHILD_W;
 			$JOBS_RUNNING++;
 			$JOBWAITDATA{$JOBID} = {
 				pid => $pid,
-				pipe => \*PARENT_R
+				pipe => $PARENT_R
 				};
 			if ($PROFILE) {
 				push(@PROFILE_EVENTS, "FORK", $pid, time());
Index: modules/provider-coaster/resources/log4j.properties
===================================================================
--- modules/provider-coaster/resources/log4j.properties	(revision 3354)
+++ modules/provider-coaster/resources/log4j.properties	(working copy)
@@ -27,3 +27,5 @@
 #log4j.logger.org.globus.cog.karajan.workflow.service.channels.AbstractKarajanChannel=WARN
 log4j.logger.org.globus.cog.abstraction.coaster=INFO
 log4j.logger.org.globus.cog.abstraction.impl.common.task.TaskImpl=DEBUG
+log4j.logger.org.globus.cog.karajan.workflow.service.channels.PerformanceDiagnosticInputStream=INFO
+log4j.logger.org.globus.cog.karajan.workflow.service.channels.PerformanceDiagnosticOutputStream=INFO
Index: modules/abstraction-common/.classpath
===================================================================
--- modules/abstraction-common/.classpath	(revision 3354)
+++ modules/abstraction-common/.classpath	(working copy)
@@ -6,7 +6,6 @@
 	<classpathentry kind="lib" path="lib/castor-0.9.6.jar"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/util"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/jglobus"/>
-	<classpathentry exported="true" kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j-1.2.8"/>
-	<classpathentry kind="lib" path="/util/lib/log4j-1.2.8.jar"/>
+	<classpathentry exported="true" kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
 	<classpathentry kind="output" path=".build"/>
 </classpath>
Index: modules/abstraction-common/src/org/globus/cog/abstraction/impl/file/FileFragmentImpl.java
===================================================================
--- modules/abstraction-common/src/org/globus/cog/abstraction/impl/file/FileFragmentImpl.java	(revision 3354)
+++ modules/abstraction-common/src/org/globus/cog/abstraction/impl/file/FileFragmentImpl.java	(working copy)
@@ -17,10 +17,17 @@
     
     public FileFragmentImpl(String file, long offset, long length) {
         this.file = file;
-        this.offset = offset;
-        this.length = length;
+        this.offset = checkPositive(offset, "offset");
+        this.length = checkPositive(length, "length");
     }
     
+    private long checkPositive(long n, String param) {
+        if (n < 0) {
+            throw new IllegalArgumentException(param + " < 0 (" + n + ")");
+        }
+        return n;
+    }
+
     /**
      * Creates a new FileFragment object for the entire specified file
      */
Index: modules/abstraction-common/src/org/globus/cog/abstraction/impl/file/AbstractFileResource.java
===================================================================
--- modules/abstraction-common/src/org/globus/cog/abstraction/impl/file/AbstractFileResource.java	(revision 3354)
+++ modules/abstraction-common/src/org/globus/cog/abstraction/impl/file/AbstractFileResource.java	(working copy)
@@ -135,37 +135,31 @@
         return this.attributes.get(name);
     }
 
-    @Override
     public void getFile(String remoteFileName, String localFileName)
             throws FileResourceException {
         getFile(new FileFragmentImpl(remoteFileName), new FileFragmentImpl(localFileName));
     }
 
-    @Override
     public void getFile(String remoteFileName, String localFileName,
             ProgressMonitor progressMonitor) throws FileResourceException {
         getFile(new FileFragmentImpl(remoteFileName), new FileFragmentImpl(localFileName), progressMonitor);
     }
 
-    @Override
     public void getFile(FileFragment remote, FileFragment local)
             throws FileResourceException {
         getFile(remote, local, null);
     }
 
-    @Override
     public void putFile(String localFileName, String remoteFileName)
             throws FileResourceException {
         putFile(new FileFragmentImpl(localFileName), new FileFragmentImpl(remoteFileName));
     }
 
-    @Override
     public void putFile(String localFileName, String remoteFileName,
             ProgressMonitor progressMonitor) throws FileResourceException {
         putFile(new FileFragmentImpl(localFileName), new FileFragmentImpl(remoteFileName), progressMonitor);
     }
 
-    @Override
     public void putFile(FileFragment local, FileFragment remote)
             throws FileResourceException {
         putFile(local, remote, null);
@@ -386,7 +380,6 @@
         return "FileResource: " + name;
     }
 
-    @Override
     public void thirdPartyTransfer(FileResource sourceResource,
             FileFragment source, FileFragment destination)
             throws FileResourceException {
Index: modules/abstraction-common/src/org/globus/cog/abstraction/impl/common/task/TaskImpl.java
===================================================================
--- modules/abstraction-common/src/org/globus/cog/abstraction/impl/common/task/TaskImpl.java	(revision 3354)
+++ modules/abstraction-common/src/org/globus/cog/abstraction/impl/common/task/TaskImpl.java	(working copy)
@@ -183,7 +183,7 @@
             }
         }
         finally {
-            outputListeners.release();
+            outputListeners.release(i);
         }
     }
 
@@ -241,7 +241,7 @@
             }
         }
         finally {
-            statusListeners.release();
+            statusListeners.release(i);
         }
         synchronized (this) {
             if (anythingWaiting) {
Index: modules/abstraction-common/src/org/globus/cog/abstraction/impl/common/task/JobSpecificationImpl.java
===================================================================
--- modules/abstraction-common/src/org/globus/cog/abstraction/impl/common/task/JobSpecificationImpl.java	(revision 3354)
+++ modules/abstraction-common/src/org/globus/cog/abstraction/impl/common/task/JobSpecificationImpl.java	(working copy)
@@ -166,7 +166,6 @@
     }
 
 
-    @Override
     public void addEnvironmentVariable(String name, int i) {
         addEnvironmentVariable(name, Integer.toString(i));
     }
@@ -469,7 +468,6 @@
         return result;
     }
 
-    @Override
     public void unpackProviderAttributes() {
         String attrs = (String) getAttribute("providerAttributes");
 
Index: modules/abstraction-common/src/org/globus/cog/abstraction/impl/common/execution/WallTime.java
===================================================================
--- modules/abstraction-common/src/org/globus/cog/abstraction/impl/common/execution/WallTime.java	(revision 3354)
+++ modules/abstraction-common/src/org/globus/cog/abstraction/impl/common/execution/WallTime.java	(working copy)
@@ -12,15 +12,16 @@
 import java.util.HashMap;
 import java.util.Map;
 
-public class WallTime implements Comparable {
-    private static final Map FORMATTERS;
+public class WallTime implements Comparable<WallTime> {
+    private static final Map<String, Formatter> FORMATTERS;
     private static final Formatter DEFAULT_FORMATTER = new DefaultFormatter();
 
     static {
-        FORMATTERS = new HashMap();
+        FORMATTERS = new HashMap<String, Formatter>();
         FORMATTERS.put(null, DEFAULT_FORMATTER);
         FORMATTERS.put("default", DEFAULT_FORMATTER);
         FORMATTERS.put("pbs", new PBSFormatter());
+        FORMATTERS.put("hms", new HHMMSSFormatter());
         FORMATTERS.put("globus-jobmanager-pbs", new PBSFormatter());
         FORMATTERS.put("pbs-native", new NativePBSFormatter());
         FORMATTERS.put("sge-native", new NativeSGEFormatter());
@@ -30,7 +31,7 @@
         if (type != null) {
             type = type.toLowerCase();
         }
-        Formatter f = (Formatter) FORMATTERS.get(type);
+        Formatter f = FORMATTERS.get(type);
         return f == null ? DEFAULT_FORMATTER : f;
     }
 
@@ -74,11 +75,11 @@
         return seconds;
     }
 
-    public static String format(String type, int seconds) {
+    public static String format(String type, long seconds) {
         return getFormatter(type).format(seconds);
     }
 
-    public static String format(int seconds) {
+    public static String format(long seconds) {
         return format(null, seconds);
     }
 
@@ -86,7 +87,7 @@
         return format(target, timeToSeconds(spec));
     }
 
-    private static void pad(StringBuffer sb, int value) {
+    private static void pad(StringBuffer sb, long value) {
         if (value < 10) {
             sb.append('0');
         }
@@ -128,30 +129,30 @@
     }
 
     public static interface Formatter {
-        String format(int seconds);
+        String format(long seconds);
     }
 
     private static class DefaultFormatter implements Formatter {
-        public String format(int seconds) {
-            return String.valueOf((int) Math.ceil((double) seconds / 60));
+        public String format(long seconds) {
+            return String.valueOf((long) Math.ceil((double) seconds / 60));
         }
     }
 
     private static class HHMMSSFormatter implements Formatter {
     	
-        private static int seconds(int secondsInterval) {
+        private static long seconds(long secondsInterval) {
             return secondsInterval % 60;
         }
 
-        private static int minutes(int secondsInterval) {
+        private static long minutes(long secondsInterval) {
             return (secondsInterval / 60) % 60;
         }
 
-        private static int hours(int secondsInterval) {
+        private static long hours(long secondsInterval) {
             return secondsInterval / 3600;
         }
 
-        public String format(int seconds) {
+        public String format(long seconds) {
             StringBuffer sb = new StringBuffer();
             pad(sb, hours(seconds));
             sb.append(':');
@@ -177,12 +178,7 @@
     private static class NativeSGEFormatter extends HHMMSSFormatter {
     }
 
-    public int compareTo(Object o) {
-        if (o instanceof WallTime) {
-            return seconds - ((WallTime) o).seconds;
-        }
-        else {
-            throw new ClassCastException(o.getClass().getName());
-        }
+    public int compareTo(WallTime o) {
+        return seconds - o.seconds;
     }
 }
Index: modules/provider-dcache/.classpath
===================================================================
--- modules/provider-dcache/.classpath	(revision 3354)
+++ modules/provider-dcache/.classpath	(working copy)
@@ -4,6 +4,6 @@
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/abstraction-common"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/abstraction-provider-local"/>
-	<classpathentry kind="lib" path="/util/lib/log4j-1.2.8.jar"/>
-	<classpathentry kind="output" path="bin"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
+	<classpathentry kind="output" path=".build"/>
 </classpath>
Index: modules/provider-webdav/.classpath
===================================================================
--- modules/provider-webdav/.classpath	(revision 3354)
+++ modules/provider-webdav/.classpath	(working copy)
@@ -11,6 +11,6 @@
 			<accessrule kind="nonaccessible" pattern="**/CVS/*"/>
 		</accessrules>
 	</classpathentry>
-	<classpathentry kind="lib" path="/util/lib/log4j-1.2.8.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
 	<classpathentry kind="output" path=".build"/>
 </classpath>
Index: modules/provider-webdav/src/org/globus/cog/abstraction/impl/file/http/FileResourceImpl.java
===================================================================
--- modules/provider-webdav/src/org/globus/cog/abstraction/impl/file/http/FileResourceImpl.java	(revision 3354)
+++ modules/provider-webdav/src/org/globus/cog/abstraction/impl/file/http/FileResourceImpl.java	(working copy)
@@ -229,12 +229,10 @@
         throw new UnsupportedOperationException("submit");
     }
 
-    @Override
     public boolean supportsPartialTransfers() {
         return false;
     }
 
-    @Override
     public boolean supportsThirdPartyTransfers() {
         return false;
     }
Index: modules/provider-webdav/src/org/globus/cog/abstraction/impl/file/webdav/FileResourceImpl.java
===================================================================
--- modules/provider-webdav/src/org/globus/cog/abstraction/impl/file/webdav/FileResourceImpl.java	(revision 3354)
+++ modules/provider-webdav/src/org/globus/cog/abstraction/impl/file/webdav/FileResourceImpl.java	(working copy)
@@ -330,12 +330,10 @@
         tempFile.delete();
     }
 
-    @Override
     public boolean supportsPartialTransfers() {
         return false;
     }
 
-    @Override
     public boolean supportsThirdPartyTransfers() {
         return false;
     }
Index: modules/karajan/src/org/globus/cog/karajan/arguments/NamedArgumentsImpl.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/arguments/NamedArgumentsImpl.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/arguments/NamedArgumentsImpl.java	(working copy)
@@ -70,7 +70,7 @@
 		}
 	}
 	
-	public void addInitial(String name, Object value) {
+	public synchronized void addInitial(String name, Object value) {
 		named.put(name, value);
 	}
 
@@ -105,7 +105,7 @@
 		return named.keySet().iterator();
 	}
 
-	public Object getArgument(String name) {
+	public synchronized Object getArgument(String name) {
 		if (named != null) {
 			return named.get(name);
 		}
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/TimeoutException.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/TimeoutException.java	(revision 0)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/TimeoutException.java	(revision 3355)
@@ -0,0 +1,30 @@
+//----------------------------------------------------------------------
+//This code is developed as part of the Java CoG Kit project
+//The terms of the license can be found at http://www.cogkit.org/license
+//This message may not be removed or altered.
+//----------------------------------------------------------------------
+
+/*
+ * Created on Aug 2, 2005
+ */
+package org.globus.cog.karajan.workflow.service;
+
+public class TimeoutException extends ProtocolException {
+	private static final long serialVersionUID = -6781619140427115780L;
+
+	public TimeoutException() {
+		super();
+	}
+	
+	public TimeoutException(String message) {
+		super(message);
+	}
+
+	public TimeoutException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	public TimeoutException(Throwable cause) {
+		super(cause);
+	}
+}
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/RequestReply.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/RequestReply.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/RequestReply.java	(working copy)
@@ -15,6 +15,8 @@
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -25,11 +27,16 @@
 import java.util.zip.Inflater;
 import java.util.zip.InflaterInputStream;
 
+import org.apache.log4j.Logger;
 import org.globus.cog.karajan.util.serialization.XMLConverter;
 import org.globus.cog.karajan.workflow.service.channels.AbstractKarajanChannel;
 import org.globus.cog.karajan.workflow.service.channels.KarajanChannel;
 
 public abstract class RequestReply {
+	public static final Logger logger = Logger.getLogger(RequestReply.class);
+	
+	public static final int DEFAULT_TIMEOUT = 120 * 1000;
+	private int timeout = DEFAULT_TIMEOUT;
 
 	public static final int NOID = -1;
 	private int id;
@@ -39,6 +46,9 @@
 	private List<byte[]> inData;
 	private boolean inDataReceived;
 	private KarajanChannel channel;
+	private long lastTime = Long.MAX_VALUE;
+	
+	public static final DateFormat DF = new SimpleDateFormat("yyMMdd-HHmmss.SSS");
 
 	// private static final byte[] NO_EXCEPTION = new byte[0];
 
@@ -57,8 +67,7 @@
 	protected void setOutCmd(String outCmd) {
 		this.outCmd = outCmd;
 	}
-	
-	@SuppressWarnings("hiding") 
+	 
 	public void register(KarajanChannel channel) {
 		this.channel = channel;
 	}
@@ -132,12 +141,11 @@
 	}
 	
 	public abstract void send(boolean err) throws ProtocolException;
-
-	@SuppressWarnings("unused")
+	
 	protected void dataReceived(boolean fin, boolean error, byte[] data) throws ProtocolException {
+		setLastTime(System.currentTimeMillis());
 	}
-	
-	@SuppressWarnings("unused")
+		
 	protected synchronized void addInData(boolean fin, boolean err, byte[] data) {
 		if (inData == null) {
 			inData = new ArrayList<byte[]>(4);
@@ -145,7 +153,6 @@
 		inData.add(data);
 	}
 	
-	@SuppressWarnings("unused")
 	protected final void addInData(byte[] data) {
 		throw new RuntimeException("Should not be used");
 	}
@@ -345,4 +352,29 @@
 	protected Object getInObject(int index) {
 		return deserialize(getInData(index));
 	}
+
+	public long getLastTime() {
+		return lastTime;
+	}
+
+	public void setLastTime(long lastTime) {
+		this.lastTime = lastTime;
+	}
+	
+	public int getTimeout() {
+		return timeout;
+	}
+
+	public void setTimeout(int timeout) {
+		this.timeout = timeout;
+	}
+
+	public void handleTimeout() {
+		logger.warn("Unhandled timeout", new Throwable());
+		setLastTime(Long.MAX_VALUE);
+	}
+	
+	public void handleSignal(byte[] data) {
+		
+	}
 }
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/PerformanceDiagnosticOutputStream.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/PerformanceDiagnosticOutputStream.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/PerformanceDiagnosticOutputStream.java	(working copy)
@@ -27,13 +27,12 @@
 			public void run() {
 				count++;
 				String s;
-				logger.info(s = "Total transferred: "
+				logger.info(s = "[OUT] Total transferred: "
 						+ PerformanceDiagnosticInputStream.units(bytes) + "B, current rate: "
 						+ PerformanceDiagnosticInputStream.units(bytes - last)
 						+ "B/s, average rate: "
 						+ PerformanceDiagnosticInputStream.units(bytes / count)
 						+ "B/s");
-				System.out.println("[OUT] " + s);
 				last = bytes;
 			}
 		});
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/ChannelContext.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/ChannelContext.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/ChannelContext.java	(working copy)
@@ -9,6 +9,8 @@
  */
 package org.globus.cog.karajan.workflow.service.channels;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -38,8 +40,8 @@
 	private String remoteContact;
 	private UserContext userContext;
 	private int cmdseq;
-	private TagTable activeSenders;
-	private TagTable activeReceivers;
+	private TagTable<Command> activeSenders;
+	private TagTable<RequestHandler> activeReceivers;
 	// private Map reexecutionSet;
 	private static Timer timer;
 	private ServiceContext serviceContext;
@@ -55,9 +57,9 @@
 	
 	public ChannelContext(ServiceContext sc) {
 	    attributes = new HashMap<String,Object>();
-		activeSenders = new TagTable();
-		activeReceivers = new TagTable();
-		// reexecutionSet = new Hashtable();
+		activeSenders = new TagTable<Command>();
+		activeReceivers = new TagTable<RequestHandler>();
+
 		channelID = new ChannelID();
 		this.serviceContext = sc;
 	}
@@ -74,7 +76,7 @@
 	public synchronized void setUserContext(UserContext userContext) {
 		this.userContext = userContext;
 	}
-
+	
 	public synchronized UserContext newUserContext(String name) throws ChannelException {
 		if (userContext != null && userContext.getName() != null) {
 			try {
@@ -140,7 +142,7 @@
 		return channelID;
 	}
 	
-	public int nextCmdSeq() {
+	public synchronized int nextCmdSeq() {
 		cmdseq = cmdseq + 1;
 		while (activeSenders.containsKey(cmdseq) || activeReceivers.containsKey(cmdseq)) {
 			cmdseq = cmdseq + 1;
@@ -148,19 +150,39 @@
 		return cmdseq;
 	}
 
-	public synchronized void registerCommand(Command cmd) throws ProtocolException {
+	public void registerCommand(Command cmd) throws ProtocolException {
 		if (cmd.getId() == RequestReply.NOID) {
 			cmd.setId(nextCmdSeq());
-			activeSenders.put(cmd.getId(), cmd);
+			synchronized(activeSenders) {
+				activeSenders.put(cmd.getId(), cmd);
+			}
 		}
 		else {
 			throw new ProtocolException("Command already registered with id " + cmd.getId());
 		}
 	}
+	
+	public Collection<Command> getActiveCommands() {
+		List<Command> l = new ArrayList<Command>();
+		synchronized(activeSenders) {
+		    l.addAll(activeSenders.values());
+		}
+		return l;
+	}
+	
+	public Collection<RequestHandler> getActiveHandlers() {
+	    List<RequestHandler> l = new ArrayList<RequestHandler>();
+	    synchronized(activeReceivers) {
+	    	l.addAll(activeReceivers.values());
+	    }
+	    return l;
+	}
 
 	public void unregisterCommand(Command cmd) {
 		Object removed;
-		removed = activeSenders.remove(cmd.getId());
+		synchronized(activeSenders) {
+			removed = activeSenders.remove(cmd.getId());
+		}
 		if (removed == null) {
 			logger.warn("Attempted to unregister unregistered command with id " + cmd.getId());
 		}
@@ -170,23 +192,31 @@
 	}
 
 	public void registerHandler(RequestHandler handler, int tag) {
-		activeReceivers.put(tag, handler);
+		synchronized(activeReceivers) {
+			activeReceivers.put(tag, handler);
+		}
 	}
 
 	public void unregisterHandler(int tag) {
 		Object removed;
-		removed = activeReceivers.remove(tag);
+		synchronized(activeReceivers) {
+			removed = activeReceivers.remove(tag);
+		}
 		if (removed == null) {
 			logger.warn("Attempted to unregister unregistered handler with id " + tag);
 		}
 	}
 
 	public Command getRegisteredCommand(int id) {
-		return (Command) activeSenders.get(id);
+		synchronized(activeSenders) {
+			return activeSenders.get(id);
+		}
 	}
 
 	public RequestHandler getRegisteredHandler(int id) {
-		return (RequestHandler) activeReceivers.get(id);
+		synchronized(activeReceivers) {
+			return activeReceivers.get(id);
+		}
 	}
 
 	public void notifyRegisteredListeners(Exception e) {
@@ -195,8 +225,10 @@
 	}
 
 	private void notifyListeners(TagTable map, Exception t) {
-		for (Object o : map.values())
-			((RequestReply) o).errorReceived(null, t);
+		synchronized(map) {
+			for (Object o : map.values())
+				((RequestReply) o).errorReceived(null, t);
+		}
 	}
 
 	public Timer getTimer() {
@@ -235,6 +267,12 @@
 	        attributes.put(name, o);
 	    }
 	}
+	
+	public Object getAttribute(String name) {
+	    synchronized(attributes) {
+	        return attributes.get(name);
+	    }
+	}
 
 	public int getReconnectionAttempts() {
 		return reconnectionAttempts;
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/AbstractPipedChannel.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/AbstractPipedChannel.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/AbstractPipedChannel.java	(working copy)
@@ -9,14 +9,14 @@
  */
 package org.globus.cog.karajan.workflow.service.channels;
 
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
 import org.apache.log4j.Logger;
+import org.globus.cog.karajan.workflow.service.RemoteConfiguration.Entry;
 import org.globus.cog.karajan.workflow.service.RequestManager;
 import org.globus.cog.karajan.workflow.service.UserContext;
-import org.globus.cog.karajan.workflow.service.RemoteConfiguration.Entry;
 
-import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue;
-import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue;
-
 /**
  * A channel implementation for which the other endpoint lives in the
  * same JVM.
@@ -75,10 +75,10 @@
 		byte[] copy = new byte[bytes.length];
 		System.arraycopy(bytes, 0, copy, 0, bytes.length);
 		if (reply) {
-			s.handleReply(tag, fin, error, copy.length, copy);
+			s.handleReply(tag, flags, copy.length, copy);
 		}
 		else {
-			s.handleRequest(tag, fin, error, copy.length, copy);
+			s.handleRequest(tag, flags, copy.length, copy);
 		}
 		if (cb != null) {
 			cb.dataSent();
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/TagTable.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/TagTable.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/TagTable.java	(working copy)
@@ -13,14 +13,14 @@
 import java.util.HashMap;
 import java.util.Map;
 
-public class TagTable {
+public class TagTable<T> {
 	private static final long serialVersionUID = 1659255187618780167L;
 
-	private Map<MutableInteger,Object> map;
+	private Map<MutableInteger, T> map;
 	private MutableInteger mkey;
 	
 	public TagTable() {
-		map = new HashMap<MutableInteger,Object>();
+		map = new HashMap<MutableInteger, T>();
 		mkey = new MutableInteger();
 	}
 	
@@ -29,21 +29,21 @@
 		return map.containsKey(mkey);
 	}
 	
-	public synchronized void put(int key, Object value) {
+	public synchronized void put(int key, T value) {
 		map.put(new MutableInteger(key), value);
 	}
 	
-	public synchronized Object remove(int key) {
+	public synchronized T remove(int key) {
 		mkey.setValue(key);
 		return map.remove(mkey);
 	}
 	
-	public synchronized Object get(int key) {
+	public synchronized T get(int key) {
 		mkey.setValue(key);
 		return map.get(mkey);
 	}
 	
-	public Collection<Object> values() {
+	public Collection<T> values() {
 		return map.values();
 	}
 	
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/GSSChannel.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/GSSChannel.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/GSSChannel.java	(working copy)
@@ -82,12 +82,14 @@
 
 				gssContext.requestAnonymity(false);
 				gssContext.requestCredDeleg(false);
+				//gssContext.requestConf(false);
 				gssContext.setOption(GSSConstants.GSS_MODE, GSIConstants.MODE_SSL);
 				gssContext.setOption(GSSConstants.DELEGATION_TYPE,
 						GSIConstants.DELEGATION_TYPE_LIMITED);
 				URI contact = getContact();
 				socket = (GssSocket) GssSocketFactory.getDefault().createSocket(contact.getHost(),
 						contact.getPort(), gssContext);
+
 				socket.setKeepAlive(true);
 				socket.setSoTimeout(0);
 				socket.setWrapMode(GSIConstants.MODE_SSL.intValue());
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/KarajanChannel.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/KarajanChannel.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/KarajanChannel.java	(working copy)
@@ -24,6 +24,7 @@
 	public static final int FINAL_FLAG = 0x00000002;
 	public static final int ERROR_FLAG = 0x00000004;
 	public static final int COMPRESSED_FLAG = 0x00000008;
+	public static final int SIGNAL_FLAG = 0x00000010;
 
 	void sendTaggedData(int i, boolean fin, byte[] bytes);
 
@@ -41,11 +42,17 @@
 
 	void unregisterHandler(int tag);
 
-	void sendTaggedReply(int i, byte[] buf, boolean fin, boolean errorFlag);
+	void sendTaggedReply(int i, byte[] buf, boolean fin, boolean err);
 	
-	void sendTaggedReply(int i, byte[] buf, boolean fin, boolean errorFlag, SendCallback cb);
+	void sendTaggedReply(int i, byte[] buf, boolean fin, boolean err, SendCallback cb);
 	
-	void sendTaggedReply(int id, ByteBuffer buf, boolean fin, boolean errorFlag, SendCallback cb);
+	void sendTaggedReply(int i, byte[] buf, int flags);
+	
+	void sendTaggedReply(int i, byte[] buf, int flags, SendCallback cb);
+	
+	void sendTaggedReply(int id, ByteBuffer buf, boolean fin, boolean err, SendCallback cb);
+	
+	void sendTaggedReply(int id, ByteBuffer buf, int flags, SendCallback cb);
 
 	void registerHandler(RequestHandler handler, int tag);
 
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/AbstractStreamKarajanChannel.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/AbstractStreamKarajanChannel.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/AbstractStreamKarajanChannel.java	(working copy)
@@ -14,21 +14,22 @@
 import java.io.OutputStream;
 import java.net.URI;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.zip.Adler32;
 
 import org.apache.log4j.Logger;
 import org.globus.cog.karajan.workflow.service.RemoteConfiguration;
 import org.globus.cog.karajan.workflow.service.RequestManager;
 import org.globus.cog.karajan.workflow.service.commands.ChannelConfigurationCommand;
 
-import edu.emory.mathcs.backport.java.util.Collections;
-
 public abstract class AbstractStreamKarajanChannel extends AbstractKarajanChannel implements
 		Purgeable {
 	public static final Logger logger = Logger.getLogger(AbstractStreamKarajanChannel.class);
@@ -36,7 +37,7 @@
 	public static final int STATE_IDLE = 0;
 	public static final int STATE_RECEIVING_DATA = 1;
 
-	public static final int HEADER_LEN = 12;
+	public static final int HEADER_LEN = 20;
 
 	private InputStream inputStream;
 	private OutputStream outputStream;
@@ -44,7 +45,7 @@
 	private final byte[] rhdr;
 	private byte[] data;
 	private int dataPointer;
-	private int state, tag, flags, len;
+	private int state, tag, flags, len, hcsum, csum;
 
 	protected AbstractStreamKarajanChannel(RequestManager requestManager,
 			ChannelContext channelContext, boolean client) {
@@ -78,10 +79,15 @@
 
 	protected abstract void reconnect() throws ChannelException;
 
-	protected synchronized void handleChannelException(Exception e) {
+	protected synchronized boolean handleChannelException(Exception e) {
 		logger.info("Channel config: " + getChannelContext().getConfiguration());
-		ChannelManager.getManager().handleChannelException(this, e);
-		close();
+		if (!ChannelManager.getManager().handleChannelException(this, e)) {
+			close();
+			return false;
+		}
+		else {
+		    return true;
+		}
 	}
 
 	protected void configure() throws Exception {
@@ -121,11 +127,20 @@
 				tag = unpack(rhdr, 0);
 				flags = unpack(rhdr, 4);
 				len = unpack(rhdr, 8);
+				hcsum = unpack(rhdr, 12);
+				if ((tag ^ flags ^ len) != hcsum) {
+					logger.warn("Header checksum failed. Computed checksum: " + 
+							Integer.toHexString(tag ^ flags ^ len) + 
+							", checksum: " + Integer.toHexString(hcsum));
+					return true;
+				}
+				csum = unpack(rhdr, 16);
 				if (len > 1048576) {
 					logger.warn("Big len: " + len + " (tag: " + tag + ", flags: " + flags + ")");
 					data = new byte[1024];
 					inputStream.read(data);
 					logger.warn("data: " + ppByteBuf(data));
+					return true;
 				}
 				data = new byte[len];
 				dataPointer = 0;
@@ -143,18 +158,26 @@
 			if (dataPointer == len) {
 				dataPointer = 0;
 				state = STATE_IDLE;
-				boolean fin = (flags & FINAL_FLAG) != 0;
-				boolean error = (flags & ERROR_FLAG) != 0;
+				
+				if (csum != 0) {
+					Adler32 c = new Adler32();
+					c.update(data);
+					
+					if (((int) c.getValue()) != csum) {
+						logger.warn("Data checksum failed. Compute checksum: " + 
+								Integer.toHexString((int) c.getValue()) + ", checksum: " + Integer.toHexString(csum));
+					}
+				}
 				byte[] tdata = data;
 				// don't hold reference from the channel to the data
 				data = null;
-				if ((flags & REPLY_FLAG) != 0) {
+				if (flagIsSet(flags, REPLY_FLAG)) {
 					// reply
-					handleReply(tag, fin, error, len, tdata);
+					handleReply(tag, flags, len, tdata);
 				}
 				else {
 					// request
-					handleRequest(tag, fin, error, len, tdata);
+					handleRequest(tag, flags, len, tdata);
 				}
 				data = null;
 			}
@@ -188,7 +211,7 @@
 
 		Sender s = sender.get(channel.getClass());
 		if (s == null) {
-			sender.put(channel.getClass(), s = new Sender());
+			sender.put(channel.getClass(), s = new Sender(channel.getClass().getSimpleName()));
 			s.start();
 		}
 		return s;
@@ -210,20 +233,26 @@
 	}
 
 	private static class Sender extends Thread {
-		private final LinkedList<SendEntry> queue;
+		private final BlockingQueue<SendEntry> queue;
 		private final byte[] shdr;
+		private final String name;
 
-		public Sender() {
-			super("Sender");
-			queue = new LinkedList<SendEntry>();
+		public Sender(String name) {
+			super("Sender " + name);
+			this.name = name;
+			queue = new LinkedBlockingQueue<SendEntry>();
 			setDaemon(true);
 			shdr = new byte[HEADER_LEN];
 		}
 
-		public synchronized void enqueue(int tag, int flags, byte[] data,
+		public void enqueue(int tag, int flags, byte[] data,
 				AbstractStreamKarajanChannel channel, SendCallback cb) {
-			queue.addLast(new SendEntry(tag, flags, data, channel, cb));
-			notifyAll();
+			try {
+				queue.put(new SendEntry(tag, flags, data, channel, cb));
+			}
+			catch (InterruptedException e) {
+				logger.warn("Interrupted", e);
+			}
 		}
 
 		public void run() {
@@ -232,17 +261,14 @@
 				SendEntry e;
 				while (true) {
 					long now = System.currentTimeMillis();
-					synchronized (this) {
-						while (queue.isEmpty()) {
-							wait();
-						}
-						e = queue.removeFirst();
-						if (now - last > 10000) {
-							logger.info("Sender " + System.identityHashCode(this) + " queue size: "
-									+ queue.size());
-							last = now;
-						}
+					
+					e = queue.take();
+					if (now - last > 10000) {
+						logger.info("Sender " + name + " queue size: "
+								+ queue.size());
+						last = now;
 					}
+					
 					try {
 						send(e.tag, e.flags, e.data, e.channel.getOutputStream());
 						if (e.cb != null) {
@@ -252,23 +278,22 @@
 					catch (IOException ex) {
 						logger.info("Channel IOException", ex);
 						try {
-							synchronized (this) {
-								queue.addFirst(e);
+							if (e.channel.handleChannelException(ex)) {
+								queue.put(e);
 							}
-							e.channel.handleChannelException(ex);
 						}
 						catch (Exception exx) {
 						    logger.warn("Channel threw exception while handling channel exception", exx);
 						}
 					}
 					catch (Exception ex) {
-						ex.printStackTrace();
+						logger.warn("Caught exception while sending data", ex);
 						try {
 							e.channel.getChannelContext().getRegisteredCommand(e.tag).errorReceived(
 									null, ex);
 						}
 						catch (Exception exx) {
-							logger.warn(exx);
+							logger.warn("Exception", exx);
 						}
 					}
 				}
@@ -291,11 +316,16 @@
 				}
 			}
 		}
+		
 
 		private void send(int tag, int flags, byte[] data, OutputStream os) throws IOException {
 			pack(shdr, 0, tag);
 			pack(shdr, 4, flags);
 			pack(shdr, 8, data.length);
+			pack(shdr, 12, tag ^ flags ^ data.length);
+			Adler32 csum = new Adler32();
+			csum.update(data);
+			pack(shdr, 16, (int) csum.getValue());
 			synchronized (os) {
 				os.write(shdr);
 				os.write(data);
@@ -330,7 +360,6 @@
 		private boolean terminated;
 		private int id;
 
-		@SuppressWarnings("unchecked")
 		public Multiplexer(int id) {
 			super("Channel multiplexer " + id);
 			this.id = id;
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/PerformanceDiagnosticInputStream.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/PerformanceDiagnosticInputStream.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/PerformanceDiagnosticInputStream.java	(working copy)
@@ -28,15 +28,14 @@
 			public void run() {
 				count++;
 				String s;
-				logger.info(s = "Total transferred: " + units(bytes) + "B, current rate: "
+				logger.info(s = "[IN] Total transferred: " + units(bytes) + "B, current rate: "
 						+ units(bytes - last) + "B/s, average rate: " + units(bytes / count)
 						+ "B/s");
-				System.out.println("[IN]: " + s);
-				System.out.println("[MEM] Heap total: "
+				logger.info(s = "[MEM] Heap total: "
 						+ units(Runtime.getRuntime().totalMemory())
-						+ "MB, Heap used: "
+						+ "B, Heap used: "
 						+ units(Runtime.getRuntime().totalMemory()
-								- Runtime.getRuntime().freeMemory()) + "MB");
+								- Runtime.getRuntime().freeMemory()) + "B");
 				last = bytes;
 			}
 		});
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/ChannelManager.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/ChannelManager.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/ChannelManager.java	(working copy)
@@ -259,31 +259,40 @@
 		return meta;
 	}
 
-	public void handleChannelException(KarajanChannel channel, Exception e) {
+	/**
+	 * Returns <code>true</code> if this channel can still transmit after this
+	 * exception
+	 */
+	public boolean handleChannelException(KarajanChannel channel, Exception e) {
 		logger.info("Handling channel exception", e == null ? new Throwable() : e);
 		if (channel.isOffline()) {
 			logger.info("Channel already shut down");
-			return;
+			return false;
 		}
 		channel.setLocalShutdown();
 		ChannelContext ctx = channel.getChannelContext();
 		RemoteConfiguration.Entry config = ctx.getConfiguration();
+		boolean canContinue;
 		try {
 			if (config != null && config.hasOption(RemoteConfiguration.RECONNECT)) {
 				buffer(channel);
 				channel.close();
 				asyncReconnect(channel, e);
+				canContinue = true;
 			}
 			else {
 				channel.close();
 				shutdownChannel(channel);
+				canContinue = false;
 			}
 		}
 		catch (Exception e2) {
 			logger.info("Failed to shut down channel", e2);
+			canContinue = false;
 		}
 		ctx.channelShutDown(e);
 		logger.info("Channel exception handled");
+		return canContinue;
 	}
 
 	private void asyncReconnect(final KarajanChannel channel, final Exception e) {
@@ -372,7 +381,7 @@
 			unregisterChannel(getMetaChannel(ctx));
 		}
 		synchronized (channels) {
-			channels.remove(new HostCredentialPair("id://" + ctx.getChannelID(), ctx.getUserContext().getCredential()));
+			channels.remove(new HostCredentialPair("id://" + ctx.getChannelID(), (GSSCredential) null));
 		}
 	}
 	
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/NullChannel.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/NullChannel.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/NullChannel.java	(working copy)
@@ -26,6 +26,10 @@
 	protected void configureHeartBeat() {
 		// override to do nothing
 	}
+	
+	public void configureTimeoutChecks() {
+		// do nothing
+	}
 
 	public void sendTaggedData(int i, int flags, byte[] bytes, SendCallback cb) {
 		if (!sink) {
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/UDPChannel.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/UDPChannel.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/UDPChannel.java	(working copy)
@@ -156,16 +156,14 @@
 		if (checksum != actual) {
 			throw new ChannelException("Checksum failed. Expected " + checksum + " got " + actual);
 		}
-		boolean fin = (flags & FINAL_FLAG) != 0;
-		boolean error = (flags & ERROR_FLAG) != 0;
 		byte[] data = new byte[len - HDRLEN];
 		System.arraycopy(recvbuf, HDRLEN, data, 0, len - HDRLEN);
 		if ((flags & REPLY_FLAG) != 0) {
 			// reply
-			handleReply(tag, fin, error, len - HDRLEN, data);
+			handleReply(tag, flags, len - HDRLEN, data);
 		}
 		else {
-			handleRequest(tag, fin, error, len - HDRLEN, data);
+			handleRequest(tag, flags, len - HDRLEN, data);
 		}
 	}
 	
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/AbstractKarajanChannel.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/AbstractKarajanChannel.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/channels/AbstractKarajanChannel.java	(working copy)
@@ -14,6 +14,7 @@
 import java.io.InputStream;
 import java.net.URI;
 import java.nio.ByteBuffer;
+import java.util.Collection;
 import java.util.TimerTask;
 
 import org.apache.log4j.Logger;
@@ -22,6 +23,7 @@
 import org.globus.cog.karajan.workflow.service.RemoteConfiguration;
 import org.globus.cog.karajan.workflow.service.RemoteConfiguration.Entry;
 import org.globus.cog.karajan.workflow.service.RequestManager;
+import org.globus.cog.karajan.workflow.service.RequestReply;
 import org.globus.cog.karajan.workflow.service.Service;
 import org.globus.cog.karajan.workflow.service.UserContext;
 import org.globus.cog.karajan.workflow.service.commands.Command;
@@ -33,6 +35,8 @@
 	// some random spread to avoid sending all heartbeats at once
 	public static final int DEFAULT_HBI_INITIAL_SPREAD = 10;
 	public static final int DEFAULT_HBI_SPREAD = 10;
+	
+	public static final int TIMEOUT_CHECK_INTERVAL = 1;
 
 	private ChannelContext context;
 	private volatile int usageCount, longTermUsageCount;
@@ -52,6 +56,7 @@
 		// registeredMaps = new LinkedList();
 		this.client = client;
 		configureHeartBeat();
+		configureTimeoutChecks();
 	}
 
 	protected void configureHeartBeat() {
@@ -63,9 +68,9 @@
 			}
 		}
 		heartBeatInterval *= 1000;
-
+		
 		boolean controlHeartbeats = isClient() == clientControlsHeartbeats();
-
+		
 		if (!isOffline() && controlHeartbeats) {
 		    scheduleHeartbeats(heartBeatInterval);
 		}
@@ -89,15 +94,15 @@
 			scheduleHeartbeatCheck(heartBeatInterval);
 		}
 	}
-
+	
 	public void scheduleHeartbeats(int heartBeatInterval) {
 	    TimerTask heartBeatTask;
 	    heartBeatTask = new HeartBeatTask(this);
-	    context.getTimer().schedule(heartBeatTask,
-        heartBeatInterval + (int) (Math.random() * DEFAULT_HBI_INITIAL_SPREAD * 1000),
+	    context.getTimer().schedule(heartBeatTask, 
+        heartBeatInterval + (int) (Math.random() * DEFAULT_HBI_INITIAL_SPREAD * 1000), 
         heartBeatInterval + (int) (Math.random() * DEFAULT_HBI_SPREAD * 1000));
 	}
-
+	
 	public void scheduleHeartbeatCheck(int heartBeatInterval) {
 	    TimerTask heartBeatTask;
 	    int mult = 2;
@@ -105,6 +110,33 @@
         context.getTimer().schedule(heartBeatTask, mult * heartBeatInterval,
         		mult * heartBeatInterval);
 	}
+	
+	public void configureTimeoutChecks() {
+	    context.getTimer().schedule(new TimerTask() {
+			public void run() {
+			    checkTimeouts();
+			}},
+	        TIMEOUT_CHECK_INTERVAL * 1000, TIMEOUT_CHECK_INTERVAL * 1000);
+	}
+	
+	protected void checkTimeouts() {
+	    checkTimeouts(context.getActiveCommands());
+	    checkTimeouts(context.getActiveHandlers());
+	}
+	
+	private void checkTimeouts(Collection<? extends RequestReply> l) {
+	    long now = System.currentTimeMillis();
+	    for (RequestReply r : l) {
+	    	if (now - r.getLastTime() > r.getTimeout()) {
+	    		try {
+	    			r.handleTimeout();
+	    		}
+	    		catch (Exception e) {
+	    			logger.warn("Error handling timeout", e);
+	    		}
+	    	}
+	    }
+	}
 
 	protected boolean clientControlsHeartbeats() {
 	    return true;
@@ -131,6 +163,16 @@
 		context.unregisterHandler(tag);
 	}
 
+	@Override
+	public void sendTaggedReply(int tag, byte[] data, boolean fin, boolean err) {
+		sendTaggedReply(tag, data, (fin ? FINAL_FLAG : 0) + (err ? ERROR_FLAG : 0));
+	}
+	
+	@Override
+	public void sendTaggedReply(int tag, byte[] data, boolean fin, boolean err, SendCallback cb) {
+		sendTaggedReply(tag, data, (fin ? FINAL_FLAG : 0) + (err ? ERROR_FLAG : 0), cb);
+	}
+
 	public void sendTaggedReply(int tag, byte[] data, boolean fin) {
 		sendTaggedReply(tag, data, fin, false);
 	}
@@ -151,36 +193,33 @@
 		sendTaggedData(i, flags, bytes, null);
 	}
 
-	public void sendTaggedReply(int tag, byte[] data, boolean fin, boolean err) {
-		sendTaggedReply(tag, data, fin, err, null);
+	public void sendTaggedReply(int tag, byte[] data, int flags) {
+		sendTaggedReply(tag, data, flags, null);
 	}
 
-	public void sendTaggedReply(int tag, byte[] data, boolean fin, boolean err, SendCallback cb) {
+	public void sendTaggedReply(int tag, byte[] data, int flags, SendCallback cb) {
 		if (logger.isDebugEnabled()) {
-			logger.debug(this + " REPL>: tag = " + tag + ", fin = " + fin + ", datalen = "
+			logger.debug(this + " REPL>: tag = " + tag + ", fin = " + flagIsSet(flags, FINAL_FLAG) + ", datalen = "
 					+ data.length + ", data = " + ppByteBuf(data));
 		}
-		int flags = REPLY_FLAG;
-		if (fin) {
-			flags |= FINAL_FLAG;
-		}
-		if (err) {
-			flags |= ERROR_FLAG;
-		}
-		sendTaggedData(tag, flags, data, cb);
+		
+		sendTaggedData(tag, flags | REPLY_FLAG, data, cb);
 	}
+	
+	public void sendTaggedReply(int id, ByteBuffer buf, boolean fin, boolean err, SendCallback cb) {
+		sendTaggedReply(id, buf, (fin ? FINAL_FLAG : 0) + (err ? ERROR_FLAG : 0), cb);
+	}
 
-	public void sendTaggedReply(int id, ByteBuffer buf, boolean fin, boolean errorFlag,
-			SendCallback cb) {
+	public void sendTaggedReply(int id, ByteBuffer buf, int flags, SendCallback cb) {
 		// TODO this should probably be changed to use buffers more efficiently
 		if (buf.hasArray() && (buf.limit() == buf.capacity())) {
-			sendTaggedReply(id, buf.array(), fin, errorFlag, cb);
+			sendTaggedReply(id, buf.array(), flags, cb);
 		}
 		else {
 			byte[] bbuf = new byte[buf.limit()];
 			buf.get(bbuf);
 			buf.rewind();
-			sendTaggedReply(id, bbuf, fin, errorFlag, cb);
+			sendTaggedReply(id, bbuf, flags, cb);
 		}
 	}
 
@@ -199,7 +238,7 @@
 	public ChannelContext getChannelContext() {
 		return context;
 	}
-
+	
 	public final UserContext getUserContext() {
 	    return context.getUserContext();
 	}
@@ -237,7 +276,7 @@
 		i += (buf[offset + 3] & 0xff) << 24;
 		return i;
 	}
-
+	
 	/**
 	   Pretty-print byte buffer
 	 */
@@ -342,20 +381,28 @@
 
 	}
 
-	protected void handleReply(int tag, boolean fin, boolean error, int len, byte[] data) {
+	protected void handleReply(int tag, int flags, int len, byte[] data) {
 		if (logger.isDebugEnabled()) {
-			logger.debug(this + " REPL<: tag = " + tag + ", fin = " + fin + ", err = " + error
+			logger.debug(this + " REPL<: tag = " + tag + ", fin = " + 
+					flagIsSet(flags, FINAL_FLAG) + ", err = " + flagIsSet(flags, ERROR_FLAG)
 					+ ", datalen = " + len + ", data = " + ppByteBuf(data));
 		}
 		Command cmd = getChannelContext().getRegisteredCommand(tag);
 		if (cmd != null) {
 			try {
-				cmd.replyReceived(fin, error, data);
+				boolean fin = finalFlagIsSet(flags);
+				boolean err = errorFlagIsSet(flags);
+				if (flagIsSet(flags, SIGNAL_FLAG)) {
+                    cmd.handleSignal(data);
+                }
+				else {
+					cmd.replyReceived(fin, err, data);
+				}
 				if (fin) {
 					if (logger.isDebugEnabled()) {
 						logger.debug(this + " REPL: " + cmd);
 					}
-					if (error) {
+					if (err) {
 						cmd.errorReceived();
 					}
 					else {
@@ -378,22 +425,45 @@
 		}
 	}
 
+	protected boolean flagIsSet(int flags, int mask) {
+		return (flags & mask) != 0;
+	}
+	
+	protected boolean finalFlagIsSet(int flags) {
+		return (flags & FINAL_FLAG) != 0;
+	}
+	
+	protected boolean errorFlagIsSet(int flags) {
+		return (flags & ERROR_FLAG) != 0;
+	}
+
 	protected void unregisteredSender(int tag) {
 		logger.warn(getName() + " Recieved reply to unregistered sender. Tag: " + tag);
 	}
 
-	protected void handleRequest(int tag, boolean fin, boolean error, int len, byte[] data) {
+	protected void handleRequest(int tag, int flags, int len, byte[] data) {
 		if (logger.isDebugEnabled()) {
-			logger.debug(this + " REQ<: tag = " + tag + ", fin = " + fin + ", err = " + error
+			logger.debug(this + " REQ<: tag = " + tag + ", fin = " + 
+					flagIsSet(flags, FINAL_FLAG) + ", err = " + flagIsSet(flags, ERROR_FLAG)
 					+ ", datalen = " + len + ", data = " + ppByteBuf(data));
 		}
 		RequestHandler handler = getChannelContext().getRegisteredHandler(tag);
+		boolean fin = finalFlagIsSet(flags);
+		boolean err = errorFlagIsSet(flags);
 		try {
 			if (handler != null) {
-				handler.register(this);
-				handler.dataReceived(fin, error, data);
+				if (flagIsSet(flags, SIGNAL_FLAG)) {
+					handler.handleSignal(data);
+				}
+				else {
+					handler.dataReceived(fin, err, data);
+				}
 			}
 			else {
+				if (flagIsSet(flags, SIGNAL_FLAG)) {
+					logger.warn("Got signal for unregistered tag (" + tag + "): " + new String(data));
+					return;
+				}
 				try {
 					handler = getRequestManager().handleInitialRequest(data);
 					handler.setId(tag);
@@ -409,7 +479,7 @@
 					if (logger.isDebugEnabled()) {
 						logger.debug(this + " REQ: " + handler);
 					}
-					if (error) {
+					if (err) {
 						handler.errorReceived();
 					}
 					else {
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/handlers/RequestHandler.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/handlers/RequestHandler.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/handlers/RequestHandler.java	(working copy)
@@ -10,11 +10,13 @@
 package org.globus.cog.karajan.workflow.service.handlers;
 
 import java.util.Collection;
+import java.util.Date;
 import java.util.Iterator;
 
 import org.apache.log4j.Logger;
 import org.globus.cog.karajan.workflow.service.ProtocolException;
 import org.globus.cog.karajan.workflow.service.RequestReply;
+import org.globus.cog.karajan.workflow.service.TimeoutException;
 import org.globus.cog.karajan.workflow.service.channels.KarajanChannel;
 
 public abstract class RequestHandler extends RequestReply {
@@ -34,6 +36,7 @@
 	}
 	
 	protected void sendReply() throws ProtocolException {
+	    setLastTime(System.currentTimeMillis());
 		send();
 		replySent = true;
 	}
@@ -102,10 +105,22 @@
 	}
 	
 	protected String ppInData(String prefix) {
-		return ppData(prefix+"< ", getInCmd(), getInDataChunks());
+		return ppData(prefix + "< ", getInCmd(), getInDataChunks());
 	}
-	
+
 	public String toString() {
-		return "Handler(" + getInCmd() + ")";
+		return "Handler(" + getId() + ", " + getInCmd() + ")";
 	}
+
+	public void handleTimeout() {
+		if (isInDataReceived()) {
+			return;
+		}
+		setLastTime(Long.MAX_VALUE);
+		getChannel().unregisterHandler(getId());
+		String msg = this + ": timed out receiving request. Last time "
+				+ DF.format(new Date(getLastTime())) + ", now: " + DF.format(new Date());
+		logger.info(msg);
+		errorReceived("Timeout", new TimeoutException(msg));
+	}
 }
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/ReplyTimeoutException.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/ReplyTimeoutException.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/ReplyTimeoutException.java	(working copy)
@@ -1,30 +0,0 @@
-//----------------------------------------------------------------------
-//This code is developed as part of the Java CoG Kit project
-//The terms of the license can be found at http://www.cogkit.org/license
-//This message may not be removed or altered.
-//----------------------------------------------------------------------
-
-/*
- * Created on Aug 2, 2005
- */
-package org.globus.cog.karajan.workflow.service;
-
-public class ReplyTimeoutException extends ProtocolException {
-	private static final long serialVersionUID = -6781619140427115780L;
-
-	public ReplyTimeoutException() {
-		super();
-	}
-	
-	public ReplyTimeoutException(String message) {
-		super(message);
-	}
-
-	public ReplyTimeoutException(String message, Throwable cause) {
-		super(message, cause);
-	}
-
-	public ReplyTimeoutException(Throwable cause) {
-		super(cause);
-	}
-}
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/commands/Command.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/commands/Command.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/commands/Command.java	(working copy)
@@ -10,18 +10,15 @@
 package org.globus.cog.karajan.workflow.service.commands;
 
 import java.io.IOException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.Timer;
-import java.util.TimerTask;
 
 import org.apache.log4j.Logger;
 import org.globus.cog.karajan.workflow.service.ProtocolException;
-import org.globus.cog.karajan.workflow.service.ReplyTimeoutException;
 import org.globus.cog.karajan.workflow.service.RequestReply;
+import org.globus.cog.karajan.workflow.service.TimeoutException;
 import org.globus.cog.karajan.workflow.service.channels.ChannelIOException;
 import org.globus.cog.karajan.workflow.service.channels.ChannelManager;
 import org.globus.cog.karajan.workflow.service.channels.KarajanChannel;
@@ -36,17 +33,12 @@
 		timer = new Timer(true);
 	}
 
-	public static final DateFormat DF = new SimpleDateFormat("yyMMdd-HHmmss.SSS");
-
-	public static final int DEFAULT_REPLY_TIMEOUT = 120 * 1000;
 	public static final int DEFAULT_MAX_RETRIES = 2;
-	private int replyTimeout = DEFAULT_REPLY_TIMEOUT;
 	private int maxRetries = DEFAULT_MAX_RETRIES;
 
 	private Callback cb;
 	private String errorMsg;
 	private Exception exception;
-	private Timeout timeout;
 	private int retries;
 	private long sendTime;
 	private long sendReqTime;
@@ -64,13 +56,13 @@
 		this.cb = cb;
 	}
 
-	public void waitForReply() throws ReplyTimeoutException {
+	public void waitForReply() throws TimeoutException {
 		synchronized (this) {
 			if (!this.isInDataReceived()) {
-				long left = replyTimeout;
+				long left = getTimeout();
 				while (!this.isInDataReceived()) {
 					if (left <= 0) {
-						throw new ReplyTimeoutException();
+						throw new TimeoutException();
 					}
 					try {
 						wait(left);
@@ -78,7 +70,7 @@
 					catch (InterruptedException e) {
 						e.printStackTrace();
 					}
-					left = sendTime == 0 ? 1000 : replyTimeout
+					left = sendTime == 0 ? 1000 : getTimeout()
 							- (System.currentTimeMillis() - sendTime);
 				}
 			}
@@ -106,8 +98,7 @@
 	}
 	
 	public void send(boolean err) throws ProtocolException {
-		sendReqTime = System.currentTimeMillis();
-		cancelTimeout();
+
 		KarajanChannel channel = getChannel();
 		if (logger.isDebugEnabled()) {
 			logger.debug("Sending " + this + " on " + channel);
@@ -137,6 +128,8 @@
 					channel.sendTaggedData(id, !i.hasNext(), buf, !i.hasNext() ? this : null);
 				}
 			}
+			sendReqTime = System.currentTimeMillis();
+			setLastTime(sendReqTime);
 		}
 		catch (ChannelIOException e) {
 			reexecute(e.getMessage(), e);
@@ -146,21 +139,11 @@
 	public void dataSent() {
 		sendTime = System.currentTimeMillis();
 		//when using the piped channels the reply will arrive before this method is called
-		setupReplyTimeoutChecker();
+		setLastTime(sendTime);
 	}
+	
+	private static boolean shutdownMsg;
 
-	protected synchronized void setupReplyTimeoutChecker() {
-		if (!isInDataReceived()) {
-			timeout = new Timeout();
-			try {
-				timer.schedule(timeout, replyTimeout);
-			}
-			catch (IllegalStateException e) {
-				logger.info("Timer cancelled due to JVM shutting down. Going without timeouts.");
-			}
-		}
-	}
-
 	public byte[] execute(KarajanChannel channel) throws ProtocolException, IOException {
 		send(channel);
 		waitForReply();
@@ -187,14 +170,6 @@
 		send();
 	}
 
-	public int getReplyTimeout() {
-		return replyTimeout;
-	}
-
-	public void setReplyTimeout(int replyTimeout) {
-		this.replyTimeout = replyTimeout;
-	}
-
 	public int getMaxRetries() {
 		return maxRetries;
 	}
@@ -203,15 +178,7 @@
 		this.maxRetries = maxRetries;
 	}
 
-	private synchronized void cancelTimeout() {
-		if (timeout != null) {
-			timeout.cancel();
-			timeout = null;
-		}
-	}
-
 	public void receiveCompleted() {
-		cancelTimeout();
 		if (logger.isDebugEnabled()) {
 			logger.debug(ppInData("CMD"));
 		}
@@ -222,7 +189,6 @@
 	}
 
 	public void errorReceived(String msg, Exception t) {
-		cancelTimeout();
 		if (logger.isDebugEnabled()) {
 			logger.debug(ppInData("CMDERR"));
 		}
@@ -275,30 +241,25 @@
 			}
 		}
 	}
-
-	protected void handleReplyTimeout() {
-		timeout = null;
+	
+	public void handleTimeout() {
 		if (isInDataReceived()) {
 			return;
 		}
 		logger.warn(this
 				+ ": handling reply timeout; sendReqTime="
 				+ DF.format(new Date(sendReqTime)) + ", sendTime=" + DF.format(new Date(sendTime))
-						+ ", now=" + DF.format(new Date()));
-		reexecute("Reply timeout", new ReplyTimeoutException());
+						+ ", now=" + DF.format(new Date()) + ", channel=" + getChannel());
+		getChannel().unregisterCommand(this);
+		//reexecute("Reply timeout", new TimeoutException());
 	}
 
-	private class Timeout extends TimerTask {
-		public void run() {
-			handleReplyTimeout();
-		}
+	protected long getSendReqTime() {
+		return sendReqTime;
+	}
 
-		public boolean cancel() {
-			if (logger.isDebugEnabled()) {
-				logger.debug("SRC " + System.identityHashCode(timeout), new Exception());
-			}
-			return super.cancel();
-		}		
+	protected void setSendReqTime(long sendReqTime) {
+		this.sendReqTime = sendReqTime;
 	}
 
 	public String toString() {
Index: modules/karajan/src/org/globus/cog/karajan/workflow/service/commands/HeartBeatCommand.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/service/commands/HeartBeatCommand.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/service/commands/HeartBeatCommand.java	(working copy)
@@ -9,11 +9,14 @@
  */
 package org.globus.cog.karajan.workflow.service.commands;
 
+import org.apache.log4j.Logger;
 import org.globus.cog.karajan.workflow.service.ProtocolException;
 import org.globus.cog.karajan.workflow.service.handlers.HeartBeatHandler;
 
 
 public class HeartBeatCommand extends Command {
+    public static final Logger logger = Logger.getLogger(HeartBeatCommand.class);
+    
     private long start;
     private static int sid;
     private int id;
@@ -31,5 +34,10 @@
 
 	public void replyReceived(boolean fin, boolean err, byte[] data) throws ProtocolException {
 		super.replyReceived(fin, err, data);
+		if (logger.isInfoEnabled()) {
+            long rst = Long.parseLong(getInDataAsString(0));
+            long now = System.currentTimeMillis();
+            logger.info(getChannel() + " up latency: " + (now - rst) + "ms, rtt: " + (now - start) + "ms");
+        }
 	}
 }
Index: modules/karajan/src/org/globus/cog/karajan/workflow/futures/ChannelSplitter.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/futures/ChannelSplitter.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/futures/ChannelSplitter.java	(working copy)
@@ -38,7 +38,6 @@
 	
 	
 
-	@Override
 	public void futureModified(Future f, VariableStack stack) {
 		FutureVariableArguments in = (FutureVariableArguments) f;
 		while(in.available() > 0) {
Index: modules/karajan/src/org/globus/cog/karajan/workflow/futures/ForwardArgumentFuture.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/workflow/futures/ForwardArgumentFuture.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/workflow/futures/ForwardArgumentFuture.java	(working copy)
@@ -114,7 +114,6 @@
 		}
 	}
 	
-	@Override
 	public void futureModified(Future f, VariableStack stack) {
 		synchronized (vargs) {
 			try {
@@ -136,4 +135,4 @@
 		this.exception = e;
 		actions();
 	}
-}
\ No newline at end of file
+}
Index: modules/karajan/src/org/globus/cog/karajan/util/ElementProperty.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/util/ElementProperty.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/util/ElementProperty.java	(working copy)
@@ -130,7 +130,7 @@
 			this.name = ident.getName();
 		}
 
-		public Object getValue(VariableStack stack) throws VariableNotFoundException {
+		public synchronized Object getValue(VariableStack stack) throws VariableNotFoundException {
 			switch (frame) {
 				case UNINITIALIZED:
 				case VariableStack.NO_FRAME:
Index: modules/karajan/src/org/globus/cog/karajan/scheduler/AbstractScheduler.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/scheduler/AbstractScheduler.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/scheduler/AbstractScheduler.java	(working copy)
@@ -169,7 +169,7 @@
 				}
 			}
 			finally {
-				jobListeners.release();
+				jobListeners.release(i);
 			}
 		}
 	}
Index: modules/karajan/src/org/globus/cog/karajan/scheduler/submitQueue/AbstractSubmitQueue.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/scheduler/submitQueue/AbstractSubmitQueue.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/scheduler/submitQueue/AbstractSubmitQueue.java	(working copy)
@@ -9,9 +9,10 @@
  */
 package org.globus.cog.karajan.scheduler.submitQueue;
 
-import edu.emory.mathcs.backport.java.util.Queue;
-import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
 
+
 /**
  * Base class for submit queues. It uses a generic throttle value.
  * Progress through the queues is triggered using the {@link step()} method
Index: modules/karajan/src/org/globus/cog/karajan/scheduler/WeightedHostScoreScheduler.java
===================================================================
--- modules/karajan/src/org/globus/cog/karajan/scheduler/WeightedHostScoreScheduler.java	(revision 3354)
+++ modules/karajan/src/org/globus/cog/karajan/scheduler/WeightedHostScoreScheduler.java	(working copy)
@@ -251,6 +251,10 @@
 		}
 		return selected.getHost();
 	}
+	
+	public synchronized boolean allOverloaded() {
+		return sorted.allOverloaded();
+	}
 
 	public void releaseContact(Contact contact) {
 		if (logger.isDebugEnabled()) {
Index: modules/karajan/.classpath
===================================================================
--- modules/karajan/.classpath	(revision 3354)
+++ modules/karajan/.classpath	(working copy)
@@ -1,18 +1,16 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" path="resources"/>
-	<classpathentry kind="src" path="/grapheditor"/>
-	<classpathentry kind="src" path="/util"/>
-	<classpathentry exported="true" kind="lib" path="lib/xpp3-1.1.3.4d_b4_min.jar" sourcepath="/home/mike/work/sources/xpp3-1.1.3.4.D/src"/>
-	<classpathentry exported="true" kind="lib" path="lib/xstream-1.1.1-patched.jar" sourcepath="/home/mike/work/sources/xstream-1.1.1/src/java"/>
-	<classpathentry kind="src" path="/abstraction"/>
-	<classpathentry kind="lib" path="etc"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/abstraction-provider-gt2"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/jglobus"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/backport-util-concurrent"/>
-	<classpathentry kind="lib" path="/util/lib/log4j-1.2.8.jar"/>
-	<classpathentry kind="lib" path="/util/lib/backport-util-concurrent.jar"/>
-	<classpathentry kind="output" path=".build"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="resources"/>
+	<classpathentry kind="src" path="/util"/>
+	<classpathentry exported="true" kind="lib" path="lib/xpp3-1.1.3.4d_b4_min.jar" sourcepath="/home/mike/work/sources/xpp3-1.1.3.4.D/src"/>
+	<classpathentry exported="true" kind="lib" path="lib/xstream-1.1.1-patched.jar" sourcepath="/home/mike/work/sources/xstream-1.1.1/src/java"/>
+	<classpathentry kind="src" path="/abstraction"/>
+	<classpathentry kind="lib" path="etc"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/abstraction-provider-gt2"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/jglobus"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/backport-util-concurrent"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
+	<classpathentry kind="output" path=".build"/>
+</classpath>
Index: modules/all/dependencies.xml
===================================================================
--- modules/all/dependencies.xml	(revision 3354)
+++ modules/all/dependencies.xml	(working copy)
@@ -26,9 +26,6 @@
 			<property name="module" value="certmanagement"/>
 		</ant>
 		<ant antfile="${main.buildfile}" target="dep">
-			<property name="module" value="gridfaces"/>
-		</ant>
-		<ant antfile="${main.buildfile}" target="dep">
 			<property name="module" value="examples"/>
 		</ant>
 	</target>
Index: modules/abstraction/.classpath
===================================================================
--- modules/abstraction/.classpath	(revision 3354)
+++ modules/abstraction/.classpath	(working copy)
@@ -10,7 +10,6 @@
 	<classpathentry exported="true" kind="src" path="/abstraction-provider-gt4_0_0"/>
 	<classpathentry combineaccessrules="false" exported="true" kind="src" path="/abstraction-provider-local"/>
 	<classpathentry combineaccessrules="false" exported="true" kind="src" path="/abstraction-provider-condor"/>
-	<classpathentry combineaccessrules="false" exported="true" kind="src" path="/abstraction-provider-dcache"/>
 	<classpathentry combineaccessrules="false" exported="true" kind="src" path="/abstraction-provider-localscheduler"/>
 	<classpathentry exported="true" kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j-1.2.8"/>
 	<classpathentry kind="output" path=".build"/>
Index: modules/provider-condor/.classpath
===================================================================
--- modules/provider-condor/.classpath	(revision 3354)
+++ modules/provider-condor/.classpath	(working copy)
@@ -9,6 +9,6 @@
 			<accessrule kind="nonaccessible" pattern="**/CVS/*"/>
 		</accessrules>
 	</classpathentry>
-	<classpathentry kind="lib" path="/util/lib/log4j-1.2.8.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
 	<classpathentry kind="output" path=".build"/>
 </classpath>
Index: modules/util/.classpath
===================================================================
--- modules/util/.classpath	(revision 3354)
+++ modules/util/.classpath	(working copy)
@@ -4,8 +4,6 @@
 	<classpathentry kind="lib" path="etc"/>
 	<classpathentry exported="true" kind="lib" path="lib/jakarta-regexp-1.2.jar"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j-1.2.8"/>
-	<classpathentry exported="true" kind="lib" path="lib/log4j-1.2.16.jar"/>
-	<classpathentry exported="true" kind="lib" path="lib/log4j-1.2.8.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/log4j"/>
 	<classpathentry kind="output" path=".build"/>
 </classpath>
Index: modules/util/lib/backport-util-concurrent.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: modules/util/lib/log4j-1.2.16.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: modules/util/lib/log4j-1.2.8.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: modules/util/src/org/globus/cog/util/CopyOnWriteHashSet.java
===================================================================
--- modules/util/src/org/globus/cog/util/CopyOnWriteHashSet.java	(revision 3354)
+++ modules/util/src/org/globus/cog/util/CopyOnWriteHashSet.java	(working copy)
@@ -41,7 +41,7 @@
 
 public class CopyOnWriteHashSet<T> implements Set<T>, Cloneable {
 	private Set<T> set = Collections.emptySet();
-	private int lock;	
+	private int lock;
 
 	public int size() {
 		return set.size();
@@ -55,22 +55,24 @@
 		return set.contains(o);
 	}
 	
-	public synchronized void release() {
-		if (lock > 0) {
-			lock--;
-		}
+	public synchronized void release(Iterator<T> it) {
+	    if (((LIterator) it).set == set) {
+    		if (lock > 0) {
+    			lock--;
+    		}
+	    }
 	}
 
 	public synchronized Iterator<T> iterator() {
 		lock++;
-		return set.iterator();
+		return new LIterator(set);
 	}
 
 	public Object[] toArray() {
 		return set.toArray();
 	}
 
-    public <T> T[] toArray(T[] a) {
+    public <S> S[] toArray(S[] a) {
         return set.toArray(a);
     }
 
@@ -194,11 +196,33 @@
 	        T o = it.next();
 	        tmp.add(o);
 	    }
-	    release();
+	    release(it);
 	    
 	    CopyOnWriteHashSet<T> result = new CopyOnWriteHashSet<T>();
 	    result.lock = 0;
 	    result.set = tmp;
 	    return result;
 	}
+	
+	private class LIterator implements Iterator<T> {
+	    private Iterator<T> it;
+	    public Set<T> set;
+	    
+	    public LIterator(Set<T> set) {
+	        this.set = set;
+	        this.it = set.iterator();
+	    }
+
+        public boolean hasNext() {
+            return it.hasNext();
+        }
+
+        public T next() {
+            return it.next();
+        }
+
+        public void remove() {
+            it.remove();
+        }
+	}
 }
Index: modules/util/src/org/globus/cog/util/CopyOnWriteArrayList.java
===================================================================
--- modules/util/src/org/globus/cog/util/CopyOnWriteArrayList.java	(revision 3354)
+++ modules/util/src/org/globus/cog/util/CopyOnWriteArrayList.java	(working copy)
@@ -26,7 +26,7 @@
 
 public class CopyOnWriteArrayList<T> implements List<T> {
 	private List<T> list = Collections.emptyList();
-	private int lock;	
+	private int lock;
 
 	public int size() {
 		return list.size();
@@ -40,22 +40,24 @@
 		return list.contains(o);
 	}
 	
-	public synchronized void release() {
-		if (lock > 0) {
-			lock--;
-		}
+	public synchronized void release(Iterator<T> it) {
+	    if (((LIterator) it).list == list) {
+	        if (lock > 0) {
+	            lock--;
+	        }
+	    }
 	}
 
 	public synchronized Iterator<T> iterator() {
 		lock++;
-		return list.iterator();
+		return new LIterator(list);
 	}
 
 	public Object[] toArray() {
 		return list.toArray();
 	}
 
-    public <T> T[] toArray(T[] a) {
+    public <S> S[] toArray(S[] a) {
         return list.toArray(a);
     }
 
@@ -68,7 +70,6 @@
 		}
 	}
     
-    @Override
     public synchronized void add(int index, T o) {
         if (lock > 0 || list.isEmpty()) {
             copyAndAdd(index, o);
@@ -102,7 +103,6 @@
 		}
 	}
 	
-	@Override
     public synchronized T remove(int index) {
         if (lock > 0) {
             return copyAndRemove(index);
@@ -141,7 +141,6 @@
 		}
 	}
 	
-	@Override
     public boolean addAll(int index, Collection<? extends T> c) {
         if (lock > 0 || list.isEmpty()) {
             return copyAndAddAll(index, c);
@@ -224,40 +223,53 @@
 		return list.toString();
 	}
 
-    @Override
     public T get(int index) {
         return list.get(index);
     }
 
-    @Override
     public int indexOf(Object o) {
         return list.indexOf(o);
     }
 
-    @Override
     public int lastIndexOf(Object o) {
         return list.lastIndexOf(o);
     }
 
-    @Override
     public ListIterator<T> listIterator() {
     	throw new UnsupportedOperationException();
     }
 
-    @Override
     public ListIterator<T> listIterator(int index) {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public T set(int index, T o) {
         return list.set(index, o);
     }
 
-    @Override
     public List<T> subList(int fromIndex, int toIndex) {
         throw new UnsupportedOperationException();
     };
 	
-	
+	private class LIterator implements Iterator<T> {
+	    public List<T> list;
+	    private Iterator<T> it;
+	    
+	    public LIterator(List<T> list){
+	        this.list = list;
+	        this.it = list.iterator();
+	    }
+
+        public boolean hasNext() {
+            return it.hasNext();
+        }
+
+        public T next() {
+            return it.next();
+        }
+
+        public void remove() {
+            it.remove();
+        }
+	}
 }

Property changes on: .
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /branches/4.1.9/src/cog:r3174-3353




More information about the Swift-commit mailing list