一个servlet web server,由移植自Tomcat的完整的connector模块和简化的Container(取代servlet处理器)组成
背景:连接器模块由此已经完整,Tomcat中连接器已经有完善的线程、异常处理和Http处理(res/req解析),只是之前的servlet处理器还是很简单,现在作为学习Container的一个预热,写一个简单的container取代servlet,由主程序初始化并启动Tomcat连接器,创建container传递给连接器。这次演进,程序的大体运行方向较之前其实是不变的:等待连接、解析socket、创建res/req,"组装"(动态加载)程序员编写的servlet并处理,清理res/req,关闭socket、停止线程。不同的是,servlet处理器不仅要演进得更完善(成为container)而且还要和连接器解耦。通过连接器源码《setContainer(Container container)》可以发现,contaner被其关联,container的创建初始化工作是由专门 程序来完成,虽然它需要依赖container的getContainer().invoke(this.request, this.response)来执行“组装”servlet的子程序。 (getContainer().invoke(this.request,this.response)是模块的连接处也是程序输入输出的入口,对研究源码很关键;connector.initialize()和connector.start()是连接器执行的入口。牢牢记住,感谢作者,百万行的代码中截取某些程序入口真的是很难,也可能是我功力不够,没找到方法)。
- 代码目录
附加说明:这里直接引入源码供主程序调用,classpath编译运行这些依赖,即我所谓的移植Tomcat源码。
- Bootstrap
package startup; import core.SimpleContainer; import org.apache.catalina.connector.http.HttpConnector; public final class Bootstrap { public static void main(String[] args) { HttpConnector connector = new HttpConnector();//创建连接对象 SimpleContainer container = new SimpleContainer();//创建容器 connector.setContainer(container);//放进连接对象 try { connector.initialize();//初始化 并启动,这里可是使用生命周期,然后执行即可,应该是?! connector.start(); // make the application wait until we press any key. System.in.read(); } catch (Exception e) { e.printStackTrace(); } } }
- simpleContainer
package core; import java.beans.PropertyChangeListener; import java.net.URL; import java.net.URLClassLoader; import java.net.URLStreamHandler; import java.io.File; import java.io.IOException; import javax.naming.directory.DirContext; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.Cluster; import org.apache.catalina.Container; import org.apache.catalina.ContainerListener; import org.apache.catalina.Loader; import org.apache.catalina.Logger; import org.apache.catalina.Manager; import org.apache.catalina.Mapper; import org.apache.catalina.Realm; import org.apache.catalina.Request; import org.apache.catalina.Response; public class SimpleContainer implements Container { public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";//资源地址 public SimpleContainer() { } public String getInfo() { return null; } public Loader getLoader() { return null; } public void setLoader(Loader loader) { } public Logger getLogger() { return null; } public void setLogger(Logger logger) { } public Manager getManager() { return null; } public void setManager(Manager manager) { } public Cluster getCluster() { return null; } public void setCluster(Cluster cluster) { } public String getName() { return null; } public void setName(String name) { } public Container getParent() { return null; } public void setParent(Container container) { } public ClassLoader getParentClassLoader() { return null; } public void setParentClassLoader(ClassLoader parent) { } public Realm getRealm() { return null; } public void setRealm(Realm realm) { } public DirContext getResources() { return null; } public void setResources(DirContext resources) { } public void addChild(Container child) { } public void addContainerListener(ContainerListener listener) { } public void addMapper(Mapper mapper) { } public void addPropertyChangeListener(PropertyChangeListener listener) { } public Container findChild(String name) { return null; } public Container[] findChildren() { return null; } public ContainerListener[] findContainerListeners() { return null; } public Mapper findMapper(String protocol) { return null; } public Mapper[] findMappers() { return null; } public void invoke(Request request, Response response) throws IOException, ServletException { String servletName = ( (HttpServletRequest) request).getRequestURI();//获得要请求的servlet servletName = servletName.substring(servletName.lastIndexOf("/") + 1); URLClassLoader loader = null;//类加载器被直接创建,加载模块与其他模块的关系类似工具模块 try { URL[] urls = new URL[1]; URLStreamHandler streamHandler = null; File classPath = new File(WEB_ROOT);//将资源文件创建为file String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;//路径传递给url对象 urls[0] = new URL(null, repository, streamHandler); loader = new URLClassLoader(urls);//初始化servlet加载器 } catch (IOException e) { System.out.println(e.toString() ); } Class myClass = null; try { myClass = loader.loadClass(servletName);//加载用户请求的servlet } catch (ClassNotFoundException e) { System.out.println(e.toString()); } Servlet servlet = null; try { servlet = (Servlet) myClass.newInstance();//初始化,并传递res/req,组件得以装载 servlet.service((HttpServletRequest) request, (HttpServletResponse) response); } catch (Exception e) { System.out.println(e.toString()); } catch (Throwable e) { System.out.println(e.toString()); } } public Container map(Request request, boolean update) { return null; } public void removeChild(Container child) { } public void removeContainerListener(ContainerListener listener) { } public void removeMapper(Mapper mapper) { } public void removePropertyChangeListener(PropertyChangeListener listener) { } }