private static Object sendMessage(String ipAddr, int port, Object msg)
			throws Exception {
		Object reply = null;
		Socket socket = null;
		ObjectOutputStream output = null;
		ObjectInputStream input = null;

		// open the socket to talk to registry/server
		try {
			// socket cache hit here
			if (socketCache.containsSocket(ipAddr, port)) {
				SocketInfo socketInfo = socketCache.getSocketInfo(ipAddr, port);
				output = socketInfo.output;
				try {
					output.writeObject(msg);
					output.flush();

					input = socketInfo.input;
					reply = input.readObject();
				}
				// if fails to connect the cached socket and its streams, try
				// another time
				catch (Exception ex) {

					if (ex instanceof java.io.EOFException
							|| ex instanceof java.net.SocketException) {
						socket = new Socket(ipAddr, port);
						output = new ObjectOutputStream(
								socket.getOutputStream());
						output.writeObject(msg);
						output.flush();

						input = new ObjectInputStream(socket.getInputStream());
						reply = input.readObject();
						socketCache.putSocket(socket, input, output);
					} else {
						throw ex;
					}
				}
			}
			// socket cache miss here
			else {
				socket = new Socket(ipAddr, port);
				output = new ObjectOutputStream(socket.getOutputStream());
				output.writeObject(msg);
				output.flush();

				input = new ObjectInputStream(socket.getInputStream());
				reply = input.readObject();
				socketCache.putSocket(socket, input, output);

			}

		} catch (java.net.ConnectException e) {
			System.err.println("Server is down");
			return null;
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} catch (ClassNotFoundException ce) {
			// TODO Auto-generated catch block
			ce.printStackTrace();
			return null;
		}

		return reply;
	}