PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/jboss-5.1.0/server/src/main/org/jboss/web/WebServer.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 550 lines | 320 code | 51 blank | 179 comment | 40 complexity | d2b3fc554adb6dd81a19304121e2998b MD5 | raw file
  1. /*
  2. * JBoss, Home of Professional Open Source.
  3. * Copyright 2006, Red Hat Middleware LLC, and individual contributors
  4. * as indicated by the @author tags. See the copyright.txt file in the
  5. * distribution for a full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.jboss.web;
  23. import java.io.BufferedInputStream;
  24. import java.io.BufferedReader;
  25. import java.io.ByteArrayOutputStream;
  26. import java.io.DataOutputStream;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.InputStreamReader;
  30. import java.net.InetAddress;
  31. import java.net.MalformedURLException;
  32. import java.net.ServerSocket;
  33. import java.net.Socket;
  34. import java.net.URL;
  35. import java.util.Properties;
  36. import org.jboss.logging.Logger;
  37. import org.jboss.util.threadpool.BasicThreadPool;
  38. import org.jboss.util.threadpool.BasicThreadPoolMBean;
  39. import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
  40. /**
  41. * A mini webserver that should be embedded in another application. It can
  42. * server any file that is available from classloaders that are registered with
  43. * it, including class-files.
  44. *
  45. * Its primary purpose is to simplify dynamic class-loading in RMI. Create an
  46. * instance of it, register a classloader with your classes, start it, and
  47. * you'll be able to let RMI-clients dynamically download classes from it.
  48. *
  49. * It is configured by calling any methods programmatically prior to startup.
  50. * @author <a href="mailto:marc@jboss.org">Marc Fleury</a>
  51. * @author <a href="mailto:Scott.Stark@org.jboss">Scott Stark</a>
  52. * @authro <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
  53. * @version $Revision: 64248 $
  54. * @see WebClassLoader
  55. */
  56. public class WebServer
  57. implements Runnable
  58. {
  59. // Constants -----------------------------------------------------
  60. // Attributes ----------------------------------------------------
  61. private static Logger log = Logger.getLogger(WebServer.class);
  62. /**
  63. * The port the web server listens on
  64. */
  65. private int port = 8083;
  66. /**
  67. * The interface to bind to. This is useful for multi-homed hosts that want
  68. * control over which interfaces accept connections.
  69. */
  70. private InetAddress bindAddress;
  71. /**
  72. * The serverSocket listen queue depth
  73. */
  74. private int backlog = 50;
  75. /**
  76. * The map of class loaders registered with the web server
  77. */
  78. private final ConcurrentReaderHashMap loaderMap = new ConcurrentReaderHashMap();
  79. /**
  80. * The web server http listening socket
  81. */
  82. private ServerSocket server = null;
  83. /**
  84. * A flag indicating if the server should attempt to download classes from
  85. * thread context class loader when a request arrives that does not have a
  86. * class loader key prefix.
  87. */
  88. private boolean downloadServerClasses = true;
  89. /**
  90. * A flag indicating if the server should attempt to download resources,
  91. * i.e. resource paths that don't end in .class
  92. */
  93. private boolean downloadResources = false;
  94. /**
  95. * The class wide mapping of type suffixes(class, txt) to their mime type
  96. * string used as the Content-Type header for the vended classes/resources
  97. */
  98. private static final Properties mimeTypes = new Properties();
  99. /**
  100. * The thread pool used to manage listening threads
  101. */
  102. private BasicThreadPoolMBean threadPool;
  103. // Public --------------------------------------------------------
  104. /**
  105. * Set the http listening port
  106. */
  107. public void setPort(int port)
  108. {
  109. this.port = port;
  110. }
  111. /**
  112. * Get the http listening port
  113. * @return the http listening port
  114. */
  115. public int getPort()
  116. {
  117. return port;
  118. }
  119. /**
  120. * Set the http server bind address
  121. * @param bindAddress
  122. */
  123. public void setBindAddress(InetAddress bindAddress)
  124. {
  125. this.bindAddress = bindAddress;
  126. }
  127. /**
  128. * Get the address the http server binds to
  129. * @return the http bind address
  130. */
  131. public InetAddress getBindAddress()
  132. {
  133. return bindAddress;
  134. }
  135. /**
  136. * Get the server sockets listen queue depth
  137. * @return the listen queue depth
  138. */
  139. public int getBacklog()
  140. {
  141. return backlog;
  142. }
  143. /**
  144. * Set the server sockets listen queue depth
  145. */
  146. public void setBacklog(int backlog)
  147. {
  148. if (backlog <= 0)
  149. backlog = 50;
  150. this.backlog = backlog;
  151. }
  152. public boolean getDownloadServerClasses()
  153. {
  154. return downloadServerClasses;
  155. }
  156. public void setDownloadServerClasses(boolean flag)
  157. {
  158. downloadServerClasses = flag;
  159. }
  160. public boolean getDownloadResources()
  161. {
  162. return downloadResources;
  163. }
  164. public void setDownloadResources(boolean flag)
  165. {
  166. downloadResources = flag;
  167. }
  168. public BasicThreadPoolMBean getThreadPool()
  169. {
  170. return threadPool;
  171. }
  172. public void setThreadPool(BasicThreadPoolMBean threadPool)
  173. {
  174. this.threadPool = threadPool;
  175. }
  176. /**
  177. * Augment the type suffix to mime type mappings
  178. * @param extension - the type extension without a period(class, txt)
  179. * @param type - the mime type string
  180. */
  181. public void addMimeType(String extension, String type)
  182. {
  183. mimeTypes.put(extension, type);
  184. }
  185. /**
  186. * Start the web server on port and begin listening for requests.
  187. */
  188. public void start() throws Exception
  189. {
  190. if (threadPool == null)
  191. threadPool = new BasicThreadPool("ClassLoadingPool");
  192. try
  193. {
  194. server = new ServerSocket(port, backlog, bindAddress);
  195. log.debug("Started server: " + server);
  196. listen();
  197. }
  198. catch(java.net.BindException be)
  199. {
  200. throw new Exception("Port "+port+" already in use.",be);
  201. }
  202. catch (IOException e)
  203. {
  204. throw e;
  205. }
  206. }
  207. /**
  208. * Close the web server listening socket
  209. */
  210. public void stop()
  211. {
  212. try
  213. {
  214. ServerSocket srv = server;
  215. server = null;
  216. srv.close();
  217. }
  218. catch (Exception ignore) {}
  219. }
  220. /**
  221. * Add a class loader to the web server map and return the URL that should be
  222. * used as the annotated codebase for classes that are to be available via
  223. * RMI dynamic classloading. The codebase URL is formed by taking the
  224. * java.rmi.server.codebase system property and adding a subpath unique for
  225. * the class loader instance.
  226. * @param cl - the ClassLoader instance to begin serving download requests
  227. * for
  228. * @return the annotated codebase to use if java.rmi.server.codebase is set,
  229. * null otherwise.
  230. * @see #getClassLoaderKey(ClassLoader)
  231. */
  232. public URL addClassLoader(ClassLoader cl)
  233. {
  234. String key = (cl instanceof WebClassLoader) ?
  235. ((WebClassLoader) cl).getKey() :
  236. getClassLoaderKey(cl);
  237. loaderMap.put(key, cl);
  238. URL loaderURL = null;
  239. String codebase = System.getProperty("java.rmi.server.codebase");
  240. if (codebase != null)
  241. {
  242. if (codebase.endsWith("/") == false)
  243. codebase += '/';
  244. codebase += key;
  245. codebase += '/';
  246. try
  247. {
  248. loaderURL = new URL(codebase);
  249. }
  250. catch (MalformedURLException e)
  251. {
  252. log.error("invalid url", e);
  253. }
  254. }
  255. log.trace("Added ClassLoader: " + cl + " URL: " + loaderURL);
  256. return loaderURL;
  257. }
  258. /**
  259. * Remove a class loader previously added via addClassLoader
  260. * @param cl - the ClassLoader previously added via addClassLoader
  261. */
  262. public void removeClassLoader(ClassLoader cl)
  263. {
  264. String key = getClassLoaderKey(cl);
  265. loaderMap.remove(key);
  266. }
  267. // Runnable implementation ---------------------------------------
  268. /**
  269. * Listen threads entry point. Here we accept a client connection and located
  270. * requested classes/resources using the class loader specified in the http
  271. * request.
  272. */
  273. public void run()
  274. {
  275. // Return if the server has been stopped
  276. if (server == null)
  277. return;
  278. // Accept a connection
  279. Socket socket = null;
  280. try
  281. {
  282. socket = server.accept();
  283. }
  284. catch (IOException e)
  285. {
  286. // If the server is not null meaning we were not stopped report the err
  287. if (server != null)
  288. log.error("Failed to accept connection", e);
  289. return;
  290. }
  291. // Create a new thread to accept the next connection
  292. listen();
  293. try
  294. {
  295. // Get the request socket output stream
  296. DataOutputStream out = new DataOutputStream(socket.getOutputStream());
  297. try
  298. {
  299. String httpCode = "200 OK";
  300. // Get the requested item from the HTTP header
  301. BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  302. String rawPath = getPath(in);
  303. // Parse the path into the class loader key and file path.
  304. //
  305. // The class loader key is a string whose format is
  306. // "ClassName[oid]", where the oid substring may contain '/'
  307. // chars. The expected form of the raw path is:
  308. //
  309. // "SomeClassName[some/object/id]/some/file/path"
  310. //
  311. // The class loader key is "SomeClassName[some/object/id]"
  312. // and the file path is "some/file/path"
  313. int endOfKey = rawPath.indexOf(']');
  314. String filePath = rawPath.substring(endOfKey + 2);
  315. String loaderKey = rawPath.substring(0, endOfKey + 1);
  316. log.trace("loaderKey = " + loaderKey);
  317. log.trace("filePath = " + filePath);
  318. ClassLoader loader = (ClassLoader) loaderMap.get(loaderKey);
  319. /* If we did not find a class loader check to see if the raw path
  320. begins with className + '[' + class loader key + ']' by looking for
  321. an '[' char. If it does not and downloadServerClasses is true use
  322. the thread context class loader and set filePath to the rawPath
  323. */
  324. if (loader == null && rawPath.indexOf('[') < 0 && downloadServerClasses)
  325. {
  326. filePath = rawPath;
  327. log.trace("No loader, reset filePath = " + filePath);
  328. loader = Thread.currentThread().getContextClassLoader();
  329. }
  330. log.trace("loader = " + loader);
  331. byte[] bytes = {};
  332. if (loader != null && filePath.endsWith(".class"))
  333. {
  334. // A request for a class file
  335. String className = filePath.substring(0, filePath.length() - 6).replace('/', '.');
  336. log.trace("loading className = " + className);
  337. Class clazz = loader.loadClass(className);
  338. URL clazzUrl = clazz.getProtectionDomain().getCodeSource().getLocation();
  339. log.trace("clazzUrl = " + clazzUrl);
  340. if (clazzUrl == null)
  341. {
  342. // Does the WebClassLoader of clazz
  343. // have the ability to obtain the bytecodes of clazz?
  344. bytes = ((WebClassLoader) clazz.getClassLoader()).getBytes(clazz);
  345. if (bytes == null)
  346. throw new Exception("Class not found: " + className);
  347. }
  348. else
  349. {
  350. if (clazzUrl.getFile().endsWith("/") == false)
  351. {
  352. clazzUrl = new URL("jar:" + clazzUrl + "!/" + filePath);
  353. }
  354. // this is a hack for the AOP ClassProxyFactory
  355. else if (clazzUrl.getFile().indexOf("/org_jboss_aop_proxy$") < 0)
  356. {
  357. clazzUrl = new URL(clazzUrl, filePath);
  358. }
  359. // Retrieve bytecodes
  360. log.trace("new clazzUrl: " + clazzUrl);
  361. bytes = getBytes(clazzUrl);
  362. }
  363. }
  364. else if (loader != null && filePath.length() > 0 && downloadServerClasses && downloadResources)
  365. {
  366. // Try getting resource
  367. log.trace("loading resource = " + filePath);
  368. URL resourceURL = loader.getResource(filePath);
  369. if (resourceURL == null)
  370. httpCode = "404 Resource not found:" + filePath;
  371. else
  372. {
  373. // Retrieve bytes
  374. log.trace("resourceURL = " + resourceURL);
  375. bytes = getBytes(resourceURL);
  376. }
  377. }
  378. else
  379. {
  380. httpCode = "404 Not Found";
  381. }
  382. // Send bytecodes/resource data in response (assumes HTTP/1.0 or later)
  383. try
  384. {
  385. log.trace("HTTP code=" + httpCode + ", Content-Length: " + bytes.length);
  386. // The HTTP 1.0 header
  387. out.writeBytes("HTTP/1.0 " + httpCode + "\r\n");
  388. out.writeBytes("Content-Length: " + bytes.length + "\r\n");
  389. out.writeBytes("Content-Type: " + getMimeType(filePath));
  390. out.writeBytes("\r\n\r\n");
  391. // The response body
  392. out.write(bytes);
  393. out.flush();
  394. }
  395. catch (IOException ie)
  396. {
  397. return;
  398. }
  399. }
  400. catch (Throwable e)
  401. {
  402. try
  403. {
  404. log.trace("HTTP code=404, " + e.getMessage());
  405. // Write out error response
  406. out.writeBytes("HTTP/1.0 404 Not Found\r\n");
  407. out.writeBytes("Content-Type: text/html\r\n\r\n");
  408. out.flush();
  409. }
  410. catch (IOException ex)
  411. {
  412. // Ignore
  413. }
  414. }
  415. }
  416. catch (IOException ex)
  417. {
  418. log.error("error writting response", ex);
  419. }
  420. finally
  421. {
  422. // Close the client request socket
  423. try
  424. {
  425. socket.close();
  426. }
  427. catch (IOException e)
  428. {
  429. }
  430. }
  431. }
  432. // Protected -----------------------------------------------------
  433. /**
  434. * Create the string key used as the key into the loaderMap.
  435. * @return The class loader instance key.
  436. */
  437. protected String getClassLoaderKey(ClassLoader cl)
  438. {
  439. String className = cl.getClass().getName();
  440. int dot = className.lastIndexOf('.');
  441. if (dot >= 0)
  442. className = className.substring(dot + 1);
  443. String key = className + '[' + cl.hashCode() + ']';
  444. return key;
  445. }
  446. protected void listen()
  447. {
  448. threadPool.getInstance().run(this);
  449. }
  450. /**
  451. * @return the path portion of the HTTP request header.
  452. */
  453. protected String getPath(BufferedReader in) throws IOException
  454. {
  455. String line = in.readLine();
  456. log.trace("raw request=" + line);
  457. // Find the request path by parsing the 'REQUEST_TYPE filePath HTTP_VERSION' string
  458. int start = line.indexOf(' ') + 1;
  459. int end = line.indexOf(' ', start + 1);
  460. // The file minus the leading '/'
  461. String filePath = line.substring(start + 1, end);
  462. return filePath;
  463. }
  464. /**
  465. * Read the local class/resource contents into a byte array.
  466. */
  467. protected byte[] getBytes(URL url) throws IOException
  468. {
  469. InputStream in = new BufferedInputStream(url.openStream());
  470. log.debug("Retrieving " + url);
  471. ByteArrayOutputStream out = new ByteArrayOutputStream();
  472. byte[] tmp = new byte[1024];
  473. int bytes;
  474. while ((bytes = in.read(tmp)) != -1)
  475. {
  476. out.write(tmp, 0, bytes);
  477. }
  478. in.close();
  479. return out.toByteArray();
  480. }
  481. /**
  482. * Lookup the mime type for the suffix of the path argument.
  483. * @return the mime-type string for path.
  484. */
  485. protected String getMimeType(String path)
  486. {
  487. int dot = path.lastIndexOf(".");
  488. String type = "text/html";
  489. if (dot >= 0)
  490. {
  491. // The suffix is the type extension without the '.'
  492. String suffix = path.substring(dot + 1);
  493. String mimeType = mimeTypes.getProperty(suffix);
  494. if (mimeType != null)
  495. type = mimeType;
  496. }
  497. return type;
  498. }
  499. }