View Javadoc

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   * Simple server for serving up html pages and other resources from class path. Uses an an embedded Jetty server under
25   * the covers.
26   *
27   * @since 2.2
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       * Main method to run the server standalone.
39       *
40       * @param args program arguments
41       * @throws Exception any exception
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 }