一个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();
    }
  }
}
View Code
  • 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) {
  }

}
View Code

总结:Bootstarp类作为主程序,只要抓住启动入口就可以了,传入container给connector,再初始化、运行connector,记住传递依赖在先,顺序很重要;simpleContainer类当前只   有invoke方法,执行了加载动作,其他方法暂时为null。现在,一个简单的Container就算完成了,本案例作为容器和连接器关系的一个记录,哪天记不清了就回顾一下。

   这里少了一个servlet.jar包,因为导入的是弃用版本Eclipse给屏蔽了!

原文地址:https://www.cnblogs.com/10000miles/p/9233736.html