User: starksm
Date: 01/06/09 12:33:39
Modified: src/main/org/jboss/web WebServer.java WebService.java
WebServiceMBean.java
Added: src/main/org/jboss/web WebClassLoader.java
Log:
Update dynamic class loading to fix bug #424287
Revision Changes Path
1.5 +327 -269 jboss/src/main/org/jboss/web/WebServer.java
Index: WebServer.java
===================================================================
RCS file: /cvsroot/jboss/jboss/src/main/org/jboss/web/WebServer.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- WebServer.java 2001/02/23 17:43:16 1.4
+++ WebServer.java 2001/06/09 19:33:39 1.5
@@ -6,10 +6,20 @@
*/
package org.jboss.web;
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import org.jboss.logging.Logger;
+import java.io.BufferedReader;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.Socket;
+import java.net.ServerSocket;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Properties;
/**
* A mini webserver that should be embedded in another application. It can server
any file that is available from
@@ -18,290 +28,338 @@
* Its primary purpose is to simplify dynamic class-loading in RMI. Create an
instance of it, register a classloader
* with your classes, start it, and you'll be able to let RMI-clients dynamically
download classes from it.
*
- * It is configured by editing either the dynaserver.default file in
dynaserver.jar (not recommended),
- * or by adding a file dynaserver.properties in the same location as the
dynaserver.jar file (recommended).
- * It can also be configured by calling any methods programmatically prior to
startup.
+ * It is configured by calling any methods programmatically prior to startup.
*
- * @author $Author: osh $
- * @version $Revision: 1.4 $
+ * @see WebClassLoader
+ *
+ * @author $Author: starksm $
+ * @author [EMAIL PROTECTED]
+ * @version $Revision: 1.5 $
*/
public class WebServer
implements Runnable
{
- // Constants -----------------------------------------------------
-
- // Attributes ----------------------------------------------------
- private int port = 8080;
- private ArrayList classLoaders = new ArrayList();
-
- private ServerSocket server = null;
-
- private boolean debug = false; // The server shows some debugging info if this
is true
-
- private static Properties mimeTypes = new Properties(); // The MIME type mapping
-
- private ThreadPool threadPool = new ThreadPool();
-
-
- // Static --------------------------------------------------------
-
- // Constructors --------------------------------------------------
+ // Constants -----------------------------------------------------
+
+ // Attributes ----------------------------------------------------
+ /** The port the web server listens on */
+ private int port = 8080;
+ /** The map of class loaders registered with the web server */
+ private HashMap loaderMap = new HashMap();
+ /** The web server http listening socket */
+ private ServerSocket server = null;
+ /** The class wide mapping of type suffixes(.class, .txt) to their mime
+ type string used as the Content-Type header for the vended classes/resources */
+ private static Properties mimeTypes = new Properties();
+ /** The thread pool used to manage listening threads */
+ private ThreadPool threadPool = new ThreadPool();
+
// Public --------------------------------------------------------
- public void setPort(int p)
- {
- port = p;
- }
-
- public int getPort()
- {
- return port;
- }
-
- public void setDebug(boolean d)
- {
- debug = d;
- }
-
- public boolean isDebug()
- {
- return debug;
- }
-
- public void addMimeType(String extension, String type)
- {
- mimeTypes.put(extension,type);
- }
-
- public void start()
- throws IOException
- {
- try
- {
- server = null;
- server = new ServerSocket(getPort());
- debug("Started on port " + getPort());
- listen();
-
- } catch (IOException e)
- {
- debug("Could not start on port " + getPort());
- throw e;
- }
- }
-
- public void stop()
- {
- try
- {
- ServerSocket srv = server;
- server = null;
- srv.close();
- } catch (Exception e) {}
- }
-
- public void addClassLoader(ClassLoader cl)
- {
- classLoaders.add(cl);
- }
-
- public void removeClassLoader(ClassLoader cl)
- {
- classLoaders.remove(cl);
- }
-
- // Runnable implementation ---------------------------------------
- public void run()
- {
- Socket socket = null;
-
- // Accept a connection
- try
- {
- socket = server.accept();
- } catch (IOException e)
- {
- if (server == null) return; // Stopped by normal means
-
- debug("DynaServer stopped: " + e.getMessage());
- Logger.exception(e);
- debug("Restarting DynaServer");
- try
- {
- start();
+ /** Set the http listening port
+ */
+ public void setPort(int p)
+ {
+ port = p;
+ }
+ /** Get the http listening port
+ @return the http listening port
+ */
+ public int getPort()
+ {
+ return port;
+ }
+
+ /** Augment the type suffix to mime type mappings
+ @param extension, the type extension(.class, .txt)
+ @param type, the mime type string
+ */
+ public void addMimeType(String extension, String type)
+ {
+ mimeTypes.put(extension,type);
+ }
+
+ /** Start the web server on port and begin listening for requests.
+ */
+ public void start() throws IOException
+ {
+ try
+ {
+ server = null;
+ server = new ServerSocket(getPort());
+ debug("Started on port " + getPort());
+ listen();
+ }
+ catch (IOException e)
+ {
+ debug("Could not start on port " + getPort());
+ throw e;
+ }
+ }
+
+ /** Close the web server listening socket
+ */
+ public void stop()
+ {
+ try
+ {
+ ServerSocket srv = server;
+ server = null;
+ srv.close();
+ }
+ catch (Exception e)
+ {
+ }
+ }
+
+ /** Add a class loader to the web server map and return the URL that
+ should be used as the annotated codebase for classes that are to be
+ available via RMI dynamic classloading. The codebase URL is formed by
+ taking the java.rmi.server.codebase system property and adding a subpath
+ unique for the class loader instance.
+
+ @see #getClassLoaderKey(ClassLoader)
+ @param cl, the ClassLoader instance to begin serving download requests for
+ @return the annotated codebase to use if java.rmi.server.codebase is set,
+ null otherwise.
+ */
+ public URL addClassLoader(ClassLoader cl)
+ {
+ String key = getClassLoaderKey(cl);
+ loaderMap.put(key, cl);
+ URL loaderURL = null;
+ String codebase = System.getProperty("java.rmi.server.codebase");
+ if( codebase != null )
+ {
+ if( codebase.endsWith("/") == false )
+ codebase += '/';
+ codebase += key;
+ try
+ {
+ loaderURL = new URL(codebase);
+ }
+ catch(MalformedURLException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ trace("Added ClassLoader: "+cl+" URL: "+loaderURL);
+ return loaderURL;
+ }
+
+ /** Remove a class loader previously added via addClassLoader
+ @param cl, the ClassLoader previously added via addClassLoader
+ */
+ public void removeClassLoader(ClassLoader cl)
+ {
+ String key = getClassLoaderKey(cl);
+ loaderMap.remove(key);
+ }
+
+ // Runnable implementation ---------------------------------------
+ /** Listen threads entry point. Here we accept a client connection
+ and located requested classes/resources using the class loader
+ specified in the http request.
+ */
+ public void run()
+ {
+ // Return if the server has been stopped
+ if (server == null)
return;
- } catch (IOException ex)
- {
- debug("Restart failed");
+
+ // Accept a connection
+ Socket socket = null;
+ try
+ {
+ socket = server.accept();
+ }
+ catch (IOException e)
+ {
+ // This restart logic seems questionable...
+ debug("DynaServer stopped: " + e.getMessage());
+ e.printStackTrace();
+ debug("Restarting DynaServer");
+ try
+ {
+ start();
+ } catch (IOException ex)
+ {
+ debug("Restart failed");
+ }
return;
- }
- }
-
- // Create a new thread to accept the next connection
- listen();
-
- try
- {
- DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+ }
+
+ // Create a new thread to accept the next connection
+ listen();
+
try
{
- // Get path to class file from header
- BufferedReader in = new BufferedReader(new
InputStreamReader(socket.getInputStream()));
-
- String path = getPath(in);
- byte[] bytes;
- if (path.endsWith(".class")) // Class
- {
- String className = path.substring(0,
path.length()-6).replace('/','.');
-
- // Try getting class
- URL clazzUrl = null;
- for (int i = 0; i < classLoaders.size(); i++)
- {
- try
- {
- Class clazz =
((ClassLoader)classLoaders.get(i)).loadClass(className);
- clazzUrl =
clazz.getProtectionDomain().getCodeSource().getLocation();
+ // Get the request socket output stream
+ DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+ try
+ {
+ // Get the requested item from the HTTP header
+ BufferedReader in = new BufferedReader(new
InputStreamReader(socket.getInputStream()));
+ String rawPath = getPath(in);
+ // Parse the path into the class loader key and file path
+ int separator = rawPath.indexOf('/');
+ String filePath = rawPath.substring(separator+1);
+ String loaderKey = rawPath.substring(0, separator+1);
+ trace("WebServer: loaderKey = "+loaderKey);
+ trace("WebServer: filePath = "+filePath);
+ ClassLoader loader = (ClassLoader) loaderMap.get(loaderKey);
+ trace("WebServer: loader = "+loader);
+ byte[] bytes;
+ if( filePath.endsWith(".class") )
+ {
+ // A request for a class file
+ String className = filePath.substring(0,
filePath.length()-6).replace('/','.');
+ trace("WebServer: loading className = "+className);
+ Class clazz = loader.loadClass(className);
+ URL clazzUrl =
clazz.getProtectionDomain().getCodeSource().getLocation();
+ trace("WebServer: clazzUrl = "+clazzUrl);
if (clazzUrl.getFile().endsWith(".jar"))
- clazzUrl = new URL("jar:"+clazzUrl+"!/"+path);
+ clazzUrl = new URL("jar:"+clazzUrl+"!/"+filePath);
else
- clazzUrl = new URL(clazzUrl, path);
-
- break;
- } catch (Exception e)
- {
- if (!(e instanceof ClassNotFoundException))
- Logger.exception(e);
- }
- }
-
- if (clazzUrl == null)
- throw new Exception("Class not found:"+className);
-
- // Retrieve bytecodes
- bytes = getBytes(clazzUrl);
- } else // Resource
- {
- // Try getting resource
- URL resourceUrl = null;
- for (int i = 0; i < classLoaders.size(); i++)
- {
- try
- {
- resourceUrl =
((ClassLoader)classLoaders.get(i)).getResource(path);
-
- if (resourceUrl != null)
- break;
- } catch (Exception e)
- {
- Logger.exception(e);
- }
- }
+ clazzUrl = new URL(clazzUrl, filePath);
+ if (clazzUrl == null)
+ throw new Exception("Class not found:"+className);
- if (resourceUrl == null)
- throw new Exception("File not found:"+path);
-
- // Retrieve bytes
- bytes = getBytes(resourceUrl);
- }
-
- // Send bytecodes in response (assumes HTTP/1.0 or later)
- try
- {
- out.writeBytes("HTTP/1.0 200 OK\r\n");
- out.writeBytes("Content-Length: " + bytes.length +
- "\r\n");
- out.writeBytes("Content-Type: "+getMimeType(path)+"\r\n\r\n");
- out.write(bytes);
- out.flush();
- } catch (IOException ie)
+ // Retrieve bytecodes
+ bytes = getBytes(clazzUrl);
+ }
+ else // Resource
+ {
+ // Try getting resource
+ trace("WebServer: loading resource = "+filePath);
+ URL resourceUrl = loader.getResource(filePath);
+ if (resourceUrl == null)
+ throw new FileNotFoundException("Resource not
found:"+filePath);
+
+ // Retrieve bytes
+ bytes = getBytes(resourceUrl);
+ }
+
+ // Send bytecodes/resource data in response (assumes HTTP/1.0 or
later)
+ try
+ {
+ // The HTTP 1.0 header
+ out.writeBytes("HTTP/1.0 200 OK\r\n");
+ out.writeBytes("Content-Length: " + bytes.length + "\r\n");
+ out.writeBytes("Content-Type: "+getMimeType(filePath));
+ out.writeBytes("\r\n\r\n");
+ // The response body
+ out.write(bytes);
+ out.flush();
+ }
+ catch (IOException ie)
+ {
+ return;
+ }
+ }
+ catch(Throwable e)
{
- return;
+ try
+ {
+ // Write out error response
+ out.writeBytes("HTTP/1.0 400 " + e.getMessage() + "\r\n");
+ out.writeBytes("Content-Type: text/html\r\n\r\n");
+ out.flush();
+ } catch (IOException ex)
+ {
+ // Ignore
+ }
}
- } catch (Exception e)
- {
- try
+ }
+ catch (IOException ex)
+ {
+ // eat exception (could log error to log file, but
+ // write out to stdout for now).
+ debug("error writing response: " + ex.getMessage());
+ ex.printStackTrace();
+ }
+ finally
+ {
+ // Close the client request socket
+ try
{
- // Write out error response
- out.writeBytes("HTTP/1.0 400 " + e.getMessage() + "\r\n");
- out.writeBytes("Content-Type: text/html\r\n\r\n");
- out.flush();
- } catch (IOException ex)
+ socket.close();
+ } catch (IOException e)
{
- // Ignore
}
- }
- } catch (IOException ex)
- {
- // eat exception (could log error to log file, but
- // write out to stdout for now).
- debug("error writing response: " + ex.getMessage());
- ex.printStackTrace();
-
- } finally
- {
- try
- {
- socket.close();
- } catch (IOException e)
- {
- }
- }
- }
-
- // Y overrides ---------------------------------------------------
-
- // Package protected ---------------------------------------------
-
- // Protected -----------------------------------------------------
- protected void debug(String msg)
- {
- if (isDebug())
- Logger.log(msg);
- }
-
- protected void listen()
- {
- threadPool.run(this);
- }
-
- /**
- * Returns the path to the class file obtained from
- * parsing the HTML header.
+ }
+ }
+
+ // Protected -----------------------------------------------------
+ /** Create the string key used as the key into the loaderMap.
+ @return The class loader instance key.
+ */
+ protected String getClassLoaderKey(ClassLoader cl)
+ {
+ String className = cl.getClass().getName();
+ int dot = className.lastIndexOf('.');
+ if( dot >= 0 )
+ className = className.substring(dot+1);
+ String key = className + '@' + cl.hashCode() + '/';
+ return key;
+ }
+
+ protected void trace(String msg)
+ {
+ System.out.println(msg);
+ }
+ protected void debug(String msg)
+ {
+ System.out.println(msg);
+ }
+
+ protected void listen()
+ {
+ threadPool.run(this);
+ }
+
+ /**
+ @return the path portion of the HTTP request header.
*/
- protected String getPath(BufferedReader in)
- throws IOException
- {
- String line = in.readLine();
-
- int idx = line.indexOf(" ")+1;
- return line.substring(idx+1,line.indexOf(" ",idx)); // The file minus the
leading /
- }
-
- protected byte[] getBytes(URL url)
- throws Exception
- {
- InputStream in = new BufferedInputStream(url.openStream());
- debug("Retrieving "+url.toString());
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- int data;
- while ((data = in.read()) != -1)
- {
- out.write(data);
- }
- return out.toByteArray();
- }
-
- protected String getMimeType(String path)
- {
- String type = mimeTypes.getProperty(path.substring(path.lastIndexOf(".")));
- if (type == null)
- return "text/html";
- else
- return type;
- }
-
- // Private -------------------------------------------------------
+ protected String getPath(BufferedReader in) throws IOException
+ {
+ String line = in.readLine();
+ debug("WebServer: raw path="+line);
+ // Find the request path by parsing the 'REQUEST_TYPE filePath
HTTP_VERSION' string
+ int start = line.indexOf(' ')+1;
+ int end = line.indexOf(' ', start+1);
+ // The file minus the leading '/'
+ String filePath = line.substring(start+1, end);
+ return filePath;
+ }
+
+ /** Read the local class/resource contents into a byte array.
+ */
+ protected byte[] getBytes(URL url) throws IOException
+ {
+ InputStream in = new BufferedInputStream(url.openStream());
+ debug("Retrieving "+url.toString());
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] tmp = new byte[1024];
+ int bytes;
+ while ((bytes = in.read(tmp)) != -1)
+ {
+ out.write(tmp, 0, bytes);
+ }
+ return out.toByteArray();
+ }
+
+ /** Lookup the mime type for the suffix of the path argument.
+ @return the mime-type string for path.
+ */
+ protected String getMimeType(String path)
+ {
+ int dot = path.lastIndexOf(".");
+ String suffix = path.substring(dot);
+ String type = mimeTypes.getProperty(suffix);
+ if (type == null)
+ type = "text/html";
+ return type;
+ }
- // Inner classes -------------------------------------------------
}
1.4 +23 -13 jboss/src/main/org/jboss/web/WebService.java
Index: WebService.java
===================================================================
RCS file: /cvsroot/jboss/jboss/src/main/org/jboss/web/WebService.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- WebService.java 2000/12/07 18:16:15 1.3
+++ WebService.java 2001/06/09 19:33:39 1.4
@@ -19,12 +19,12 @@
import org.jboss.logging.Log;
import org.jboss.util.ServiceMBeanSupport;
-/**
- * <description>
- *
- * @see <related>
+/** The WebService implementation. It configures a WebServer instance to
+ perform dynamic class and resource loading.
+
* @author Rickard �berg ([EMAIL PROTECTED])
- * @version $Revision: 1.3 $
+ * @author [EMAIL PROTECTED]
+ * @version $Revision: 1.4 $
*/
public class WebService
extends ServiceMBeanSupport
@@ -74,19 +74,29 @@
{
return "Webserver";
}
-
+
+ /** Start the web server for dynamic downloading of classes and resources.
+ This sets the system java.rmi.server.hostname property to the local hostname
+ if it has not been set. The system java.rmi.server.codebase is also set to
+ "http://"+java.rmi.server.hostname+":"+getPort()+"/" if the
+ java.rmi.server.codebase has not been set.
+ */
public void startService()
throws Exception
{
server.start();
- // Set codebase
+ // Set the rmi host and codebase if they are not already set
String host = System.getProperty("java.rmi.server.hostname");
- if (host ==null) host = InetAddress.getLocalHost().getHostName();
+ if( host == null )
+ host = InetAddress.getLocalHost().getHostName();
- String codebase = "http://"+host+":"+getPort()+"/";
- System.setProperty("java.rmi.server.codebase", codebase);
+ String codebase = System.getProperty("java.rmi.server.codebase");
+ if( codebase == null )
+ {
+ codebase = "http://"+host+":"+getPort()+"/";
+ System.setProperty("java.rmi.server.codebase", codebase);
+ }
log.log("Codebase set to "+codebase);
-
log.log("Started webserver on port "+server.getPort());
}
@@ -95,9 +105,9 @@
server.stop();
}
- public void addClassLoader(ClassLoader cl)
+ public URL addClassLoader(ClassLoader cl)
{
- server.addClassLoader(cl);
+ return server.addClassLoader(cl);
}
public void removeClassLoader(ClassLoader cl)
1.3 +5 -3 jboss/src/main/org/jboss/web/WebServiceMBean.java
Index: WebServiceMBean.java
===================================================================
RCS file: /cvsroot/jboss/jboss/src/main/org/jboss/web/WebServiceMBean.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- WebServiceMBean.java 2000/12/07 15:45:25 1.2
+++ WebServiceMBean.java 2001/06/09 19:33:39 1.3
@@ -4,15 +4,17 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
-
package org.jboss.web;
+import java.net.URL;
+
/**
* <description>
*
* @see <related>
* @author Rickard �berg ([EMAIL PROTECTED])
- * @version $Revision: 1.2 $
+ * @author [EMAIL PROTECTED]
+ * @version $Revision: 1.3 $
*/
public interface WebServiceMBean
extends org.jboss.util.ServiceMBean
@@ -21,7 +23,7 @@
public static final String OBJECT_NAME = ":service=Webserver";
// Public --------------------------------------------------------
- public void addClassLoader(ClassLoader cl);
+ public URL addClassLoader(ClassLoader cl);
public void removeClassLoader(ClassLoader cl);
1.1 jboss/src/main/org/jboss/web/WebClassLoader.java
Index: WebClassLoader.java
===================================================================
package org.jboss.web;
import java.net.URL;
import java.net.URLClassLoader;
/** A simple subclass of URLClassLoader that overrides the getURLs()
method to return a different set of URLs for remote loading than what is used
for local loading. This class is used in conjunction with the WebService
mbean to allow dynamic loading of resources and classes from deployed ears,
ejb jars and wars.
@see #getUrls()
@see #setWebURLs(URL[])
@author [EMAIL PROTECTED]
@author Sacha Labourey <[EMAIL PROTECTED]>
@author Vladimir Blagojevic <[EMAIL PROTECTED]>
@version $Revision: 1.1 $
*/
public class WebClassLoader extends URLClassLoader
{
/** The URLs returned by the getURLs() method override */
private URL[] webURLs;
/** Creates new WebClassLoader */
public WebClassLoader(URL[] urls)
{
super(urls);
}
public WebClassLoader(URL[] urls, ClassLoader parent)
{
super(urls, parent);
}
public WebClassLoader(URL[] urls, ClassLoader parent,
java.net.URLStreamHandlerFactory factory)
{
super(urls, parent, factory);
}
/** Get the list of URLs that should be used as the RMI annotated codebase.
This is the URLs previously set via setWebURLs. If setWebURLs has not
been invoked or was passed in a null value, the super class value of
getURLs() is used.
@return the local web URLs if not null, else the value of super.getURLs()
*/
public URL[] getURLs()
{
URL[] urls = webURLs;
if( urls == null )
urls = super.getURLs();
return urls;
}
/** Access the URLClassLoader.getURLs() value.
@return the URLs used for local class and resource loading
*/
public URL[] getLocalURLs()
{
return super.getURLs();
}
/** Set the URLs that should be returned from this classes getURLs() override.
@param webURLs, the set of URL codebases to be used for remote class loading.
*/
public void setWebURLs(URL[] webURLs)
{
this.webURLs = webURLs;
}
}
_______________________________________________
Jboss-development mailing list
[EMAIL PROTECTED]
http://lists.sourceforge.net/lists/listinfo/jboss-development