Server.java |
1 package com.palantir.blog.processspawner; 2 /* 3 * All source code and information in this file is made 4 * available under the following licensing terms: 5 * 6 * Copyright (c) 2009, Palantir Technologies, Inc. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions are 11 * met: 12 * 13 * * Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * * Redistributions in binary form must reproduce the above 17 * copyright notice, this list of conditions and the following 18 * disclaimer in the documentation and/or other materials provided 19 * with the distribution. 20 * 21 * * Neither the name of Palantir Technologies, Inc. nor the names of its 22 * contributors may be used to endorse or promote products derived 23 * from this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * 38 */ 39 import java.io.BufferedReader; 40 import java.io.IOException; 41 import java.io.InputStreamReader; 42 import java.io.OutputStreamWriter; 43 import java.io.Writer; 44 import java.net.InetAddress; 45 import java.net.ServerSocket; 46 import java.net.Socket; 47 import java.net.SocketException; 48 import java.util.concurrent.Executors; 49 50 import javax.net.ServerSocketFactory; 51 import javax.net.SocketFactory; 52 53 /** 54 * A server that implements a simple <a href='http://en.wikipedia.org/wiki/ACK_(computing)'>ACK</a> 55 * protocol: every message (a string matching the regex: /^.*$/) is answered with the string "ACK\n". 56 * If the message "SHUTDOWN" is encountered, the process will exit. 57 * 58 * This server spawns a handler thread for every incoming connection. A more robust implementation 59 * would use {@link Executors} to control the number of concurrent threads. 60 * @author regs 61 * 62 */ 63 public class Server extends Thread { 64 65 public static final String ACK = "ACK"; 66 public static final int SERVERPORT = 10001; 67 /** 68 * The only special command in this protocol. If this message is received, the process will 69 * immediately exit, after ACKing, of course. 70 */ 71 public static final String SHUTDOWN = "SHUTDOWN"; 72 73 public static void main(String[] args) throws Exception { 74 75 ServerSocketFactory socketFactory = ServerSocketFactory.getDefault(); 76 ServerSocket serverSocket = socketFactory.createServerSocket(SERVERPORT); 77 78 // infinite is exited by handler thread calling System.exit() 79 while(true) { 80 try { 81 System.out.println("Waiting for connection"); 82 Socket connection = serverSocket.accept(); 83 System.out.println("Spawning socket handler"); 84 new Server(connection); 85 } catch (Exception e) { 86 e.printStackTrace(); 87 } 88 } 89 } 90 91 /** 92 * Thread-naming counter. 93 */ 94 static int count = 0; 95 Socket connection; 96 int id = 0; 97 98 /** 99 * Starts a handler thread to handle the passed connection. 100 * 101 * @param connection 102 * @throws IOException 103 */ 104 public Server(Socket connection) throws IOException { 105 synchronized (getClass()) { 106 id = ++count; 107 } 108 setName("Socket Handler" + id); 109 this.connection = connection; 110 this.start(); 111 } 112 113 private void out(String message) { 114 System.out.println("[" + getName() + "]: " + message); 115 } 116 117 /** 118 * The handler routine that actually implements the protocol. 119 */ 120 @Override 121 public void run() { 122 try { 123 BufferedReader r = new BufferedReader(new InputStreamReader(connection.getInputStream())); 124 Writer w = new OutputStreamWriter(connection.getOutputStream()); 125 126 String line = null; 127 do { 128 line = r.readLine(); 129 130 if(line != null) { 131 out("Got message: " + line); 132 w.write(ACK + "\n"); 133 w.flush(); 134 135 // check for shutdown 136 if(SHUTDOWN.equals(line)) { 137 // kill it 138 System.exit(0); 139 } 140 } 141 } while ( line != null ); 142 } catch(SocketException e) { 143 if("Connection Reset".equals(e.getMessage())) { 144 // ignore - this is produced by the 145 // probing code to see if the server is up. 146 } 147 } catch (Exception e) { 148 System.out.println("Error while handling socket"); 149 e.printStackTrace(); 150 } 151 } 152 153 /** 154 * Repeats a TCP connection check every <em>sleepTime</em> milliseconds until it either succeeds 155 * or times out after <em>timeout</em> milliseconds. 156 * 157 * @see Server#checkServerIsUp(InetAddress, int) An explanation of the TCP checking mechanism. 158 * 159 * @param timeout If no check is successful after this many milliseconds has passed, fail the 160 * overall checking process. 161 * @param sleepTime How long to wait (in milliseconds) between checks of the service. 162 * @param server address of server to check. 163 * @param port port to check. 164 * @return true if a connection attempt succeeds, false in the case of error or 165 * no connection attempt successful. 166 */ 167 public static boolean checkServerIsUp(long timeout, long sleepTime,InetAddress server, int port ) { 168 long start = System.currentTimeMillis(); 169 while((System.currentTimeMillis() - start) < timeout){ 170 if(!checkServerIsUp(server, port)){ 171 try { 172 Thread.sleep(sleepTime); 173 } catch (InterruptedException e) { 174 return false; 175 } 176 } 177 else{ 178 return true; 179 } 180 } 181 return false; 182 } 183 184 /** 185 * Performs a simple TCP connection check to the specified address and port. 186 * 187 * @param server address of the server to contact. 188 * @param port TCP port to connect to on the specified server. 189 * @return true if that port is accepting connections, 190 * false in all other cases: not listening and/or connection error. 191 */ 192 public static boolean checkServerIsUp(InetAddress server, int port) { 193 Socket sock = null; 194 try { 195 sock = SocketFactory.getDefault().createSocket(server, port); 196 sock.setSoLinger(true, 0); 197 return true; 198 } catch (IOException e) { 199 return false; 200 } 201 finally{ 202 if(sock != null){ 203 try { 204 sock.close(); 205 } catch (IOException e) { 206 // don't care 207 } 208 } 209 } 210 } 211} 212