1 package com.atlassian.webdriver.testing.simpleserver;
2
3 import com.google.common.collect.ImmutableMap;
4 import com.google.common.collect.Maps;
5 import org.apache.commons.io.IOUtils;
6 import org.apache.commons.lang.Validate;
7 import org.mortbay.jetty.Handler;
8 import org.mortbay.jetty.Request;
9 import org.mortbay.jetty.Server;
10 import org.mortbay.jetty.handler.AbstractHandler;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13
14 import javax.annotation.Nonnull;
15 import javax.servlet.ServletException;
16 import javax.servlet.http.HttpServletRequest;
17 import javax.servlet.http.HttpServletResponse;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.net.ServerSocket;
21 import java.util.Map;
22
23
24
25
26
27
28
29 public class SimpleServer
30 {
31 private static final Logger logger = LoggerFactory.getLogger(SimpleServer.class);
32
33 private int port = 0;
34 private Server server = null;
35 private final Map<String,String> urlMappings;
36
37
38
39
40
41
42
43 public static void main(@Nonnull String... args) throws Exception
44 {
45 int preferredPort = 5555;
46 if (args.length > 0)
47 {
48 preferredPort = parsePort(args[0]);
49 }
50 SimpleServer server = new SimpleServer(preferredPort);
51 server.startServer();
52 int port = server.getPort();
53 logger.info("Server started: " + "http://localhost:" + port);
54 Runtime.getRuntime().addShutdownHook(new Thread(new ServerShutdown(server)));
55 }
56
57 public SimpleServer(int port)
58 {
59 this(Maps.<String, String>newHashMap(), port);
60 }
61
62 public SimpleServer()
63 {
64 this(Maps.<String, String>newHashMap());
65 }
66
67 public SimpleServer(@Nonnull Map<String, String> urlMappings) {
68 checkPort();
69 this.urlMappings = ImmutableMap.copyOf(urlMappings);
70 }
71
72 public SimpleServer(@Nonnull Map<String, String> urlMappings, int port)
73 {
74 Validate.isTrue(port > 0, "Port must be a positive number");
75 this.port = port;
76 this.urlMappings = ImmutableMap.copyOf(urlMappings);
77
78 checkPort();
79 }
80
81 public void stopServer() throws Exception
82 {
83 if (server != null)
84 {
85 server.stop();
86 }
87 }
88
89 public void startServer() throws Exception
90 {
91 Handler handler = new AbstractHandler()
92 {
93 public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
94 throws IOException, ServletException
95 {
96
97 String uri = request.getRequestURI();
98
99 if(uri.endsWith("css"))
100 {
101 response.setContentType("text/css");
102 }
103 else if(uri.endsWith("js"))
104 {
105 response.setContentType("application/javascript");
106 }
107 else
108 {
109 response.setContentType("text/html");
110 }
111
112
113 InputStream inputStream = null;
114 inputStream = getClass().getClassLoader().getResourceAsStream(uri.substring(1));
115
116 if(inputStream == null && urlMappings.containsKey(uri))
117 {
118 String filename = urlMappings.get(uri);
119 inputStream = getClass().getClassLoader().getResourceAsStream(filename);
120 }
121
122 if(inputStream != null)
123 {
124 String contents = IOUtils.toString(inputStream);
125
126 if (contents != null)
127 {
128 response.getWriter().print(contents);
129 }
130 else
131 {
132 response.getWriter().println("<h1>Cannot read file at: " + uri + "</h1>");
133 }
134 }
135
136 else
137 {
138 response.getWriter().println("<h1>File not found at: " + uri + "</h1>");
139 }
140
141 response.setStatus(HttpServletResponse.SC_OK);
142
143 ((Request)request).setHandled(true);
144 }
145 };
146
147 server = new Server(port);
148 server.setHandler(handler);
149 server.start();
150 }
151
152 public int getPort()
153 {
154 return port;
155 }
156
157 private static int parsePort(String port)
158 {
159 try
160 {
161 return Integer.parseInt(port);
162 }
163 catch (NumberFormatException e)
164 {
165 throw new RuntimeException("Could not parse port, not a number: " + port, e);
166 }
167 }
168
169 private void checkPort()
170 {
171 ServerSocket socket = null;
172 try
173 {
174 socket = new ServerSocket(port);
175 this.port = socket.getLocalPort();
176 }
177 catch (IOException e)
178 {
179 throw new RuntimeException("Error opening socket, port: " + port + " may already be in use", e);
180 }
181 finally
182 {
183 if (socket != null)
184 {
185 try
186 {
187 socket.close();
188 }
189 catch (IOException e)
190 {
191 logger.error("Error closing sockets", e);
192 }
193 }
194 }
195 }
196
197 private static final class ServerShutdown implements Runnable
198 {
199 private final SimpleServer server;
200
201 private ServerShutdown(SimpleServer server)
202 {
203 this.server = server;
204 }
205
206 @Override
207 public void run()
208 {
209 try
210 {
211 logger.info("Shutting down SimpleServer at port " + server.getPort());
212 server.stopServer();
213 } catch (Exception e)
214 {
215 throw new RuntimeException(e);
216 }
217 }
218 }
219 }