title: Using Spring threading and TaskExecutor, how do I know when a thread is finished?
body: <p>Alright, possible a naive question here. I have a service that needs to log into multiple network devices, run a command on each and collect the results.  For speed, rather than collect the information on each device in sequence, I need to access them all concurrently and consume the results after they are done. </p>
  
  <p>Using the Spring framework and Jsch I'm quite easily able to query each device correctly.  Where I am running into some confusion is in trying to rewire the beans to use TaskExecutor to accomplish this. What I can't figure out how to do is how to know when the thread is finished.</p>
  
  <p>What I have so far is this:</p>
  
  <pre><code>public class RemoteCommand {
  
      private String user;
      private String host;
      private String password;
      private String command;
      private List&lt;String&gt; commandResults;
      private TaskExecutor taskExecutor;
  
      public RemoteCommand(String user, String host, String password, TaskExecutor taskExecutor) {
  
          setUser(user);
          setHost(host);
          setPassword(password);
          setTaskExecutor(taskExecutor);
      }
  
      /**
       * @param user the user to set
       */
      public void setUser(String user) {
          this.user = user;
      }
  
      /**
       * @return the user
       */
      public String getUser() {
          return user;
      }
  
      /**
       * @param host the host to set
       */
      public void setHost(String host) {
          this.host = host;
      }
  
      /**
       * @return the host
       */
      public String getHost() {
          return host;
      }
  
      /**
       * @param password the password to set
       */
      public void setPassword(String password) {
          this.password = password;
      }
  
      /**
       * @return the password
       */
      public String getPassword() {
          return password;
      }
  
      /**
       * @param command the command to set
       */
      private void setCommand(String command) {
          this.command = command;
      }
  
      /**
       * @return the command
       */
      private String getCommand() {
          return command;
      }
  
      /**
       * @param commandResults the commandResults to set
       */
      private void setCommandResults(List&lt;String&gt; commandResults) {
          this.commandResults = commandResults;
      }
  
      /**
       * @return the commandResults
       */
      public List&lt;String&gt; getCommandResults(String command) {
          taskExecutor.execute(new CommandTask(command) );
  
          return commandResults;
      }
  
      /**
       * @param taskExecutor the taskExecutor to set
       */
      public void setTaskExecutor(TaskExecutor taskExecutor) {
          this.taskExecutor = taskExecutor;
      }
  
      /**
       * @return the taskExecutor
       */
      public TaskExecutor getTaskExecutor() {
          return taskExecutor;
      }
  
      private class CommandTask implements Runnable {
  
          public CommandTask(String command) {
              setCommand(command);
              System.out.println("test: " + getCommand());
          }
  
          /**
           * 
           * @param command
           */
          public void run() {
  
              List&lt;String&gt; results = new LinkedList&lt;String&gt;();
              String command = getCommand();
  
              try {
                  System.out.println("running");
                  JSch jsch = new JSch();
  
                  String user = getUser();
                  String host = getHost();
  
                  java.util.Properties config = new java.util.Properties(); 
                  config.put("StrictHostKeyChecking", "no");
  
                  host = host.substring(host.indexOf('@') + 1);
                  Session session = jsch.getSession(user, host, 22);
  
                  session.setPassword(getPassword());
                  session.setConfig(config);
                  session.connect();
  
                  Channel channel = session.openChannel("exec");
                  ((ChannelExec) channel).setCommand(command);
  
                  channel.setInputStream(null);
  
                  ((ChannelExec) channel).setErrStream(System.err);
  
                  InputStream in = channel.getInputStream();
  
                  channel.connect();
                  byte[] tmp = new byte[1024];
                  while (true) {
                      while (in.available() &gt; 0) {
                          int i = in.read(tmp, 0, 1024);
                          if (i &lt; 0)
                              break;
                          results.add(new String(tmp, 0, i));
                          System.out.print(new String(tmp, 0, i));
                      }
                      if (channel.isClosed()) {
                          //System.out.println("exit-status: "
                          //      + channel.getExitStatus());
                          break;
                      }
                      try {
                          Thread.sleep(1000);
                      } catch (Exception ee) {
                          ee.printStackTrace();
                      }
                  }
                  channel.disconnect();
                  session.disconnect();
              } catch (Exception e) {
                  System.out.println(e);
              }
              setCommandResults(results);
              System.out.println("finished running");
          }
      }
  }
  </code></pre>
  
  <p>Within my junit test I have:</p>
  
  <pre><code>@Test
      public void testRemoteExecution() {
  
          remoteCommand = (RemoteCommand) applicationContext.getBean("remoteCommand");
          remoteCommand.getCommandResults("scripts/something.pl xxx.xxx.xxx.xxx");
  
              //List&lt;String&gt; results = remoteCommand.getCommandResults("scripts/something.pl xxx.xxx.xxx.xxx");
          //for (String line : results) {
          //  System.out.println(line.trim());
          //}
      }
  </code></pre>
  
  <p>My applicationContext.xml file:</p>
  
  <pre><code>    &lt;bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"&gt;
         &lt;property name="corePoolSize" value="5" /&gt;
         &lt;property name="maxPoolSize" value="10" /&gt;
         &lt;property name="queueCapacity" value="25" /&gt;
      &lt;/bean&gt;        
  
  &lt;!-- ******************** --&gt;
  &lt;!--      Utilities       --&gt;
  &lt;!-- ******************** --&gt;
  
       &lt;bean name="remoteCommand" class="com.xxx.ncc.sonet.utilities.RemoteCommand" scope="prototype"&gt;
          &lt;description&gt;Remote Command&lt;/description&gt;
          &lt;constructor-arg&gt;&lt;value&gt;${remote.user}&lt;/value&gt;&lt;/constructor-arg&gt;
          &lt;constructor-arg&gt;&lt;value&gt;${remote.host}&lt;/value&gt;&lt;/constructor-arg&gt;
          &lt;constructor-arg&gt;&lt;value&gt;${remote.password}&lt;/value&gt;&lt;/constructor-arg&gt;
          &lt;constructor-arg ref="taskExecutor" /&gt;
      &lt;/bean&gt; 
  </code></pre>
  
  <p>I get as far as the first println in the run() method. Then the test exits cleanly with no errors. I never get to the second println at the bottom of that routine. I've looked at this thread <a href="https://stackoverflow.com/questions/702415/how-to-know-if-other-threads-have-finished">here</a>, which was very useful, but not implemented in a Spring specific fashion.  I'm sure I'm missing something simple, or have completely run off the rails here.  Any help is appreciated.</p>