| 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