avoiding deadlocks/infinite wait on sftp when connection protocol fails

Bernardo F Costa <[email protected]>
Newsgroups gmane.comp.java.sshtools.user
Message-ID <[email protected]>
Hello ! This message is about two related issues: an
error on connection protocol at closing channels and
message store closing. 

I've been making some tests with SftpClient operations
and everytime an Exception is thrown at Connection
Protocol (being more precise, a SocketException:
"Connection reset"), the application hangs. The reason
of it is about message store closing, which should be
done when his related communication channel closes.
This is one part of the problem.
The other part is that I found that connection
protocol has several different channels opened and
when it fails, it should close them all. Occurs that
while trying to close active channels another
exception occurs, and connection protocol often leaves
several channels opened. And here the corretion to
this problem has two possibilities: mantain the
sources compatible to java versions before 1.5 or not.
In my opinion, the solution java 1.5 offers is better,
but for compatibility issues, I'll offer both
solutions in a patch file. The attached files to this
message also offers correction to two other bugs I
have posted before at this same list.

For java 1.5 and above one should try this patch:
---
src/com/sshtools/j2ssh/connection/ConnectionProtocol.java
2003-12-08 10:54:48.000000000 -0200
+++
src/com/sshtools/j2ssh/connection/ConnectionProtocol.java
2007-01-09 18:52:38.000000000 -0200
@@ -39,6 +39,7 @@
 import java.io.IOException;
 
 import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
@@ -53,7 +54,7 @@
 public class ConnectionProtocol extends AsyncService
{
     private static Log log =
LogFactory.getLog(ConnectionProtocol.class);
     private HashSet reusableChannels = new HashSet();
-    private Map activeChannels = new HashMap();
+    private Map activeChannels = new
ConcurrentHashMap();
     private Map allowedChannels = new HashMap();
     private Map globalRequests = new HashMap();
     private long nextChannelId = 0;
@@ -135,13 +136,13 @@
     }
 
     private Long getChannelId() {
-        synchronized (activeChannels) {
+       // synchronized (activeChannels) { 
             if (reusableChannels.size() <= 0) {
                 return new Long(nextChannelId++);
             } else {
                 return (Long)
reusableChannels.iterator().next();
             }
-        }
+        //}
     }
 
     /**
@@ -157,7 +158,7 @@
      */
     public synchronized boolean openChannel(Channel
channel,
         ChannelEventListener eventListener) throws
IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             Long channelId = getChannelId();
 
             // Create the message
@@ -202,15 +203,16 @@
                 throw new SshException(
                     "The thread was interrupted
whilst waiting for a connection protocol message");
             }
-        }
+        //}
     }
 
     /**
      *
      */
-    protected void onStop() {
+    protected synchronized void onStop() {
         log.info("Closing all active channels");
-
+	//	synchronized (activeChannels) {
+		log.info("thread has
"+activeChannels.values().size()+" active channels to
stop");
         try {
             Channel channel;
 
@@ -227,9 +229,11 @@
                 }
             }
         } catch (Throwable t) {
+			log.error("Unable to close all channels:
"+t.getMessage(),t);
         }
 
         activeChannels.clear();
+	//	}
     }
 
     /**
@@ -288,7 +292,7 @@
      * @throws IOException
      */
     public void sendChannelEOF(Channel channel)
throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             if
(!activeChannels.containsValue(channel)) {
                 throw new IOException(
                     "Attempt to send EOF for a non
existent channel " +
@@ -301,7 +305,7 @@
 
             SshMsgChannelEOF msg = new
SshMsgChannelEOF(channel.getRemoteChannelId());
             transport.sendMessage(msg, this);
-        }
+       // }
     }
 
     /**
@@ -755,16 +759,15 @@
     }
 
     private Channel getChannel(long channelId) throws
IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             Long l = new Long(channelId);
 
             if (!activeChannels.containsKey(l)) {
                 throw new IOException("Non existent
channel " + l.toString() +
                     " requested");
             }
-
-            return (Channel) activeChannels.get(l);
-        }
+			return (Channel) activeChannels.get(l);
+        //}
     }
 
     private void onMsgChannelClose(SshMsgChannelClose
msg)
@@ -831,7 +834,7 @@
 
     private void onMsgChannelOpen(SshMsgChannelOpen
msg)
         throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             log.info("Request for " +
msg.getChannelType() +
                 " channel recieved");
 
@@ -874,7 +877,7 @@
                    
SshMsgChannelOpenFailure.SSH_OPEN_CONNECT_FAILED,
                     ice.getMessage(), "");
             }
-        }
+        //}
     }
 
     private void
onMsgChannelRequest(SshMsgChannelRequest msg)
@@ -922,7 +925,7 @@
      * @param channel
      */
     protected void freeChannel(Channel channel) {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             log.info("Freeing channel " +
                
String.valueOf(channel.getLocalChannelId()) + " [" +
                 channel.getName() + "]");
@@ -931,6 +934,6 @@
             activeChannels.remove(channelId);
 
             //reusableChannels.add(channelId);
-        }
+        //}
     }
 }
---
src/com/sshtools/j2ssh/subsystem/SubsystemChannel.java
2003-12-08 10:54:50.000000000 -0200
+++
src/com/sshtools/j2ssh/subsystem/SubsystemChannel.java
2007-01-09 22:05:42.000000000 -0200
@@ -151,6 +151,7 @@
     }
 
     protected void onChannelClose() throws
java.io.IOException {
+		if (messageStore != null) messageStore.close();
     }
 
     public byte[] getChannelOpenData() {

For java until 1.4, perhaps it better patch the
sources with this file:
---
src/com/sshtools/j2ssh/connection/ConnectionProtocol.java
2003-12-08 10:54:48.000000000 -0200
+++
src/com/sshtools/j2ssh/connection/ConnectionProtocol.java
2007-01-09 21:44:46.000000000 -0200
@@ -39,6 +39,7 @@
 import java.io.IOException;
 
 import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
@@ -53,7 +54,7 @@
 public class ConnectionProtocol extends AsyncService
{
     private static Log log =
LogFactory.getLog(ConnectionProtocol.class);
     private HashSet reusableChannels = new HashSet();
-    private Map activeChannels = new HashMap();
+    private Hashtable activeChannels = new
Hashtable();
     private Map allowedChannels = new HashMap();
     private Map globalRequests = new HashMap();
     private long nextChannelId = 0;
@@ -135,13 +136,13 @@
     }
 
     private Long getChannelId() {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) { 
             if (reusableChannels.size() <= 0) {
                 return new Long(nextChannelId++);
             } else {
                 return (Long)
reusableChannels.iterator().next();
             }
-        }
+        //}
     }
 
     /**
@@ -157,7 +158,7 @@
      */
     public synchronized boolean openChannel(Channel
channel,
         ChannelEventListener eventListener) throws
IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             Long channelId = getChannelId();
 
             // Create the message
@@ -202,20 +203,23 @@
                 throw new SshException(
                     "The thread was interrupted
whilst waiting for a connection protocol message");
             }
-        }
+       // }
     }
 
     /**
      *
      */
-    protected void onStop() {
+    protected synchronized void onStop() {
         log.info("Closing all active channels");
-
+		//synchronized (activeChannels) {
+		log.info("thread has
"+activeChannels.values().size()+" active channels to
stop");
         try {
             Channel channel;
 
-            for (Iterator x =
activeChannels.values().iterator(); x.hasNext();) {
-                channel = (Channel) x.next();
+			for (java.util.Enumeration x =
activeChannels.elements() ; x.hasMoreElements() ;) {
+            //for (Iterator x =
activeChannels.values().iterator(); x.hasNext();) {
+                //channel = (Channel) x.next();
+                channel = (Channel) x.nextElement();
 
                 if (channel != null) {
                     if (log.isDebugEnabled()) {
@@ -227,9 +231,11 @@
                 }
             }
         } catch (Throwable t) {
+			log.error("Unable to close all channels:
"+t.getMessage(),t);
         }
 
         activeChannels.clear();
+		//}
     }
 
     /**
@@ -242,7 +248,7 @@
      */
     public synchronized void sendChannelData(Channel
channel, byte[] data)
         throws IOException {
-        synchronized (channel.getState()) {
+        //synchronized (channel.getState()) {
             if (log.isDebugEnabled()) {
                 log.debug("Sending " +
String.valueOf(data.length) +
                     " bytes for channel id " +
@@ -277,7 +283,7 @@
                                 }*/
                 sent += block;
             }
-        }
+        //}
     }
 
     /**
@@ -288,7 +294,7 @@
      * @throws IOException
      */
     public void sendChannelEOF(Channel channel)
throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             if
(!activeChannels.containsValue(channel)) {
                 throw new IOException(
                     "Attempt to send EOF for a non
existent channel " +
@@ -301,7 +307,7 @@
 
             SshMsgChannelEOF msg = new
SshMsgChannelEOF(channel.getRemoteChannelId());
             transport.sendMessage(msg, this);
-        }
+        //}
     }
 
     /**
@@ -755,16 +761,15 @@
     }
 
     private Channel getChannel(long channelId) throws
IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             Long l = new Long(channelId);
 
             if (!activeChannels.containsKey(l)) {
                 throw new IOException("Non existent
channel " + l.toString() +
                     " requested");
             }
-
-            return (Channel) activeChannels.get(l);
-        }
+			return (Channel) activeChannels.get(l);
+        //}
     }
 
     private void onMsgChannelClose(SshMsgChannelClose
msg)
@@ -831,7 +836,7 @@
 
     private void onMsgChannelOpen(SshMsgChannelOpen
msg)
         throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             log.info("Request for " +
msg.getChannelType() +
                 " channel recieved");
 
@@ -874,7 +879,7 @@
                    
SshMsgChannelOpenFailure.SSH_OPEN_CONNECT_FAILED,
                     ice.getMessage(), "");
             }
-        }
+        //}
     }
 
     private void
onMsgChannelRequest(SshMsgChannelRequest msg)
@@ -922,7 +927,7 @@
      * @param channel
      */
     protected void freeChannel(Channel channel) {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             log.info("Freeing channel " +
                
String.valueOf(channel.getLocalChannelId()) + " [" +
                 channel.getName() + "]");
@@ -931,6 +936,6 @@
             activeChannels.remove(channelId);
 
             //reusableChannels.add(channelId);
-        }
+        //}
     }
 }
---
src/com/sshtools/j2ssh/subsystem/SubsystemChannel.java
2003-12-08 10:54:50.000000000 -0200
+++
src/com/sshtools/j2ssh/subsystem/SubsystemChannel.java
2007-01-09 22:05:42.000000000 -0200
@@ -151,6 +151,7 @@
     }
 
     protected void onChannelClose() throws
java.io.IOException {
+		if (messageStore != null) messageStore.close();
     }
 
     public byte[] getChannelOpenData() {


__________________________________________________
Fale com seus amigos  de graça com o novo Yahoo! Messenger 
http://br.messenger.yahoo.com/

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV

_______________________________________________
Sshtools-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sshtools-users
patch15.txt (text/plain, 6 KB)
--- src/com/sshtools/j2ssh/transport/TransportProtocolCommon.java	2003-12-08 10:54:52.000000000 -0200
+++ src/com/sshtools/j2ssh/transport/TransportProtocolCommon.java	2007-01-07 11:57:07.000000000 -0200
@@ -1052,6 +1052,7 @@
             provider.close();
         } catch (IOException ioe) {
         }
+	state.setValue(TransportProtocolState.DISCONNECTED);
     }
 
     private byte[] makeSshKey(char chr) throws IOException {
--- src/com/sshtools/j2ssh/transport/TransportProtocolOutputStream.java	2003-12-08 10:54:52.000000000 -0200
+++ src/com/sshtools/j2ssh/transport/TransportProtocolOutputStream.java	2007-01-06 22:37:53.000000000 -0200
@@ -178,7 +178,7 @@
             out.write(message.toByteArray());
 
             out.flush();
-            algorithms.release();
+            //algorithms.release(); an exception at out.write an deadlock !!!
 
             // Increment the sequence no
             if (sequenceNo < sequenceWrapLimit) {
@@ -191,6 +191,8 @@
                 throw new TransportProtocolException("IO Error on socket: " +
                     ioe.getMessage());
             }
-        }
+        } finally {
+	   algorithms.release();
+	}
     }
 }
--- src/com/sshtools/j2ssh/connection/ConnectionProtocol.java	2003-12-08 10:54:48.000000000 -0200
+++ src/com/sshtools/j2ssh/connection/ConnectionProtocol.java	2007-01-09 18:52:38.000000000 -0200
@@ -39,6 +39,7 @@
 import java.io.IOException;
 
 import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
@@ -53,7 +54,7 @@
 public class ConnectionProtocol extends AsyncService {
     private static Log log = LogFactory.getLog(ConnectionProtocol.class);
     private HashSet reusableChannels = new HashSet();
-    private Map activeChannels = new HashMap();
+    private Map activeChannels = new ConcurrentHashMap();
     private Map allowedChannels = new HashMap();
     private Map globalRequests = new HashMap();
     private long nextChannelId = 0;
@@ -135,13 +136,13 @@
     }
 
     private Long getChannelId() {
-        synchronized (activeChannels) {
+       // synchronized (activeChannels) { 
             if (reusableChannels.size() <= 0) {
                 return new Long(nextChannelId++);
             } else {
                 return (Long) reusableChannels.iterator().next();
             }
-        }
+        //}
     }
 
     /**
@@ -157,7 +158,7 @@
      */
     public synchronized boolean openChannel(Channel channel,
         ChannelEventListener eventListener) throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             Long channelId = getChannelId();
 
             // Create the message
@@ -202,15 +203,16 @@
                 throw new SshException(
                     "The thread was interrupted whilst waiting for a connection protocol message");
             }
-        }
+        //}
     }
 
     /**
      *
      */
-    protected void onStop() {
+    protected synchronized void onStop() {
         log.info("Closing all active channels");
-
+	//	synchronized (activeChannels) {
+		log.info("thread has "+activeChannels.values().size()+" active channels to stop");
         try {
             Channel channel;
 
@@ -227,9 +229,11 @@
                 }
             }
         } catch (Throwable t) {
+			log.error("Unable to close all channels: "+t.getMessage(),t);
         }
 
         activeChannels.clear();
+	//	}
     }
 
     /**
@@ -288,7 +292,7 @@
      * @throws IOException
      */
     public void sendChannelEOF(Channel channel) throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             if (!activeChannels.containsValue(channel)) {
                 throw new IOException(
                     "Attempt to send EOF for a non existent channel " +
@@ -301,7 +305,7 @@
 
             SshMsgChannelEOF msg = new SshMsgChannelEOF(channel.getRemoteChannelId());
             transport.sendMessage(msg, this);
-        }
+       // }
     }
 
     /**
@@ -755,16 +759,15 @@
     }
 
     private Channel getChannel(long channelId) throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             Long l = new Long(channelId);
 
             if (!activeChannels.containsKey(l)) {
                 throw new IOException("Non existent channel " + l.toString() +
                     " requested");
             }
-
-            return (Channel) activeChannels.get(l);
-        }
+			return (Channel) activeChannels.get(l);
+        //}
     }
 
     private void onMsgChannelClose(SshMsgChannelClose msg)
@@ -831,7 +834,7 @@
 
     private void onMsgChannelOpen(SshMsgChannelOpen msg)
         throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             log.info("Request for " + msg.getChannelType() +
                 " channel recieved");
 
@@ -874,7 +877,7 @@
                     SshMsgChannelOpenFailure.SSH_OPEN_CONNECT_FAILED,
                     ice.getMessage(), "");
             }
-        }
+        //}
     }
 
     private void onMsgChannelRequest(SshMsgChannelRequest msg)
@@ -922,7 +925,7 @@
      * @param channel
      */
     protected void freeChannel(Channel channel) {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             log.info("Freeing channel " +
                 String.valueOf(channel.getLocalChannelId()) + " [" +
                 channel.getName() + "]");
@@ -931,6 +934,6 @@
             activeChannels.remove(channelId);
 
             //reusableChannels.add(channelId);
-        }
+        //}
     }
 }
--- src/com/sshtools/j2ssh/subsystem/SubsystemChannel.java	2003-12-08 10:54:50.000000000 -0200
+++ src/com/sshtools/j2ssh/subsystem/SubsystemChannel.java	2007-01-09 22:05:42.000000000 -0200
@@ -151,6 +151,7 @@
     }
 
     protected void onChannelClose() throws java.io.IOException {
+		if (messageStore != null) messageStore.close();
     }
 
     public byte[] getChannelOpenData() {
patch14.txt (text/plain, 7 KB)
--- src/com/sshtools/j2ssh/transport/TransportProtocolCommon.java	2003-12-08 10:54:52.000000000 -0200
+++ src/com/sshtools/j2ssh/transport/TransportProtocolCommon.java	2007-01-07 11:57:07.000000000 -0200
@@ -1052,6 +1052,7 @@
             provider.close();
         } catch (IOException ioe) {
         }
+	state.setValue(TransportProtocolState.DISCONNECTED);
     }
 
     private byte[] makeSshKey(char chr) throws IOException {
--- src/com/sshtools/j2ssh/transport/TransportProtocolOutputStream.java	2003-12-08 10:54:52.000000000 -0200
+++ src/com/sshtools/j2ssh/transport/TransportProtocolOutputStream.java	2007-01-06 22:37:53.000000000 -0200
@@ -178,7 +178,7 @@
             out.write(message.toByteArray());
 
             out.flush();
-            algorithms.release();
+            //algorithms.release(); an exception at out.write an deadlock !!!
 
             // Increment the sequence no
             if (sequenceNo < sequenceWrapLimit) {
@@ -191,6 +191,8 @@
                 throw new TransportProtocolException("IO Error on socket: " +
                     ioe.getMessage());
             }
-        }
+        } finally {
+	   algorithms.release();
+	}
     }
 }
--- src/com/sshtools/j2ssh/connection/ConnectionProtocol.java	2003-12-08 10:54:48.000000000 -0200
+++ src/com/sshtools/j2ssh/connection/ConnectionProtocol.java	2007-01-09 21:44:46.000000000 -0200
@@ -39,6 +39,7 @@
 import java.io.IOException;
 
 import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
@@ -53,7 +54,7 @@
 public class ConnectionProtocol extends AsyncService {
     private static Log log = LogFactory.getLog(ConnectionProtocol.class);
     private HashSet reusableChannels = new HashSet();
-    private Map activeChannels = new HashMap();
+    private Hashtable activeChannels = new Hashtable();
     private Map allowedChannels = new HashMap();
     private Map globalRequests = new HashMap();
     private long nextChannelId = 0;
@@ -135,13 +136,13 @@
     }
 
     private Long getChannelId() {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) { 
             if (reusableChannels.size() <= 0) {
                 return new Long(nextChannelId++);
             } else {
                 return (Long) reusableChannels.iterator().next();
             }
-        }
+        //}
     }
 
     /**
@@ -157,7 +158,7 @@
      */
     public synchronized boolean openChannel(Channel channel,
         ChannelEventListener eventListener) throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             Long channelId = getChannelId();
 
             // Create the message
@@ -202,20 +203,23 @@
                 throw new SshException(
                     "The thread was interrupted whilst waiting for a connection protocol message");
             }
-        }
+       // }
     }
 
     /**
      *
      */
-    protected void onStop() {
+    protected synchronized void onStop() {
         log.info("Closing all active channels");
-
+		//synchronized (activeChannels) {
+		log.info("thread has "+activeChannels.values().size()+" active channels to stop");
         try {
             Channel channel;
 
-            for (Iterator x = activeChannels.values().iterator(); x.hasNext();) {
-                channel = (Channel) x.next();
+			for (java.util.Enumeration x = activeChannels.elements() ; x.hasMoreElements() ;) {
+            //for (Iterator x = activeChannels.values().iterator(); x.hasNext();) {
+                //channel = (Channel) x.next();
+                channel = (Channel) x.nextElement();
 
                 if (channel != null) {
                     if (log.isDebugEnabled()) {
@@ -227,9 +231,11 @@
                 }
             }
         } catch (Throwable t) {
+			log.error("Unable to close all channels: "+t.getMessage(),t);
         }
 
         activeChannels.clear();
+		//}
     }
 
     /**
@@ -242,7 +248,7 @@
      */
     public synchronized void sendChannelData(Channel channel, byte[] data)
         throws IOException {
-        synchronized (channel.getState()) {
+        //synchronized (channel.getState()) {
             if (log.isDebugEnabled()) {
                 log.debug("Sending " + String.valueOf(data.length) +
                     " bytes for channel id " +
@@ -277,7 +283,7 @@
                                 }*/
                 sent += block;
             }
-        }
+        //}
     }
 
     /**
@@ -288,7 +294,7 @@
      * @throws IOException
      */
     public void sendChannelEOF(Channel channel) throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             if (!activeChannels.containsValue(channel)) {
                 throw new IOException(
                     "Attempt to send EOF for a non existent channel " +
@@ -301,7 +307,7 @@
 
             SshMsgChannelEOF msg = new SshMsgChannelEOF(channel.getRemoteChannelId());
             transport.sendMessage(msg, this);
-        }
+        //}
     }
 
     /**
@@ -755,16 +761,15 @@
     }
 
     private Channel getChannel(long channelId) throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             Long l = new Long(channelId);
 
             if (!activeChannels.containsKey(l)) {
                 throw new IOException("Non existent channel " + l.toString() +
                     " requested");
             }
-
-            return (Channel) activeChannels.get(l);
-        }
+			return (Channel) activeChannels.get(l);
+        //}
     }
 
     private void onMsgChannelClose(SshMsgChannelClose msg)
@@ -831,7 +836,7 @@
 
     private void onMsgChannelOpen(SshMsgChannelOpen msg)
         throws IOException {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             log.info("Request for " + msg.getChannelType() +
                 " channel recieved");
 
@@ -874,7 +879,7 @@
                     SshMsgChannelOpenFailure.SSH_OPEN_CONNECT_FAILED,
                     ice.getMessage(), "");
             }
-        }
+        //}
     }
 
     private void onMsgChannelRequest(SshMsgChannelRequest msg)
@@ -922,7 +927,7 @@
      * @param channel
      */
     protected void freeChannel(Channel channel) {
-        synchronized (activeChannels) {
+        //synchronized (activeChannels) {
             log.info("Freeing channel " +
                 String.valueOf(channel.getLocalChannelId()) + " [" +
                 channel.getName() + "]");
@@ -931,6 +936,6 @@
             activeChannels.remove(channelId);
 
             //reusableChannels.add(channelId);
-        }
+        //}
     }
 }
--- src/com/sshtools/j2ssh/subsystem/SubsystemChannel.java	2003-12-08 10:54:50.000000000 -0200
+++ src/com/sshtools/j2ssh/subsystem/SubsystemChannel.java	2007-01-09 22:05:42.000000000 -0200
@@ -151,6 +151,7 @@
     }
 
     protected void onChannelClose() throws java.io.IOException {
+		if (messageStore != null) messageStore.close();
     }
 
     public byte[] getChannelOpenData() {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.