src/core/net/sf/basedb/util/jobagent/JobAgentConnection.java

Code
Comments
Other
Rev Date Author Line
2632 08 Sep 06 nicklas 1 /**
2632 08 Sep 06 nicklas 2   $Id$
2632 08 Sep 06 nicklas 3
3675 16 Aug 07 jari 4   Copyright (C) 2006 Nicklas Nordborg
2632 08 Sep 06 nicklas 5
2632 08 Sep 06 nicklas 6   This file is part of BASE - BioArray Software Environment.
2632 08 Sep 06 nicklas 7   Available at http://base.thep.lu.se/
2632 08 Sep 06 nicklas 8
2632 08 Sep 06 nicklas 9   BASE is free software; you can redistribute it and/or
2632 08 Sep 06 nicklas 10   modify it under the terms of the GNU General Public License
4479 05 Sep 08 jari 11   as published by the Free Software Foundation; either version 3
2632 08 Sep 06 nicklas 12   of the License, or (at your option) any later version.
2632 08 Sep 06 nicklas 13
2632 08 Sep 06 nicklas 14   BASE is distributed in the hope that it will be useful,
2632 08 Sep 06 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
2632 08 Sep 06 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2632 08 Sep 06 nicklas 17   GNU General Public License for more details.
2632 08 Sep 06 nicklas 18
2632 08 Sep 06 nicklas 19   You should have received a copy of the GNU General Public License
4515 11 Sep 08 jari 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
2632 08 Sep 06 nicklas 21 */
2632 08 Sep 06 nicklas 22 package net.sf.basedb.util.jobagent;
2632 08 Sep 06 nicklas 23
2632 08 Sep 06 nicklas 24 import java.io.IOException;
2634 12 Sep 06 nicklas 25 import java.net.InetAddress;
2632 08 Sep 06 nicklas 26 import java.net.InetSocketAddress;
2632 08 Sep 06 nicklas 27 import java.net.Socket;
5447 19 Oct 10 nicklas 28 import java.util.ArrayList;
5447 19 Oct 10 nicklas 29 import java.util.HashMap;
5447 19 Oct 10 nicklas 30 import java.util.List;
5447 19 Oct 10 nicklas 31 import java.util.Map;
5447 19 Oct 10 nicklas 32 import java.util.regex.Matcher;
5447 19 Oct 10 nicklas 33 import java.util.regex.Pattern;
2632 08 Sep 06 nicklas 34
2634 12 Sep 06 nicklas 35 import net.sf.basedb.util.SocketUtil;
2634 12 Sep 06 nicklas 36
2632 08 Sep 06 nicklas 37 /**
2632 08 Sep 06 nicklas 38   This class is used by client applications to communicate with job agents. 
2632 08 Sep 06 nicklas 39   It used to send commands to job agents. For example, it can send
2632 08 Sep 06 nicklas 40   <code>start</code>, <code>stop</code> and <code>info</code> commands.
2632 08 Sep 06 nicklas 41
2632 08 Sep 06 nicklas 42   @author nicklas
2632 08 Sep 06 nicklas 43   @version 2.0
2632 08 Sep 06 nicklas 44   @base.modified $Date$
2632 08 Sep 06 nicklas 45   @see JobAgentServerConnection
2632 08 Sep 06 nicklas 46 */
2632 08 Sep 06 nicklas 47 public class JobAgentConnection
7551 12 Dec 18 nicklas 48   implements AutoCloseable
2632 08 Sep 06 nicklas 49 {
2632 08 Sep 06 nicklas 50
5447 19 Oct 10 nicklas 51   /**
5447 19 Oct 10 nicklas 52     Regexp used to parse a typical answer.
5447 19 Oct 10 nicklas 53   */
5447 19 Oct 10 nicklas 54   private static final Pattern HEADER_REGEXP = Pattern.compile("(.*):(.*)");
5447 19 Oct 10 nicklas 55   
5447 19 Oct 10 nicklas 56   /**
5447 19 Oct 10 nicklas 57     Utility method for parsing a 'typical' answer from a job agent.
5447 19 Oct 10 nicklas 58     The typical answer is a string that contains a key-value pair on each
5447 19 Oct 10 nicklas 59     line, separated by a colon. This method allows multiple entries for
5447 19 Oct 10 nicklas 60     the same key.
5447 19 Oct 10 nicklas 61     
5447 19 Oct 10 nicklas 62     @param answer The answer
5447 19 Oct 10 nicklas 63     @return A map with the key as index and a list with all values
5447 19 Oct 10 nicklas 64     @since 2.16
5447 19 Oct 10 nicklas 65   */
5447 19 Oct 10 nicklas 66   public static Map<String, List<String>> parseAnswer(String answer)
5447 19 Oct 10 nicklas 67   {
5447 19 Oct 10 nicklas 68     Map<String, List<String>> headers = new HashMap<String, List<String>>();
5447 19 Oct 10 nicklas 69     Matcher m = HEADER_REGEXP.matcher(answer);
5447 19 Oct 10 nicklas 70     while (m.find())
5447 19 Oct 10 nicklas 71     {
5447 19 Oct 10 nicklas 72       String header = m.group(1);
5447 19 Oct 10 nicklas 73       String value = m.group(2);
5447 19 Oct 10 nicklas 74       List<String> values = headers.get(header);
5447 19 Oct 10 nicklas 75       if (values == null)
5447 19 Oct 10 nicklas 76       {
5447 19 Oct 10 nicklas 77         values = new ArrayList<String>();
5447 19 Oct 10 nicklas 78         headers.put(header, values);
5447 19 Oct 10 nicklas 79       }
5447 19 Oct 10 nicklas 80       values.add(value);
5447 19 Oct 10 nicklas 81     }
5447 19 Oct 10 nicklas 82     return headers;
5447 19 Oct 10 nicklas 83   }
5447 19 Oct 10 nicklas 84
5447 19 Oct 10 nicklas 85   /**
5447 19 Oct 10 nicklas 86     Utility method for parsing a 'typical' answer from a job agent.
5447 19 Oct 10 nicklas 87     The typical answer is a string that contains a key-value pair on each
5447 19 Oct 10 nicklas 88     line, separated by a colon. This method allows only a single entry for
5447 19 Oct 10 nicklas 89     the same key.
5447 19 Oct 10 nicklas 90     
5447 19 Oct 10 nicklas 91     @param answer The answer
5447 19 Oct 10 nicklas 92     @return A map with the key as index to the value
5447 19 Oct 10 nicklas 93     @since 2.16
5447 19 Oct 10 nicklas 94   */
5447 19 Oct 10 nicklas 95   public static Map<String, String> parseSimpleAnswer(String answer)
5447 19 Oct 10 nicklas 96   {
5447 19 Oct 10 nicklas 97     Map<String, String> headers = new HashMap<String, String>();
5447 19 Oct 10 nicklas 98     Matcher m = HEADER_REGEXP.matcher(answer);
5447 19 Oct 10 nicklas 99     while (m.find())
5447 19 Oct 10 nicklas 100     {
5447 19 Oct 10 nicklas 101       String header = m.group(1);
5447 19 Oct 10 nicklas 102       String value = m.group(2);
5447 19 Oct 10 nicklas 103       headers.put(header, value);
5447 19 Oct 10 nicklas 104     }
5447 19 Oct 10 nicklas 105     return headers;
5447 19 Oct 10 nicklas 106   }
5447 19 Oct 10 nicklas 107   
5447 19 Oct 10 nicklas 108   
2632 08 Sep 06 nicklas 109   private final String server;
2632 08 Sep 06 nicklas 110   private final int port;
2632 08 Sep 06 nicklas 111   private final int timeout;
2632 08 Sep 06 nicklas 112   
2632 08 Sep 06 nicklas 113   /**
2632 08 Sep 06 nicklas 114     Create a connection to the local job agent running on the specified port.
2632 08 Sep 06 nicklas 115     @param port The port the job agent is listening on
2632 08 Sep 06 nicklas 116     @param timeout The timeout in milliseconds
2632 08 Sep 06 nicklas 117   */
2632 08 Sep 06 nicklas 118   public JobAgentConnection(int port, int timeout)
2632 08 Sep 06 nicklas 119   {
2632 08 Sep 06 nicklas 120     this(null, port, timeout);
2632 08 Sep 06 nicklas 121   }
2632 08 Sep 06 nicklas 122   
2632 08 Sep 06 nicklas 123   /**
2632 08 Sep 06 nicklas 124     Create a connection to the job agent running on the specified server
2632 08 Sep 06 nicklas 125     and listening on the specified port.
2632 08 Sep 06 nicklas 126     @param server The server the job agent is running on, or null
2632 08 Sep 06 nicklas 127       to connect to the local host
2632 08 Sep 06 nicklas 128     @param port The port the job agent is listening on
2632 08 Sep 06 nicklas 129     @param timeout The timeout in milliseconds
2632 08 Sep 06 nicklas 130   */
2632 08 Sep 06 nicklas 131   public JobAgentConnection(String server, int port, int timeout)
2632 08 Sep 06 nicklas 132   {
2632 08 Sep 06 nicklas 133     this.server = server;
2632 08 Sep 06 nicklas 134     this.port = port;
2632 08 Sep 06 nicklas 135     this.timeout = timeout;
2632 08 Sep 06 nicklas 136   }
2632 08 Sep 06 nicklas 137   
2634 12 Sep 06 nicklas 138   /**
2634 12 Sep 06 nicklas 139     Send the <code>ping</code> command to the job agent. The only
2634 12 Sep 06 nicklas 140     answer to this command is <code>OK</code>.
2634 12 Sep 06 nicklas 141     @return OK if successful
2634 12 Sep 06 nicklas 142     @throws IOException If there is an error
2634 12 Sep 06 nicklas 143    */
2634 12 Sep 06 nicklas 144   public String sendPing()
2632 08 Sep 06 nicklas 145     throws IOException
2632 08 Sep 06 nicklas 146   {
2634 12 Sep 06 nicklas 147     return send("ping");
2632 08 Sep 06 nicklas 148   }
2634 12 Sep 06 nicklas 149
2634 12 Sep 06 nicklas 150   /**
2634 12 Sep 06 nicklas 151     Send a <code>start</code> request to the job agent.
2634 12 Sep 06 nicklas 152     @return OK if successful
2634 12 Sep 06 nicklas 153     @throws IOException If there is an error
2634 12 Sep 06 nicklas 154   */
2634 12 Sep 06 nicklas 155   public String sendStart()
2634 12 Sep 06 nicklas 156     throws IOException
2634 12 Sep 06 nicklas 157   {
2634 12 Sep 06 nicklas 158     return send("start");
2634 12 Sep 06 nicklas 159   }
2632 08 Sep 06 nicklas 160   
2634 12 Sep 06 nicklas 161   public String sendStop()
2632 08 Sep 06 nicklas 162     throws IOException
2632 08 Sep 06 nicklas 163   {
2634 12 Sep 06 nicklas 164     return send("stop");
2632 08 Sep 06 nicklas 165   }
2632 08 Sep 06 nicklas 166   
2634 12 Sep 06 nicklas 167   public String sendPause()
2632 08 Sep 06 nicklas 168     throws IOException
2632 08 Sep 06 nicklas 169   {
2634 12 Sep 06 nicklas 170     return send("pause");
2632 08 Sep 06 nicklas 171   }
2632 08 Sep 06 nicklas 172   
2634 12 Sep 06 nicklas 173   /**
2634 12 Sep 06 nicklas 174     Send an <code>info</code> or <code>status</code> request to the job agent.
2634 12 Sep 06 nicklas 175     The difference is that a <code>status</code> request doesn't return
2634 12 Sep 06 nicklas 176     information about cpu, memory or executing jobs.
2634 12 Sep 06 nicklas 177     
2634 12 Sep 06 nicklas 178     @param full If TRUE a <code>info</code> request is sent, othwerwise
2634 12 Sep 06 nicklas 179       a <code>status</code> request is sent
2634 12 Sep 06 nicklas 180     @return A <code>JobAgentInfo</code> object containing the 
2634 12 Sep 06 nicklas 181       information
2634 12 Sep 06 nicklas 182     @throws IOException If there is an error
2634 12 Sep 06 nicklas 183   */
2634 12 Sep 06 nicklas 184   public JobAgentInfo getInfo(boolean full)
2632 08 Sep 06 nicklas 185     throws IOException
2632 08 Sep 06 nicklas 186   {
2634 12 Sep 06 nicklas 187     return new JobAgentInfo(send(full ? "info" : "status"));
2632 08 Sep 06 nicklas 188   }
2632 08 Sep 06 nicklas 189   
2634 12 Sep 06 nicklas 190   /**
2634 12 Sep 06 nicklas 191     Close the connection to the job agent. In the current implementation
2634 12 Sep 06 nicklas 192     all connections are stateless and this method does nothing. This might change
2634 12 Sep 06 nicklas 193     in the future if a more advanced protocol is implemented. Clients should
2634 12 Sep 06 nicklas 194     always call this method.
2634 12 Sep 06 nicklas 195   */
7551 12 Dec 18 nicklas 196   @Override
2634 12 Sep 06 nicklas 197   public void close()
2634 12 Sep 06 nicklas 198   {}
2634 12 Sep 06 nicklas 199   
2634 12 Sep 06 nicklas 200   /**
5446 15 Oct 10 nicklas 201     Send a remote control command to the job agent.
2634 12 Sep 06 nicklas 202     
2634 12 Sep 06 nicklas 203     @param cmd The command to send
2634 12 Sep 06 nicklas 204     @return The answer
2634 12 Sep 06 nicklas 205     @throws IOException If there is an error
5446 15 Oct 10 nicklas 206     @since 2.16 (was private before that)
5446 15 Oct 10 nicklas 207   */
5446 15 Oct 10 nicklas 208   public String send(String cmd)
2632 08 Sep 06 nicklas 209     throws IOException
2632 08 Sep 06 nicklas 210   {
2634 12 Sep 06 nicklas 211     Socket socket = null;
2634 12 Sep 06 nicklas 212     String answer;
2632 08 Sep 06 nicklas 213     try
2632 08 Sep 06 nicklas 214     {
2634 12 Sep 06 nicklas 215       socket = connect();
2634 12 Sep 06 nicklas 216       answer = sendCmd(socket, cmd);
2632 08 Sep 06 nicklas 217     }
2632 08 Sep 06 nicklas 218     finally
2632 08 Sep 06 nicklas 219     {
2634 12 Sep 06 nicklas 220       SocketUtil.close(socket);
2632 08 Sep 06 nicklas 221     }
2634 12 Sep 06 nicklas 222     return answer;
2632 08 Sep 06 nicklas 223   }
2632 08 Sep 06 nicklas 224   
2634 12 Sep 06 nicklas 225   /**
2634 12 Sep 06 nicklas 226     Create a socket connection to the job agent.
2634 12 Sep 06 nicklas 227     @return The socket
2634 12 Sep 06 nicklas 228   */
2632 08 Sep 06 nicklas 229   private Socket connect()
2632 08 Sep 06 nicklas 230     throws IOException
2632 08 Sep 06 nicklas 231   {
2632 08 Sep 06 nicklas 232     Socket s = new Socket();
2634 12 Sep 06 nicklas 233     InetSocketAddress address = null;
2634 12 Sep 06 nicklas 234     if (server == null)
2632 08 Sep 06 nicklas 235     {
2634 12 Sep 06 nicklas 236       address = new InetSocketAddress(InetAddress.getLocalHost(), port);
2632 08 Sep 06 nicklas 237     }
2634 12 Sep 06 nicklas 238     else
2634 12 Sep 06 nicklas 239     {
2634 12 Sep 06 nicklas 240       address = new InetSocketAddress(server, port);
2634 12 Sep 06 nicklas 241     }
2634 12 Sep 06 nicklas 242     s.connect(address, timeout);
2634 12 Sep 06 nicklas 243     return s;
2632 08 Sep 06 nicklas 244   }
2632 08 Sep 06 nicklas 245
2634 12 Sep 06 nicklas 246   /**
2634 12 Sep 06 nicklas 247     Send a command to the job agent and return the answer. If the answer is
2634 12 Sep 06 nicklas 248     <code>FAILED</code> an exception will be thrown.
2634 12 Sep 06 nicklas 249     
2634 12 Sep 06 nicklas 250     @param socket The socket used for communicating with the job agent
2634 12 Sep 06 nicklas 251     @param cmd The command to send
2634 12 Sep 06 nicklas 252     @return The answer from the job server
2634 12 Sep 06 nicklas 253     @throws IOException If there is a communications error
2634 12 Sep 06 nicklas 254   */
2632 08 Sep 06 nicklas 255   private String sendCmd(Socket socket, String cmd)
2632 08 Sep 06 nicklas 256     throws IOException
2632 08 Sep 06 nicklas 257   {
2634 12 Sep 06 nicklas 258     // Send cmd to the job agent
2634 12 Sep 06 nicklas 259     SocketUtil.send(socket, cmd, true);
2632 08 Sep 06 nicklas 260     
2632 08 Sep 06 nicklas 261     // Read the answer
2634 12 Sep 06 nicklas 262     String answer = SocketUtil.read(socket, true);
2634 12 Sep 06 nicklas 263     
2634 12 Sep 06 nicklas 264     // The answer must start with OK, or it is an error
2634 12 Sep 06 nicklas 265     if (answer == null || answer.length() == 0)
2632 08 Sep 06 nicklas 266     {
2634 12 Sep 06 nicklas 267       throw new IOException("No answer from "+socket.getInetAddress().toString());
2632 08 Sep 06 nicklas 268     }
2634 12 Sep 06 nicklas 269     else if (answer.indexOf("OK") != 0)
2634 12 Sep 06 nicklas 270     {
2634 12 Sep 06 nicklas 271       throw new IOException(answer.toString());
2634 12 Sep 06 nicklas 272     }
2632 08 Sep 06 nicklas 273     return answer.toString();
2632 08 Sep 06 nicklas 274   }
2634 12 Sep 06 nicklas 275
2632 08 Sep 06 nicklas 276 }