1. 程式人生 > 實用技巧 >簡單的Tomcat實現--2.1Tomcat的內建物件之host

簡單的Tomcat實現--2.1Tomcat的內建物件之host

host

  • host的意思是虛擬主機,通常都是localhost,代表本機

  • 在server.xml中新增host標籤

    • <?xml version="1.0" encoding="UTF-8"?>
      <Server>
          <Host name="localhost">
              <Context path="/b" docBase="d:/Java/JavaProject/Jerrymice/webapps/b" />
          </Host>
      </Server>
      
  • 在ServerXmlUtil中新增方法解析xml配置檔案中的host標籤屬性name

    • public static String getHostName(){
          String name = "";
          try{
              Document document = Jsoup.parse(Constant.serverXmlFile, "utf-8");
              Element host = document.select("Host").first();
             	name = host.attr("name");
           }catch(IOException e){
               LogFactory.get().error(e);
           }
          return name;
      }
      
  • 建立Host類,有兩個欄位,一個是name,一個是context,這裡的context就是將原本Bootrap中的移植過來

    • package jerrymice.catalina;
      
      import cn.hutool.log.LogFactory;
      import jerrymice.util.Constant;
      import jerrymice.util.ServerXmlUtil;
      
      import java.io.File;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      
      /**
       * @author :xiaosong
       * @description:TODO
       * @date :2020/8/4 22:16
       */
      public class Host {
          private String name;
          private Map<String, Context> contextMap;
      
          public Host(){
              this.name = ServerXmlUtil.getHostName();
              this.contextMap = new HashMap<>();
              // 掃描資料夾內的所有應用
              scanContextOnWebAppsFolder();
              // 通過配置檔案server.xml掃描指定的應用
              scanContextsByServerXml();
          }
          public String getName(){
              return name;
          }
      
          public void setName(String name){
              this.name = name;
          }
      
          /**
           * 掃描webapp的根目錄,將所有的資料夾(應用)做成Context物件儲存在Map中
           */
          private  void scanContextOnWebAppsFolder(){
              LogFactory.get().info("Scanning webapps in webapps...");
              File[] files = Constant.webappsFolder.listFiles();
              if (files == null){
                  // 如果應用目錄下根本沒有應用,那就直接再見報告錯誤日誌
                  LogFactory.get().error(new NoSuchFieldError());
                  return;
              }
              for (File file : files){
                  if (!file.isDirectory()) {
                      continue;
                  }
                  loadContext(file);
              }
          }
          private void loadContext(File folder) {
              // 對資料夾中的檔案進行解析, 獲取資料夾名
              String path = folder.getName();
              if ("ROOT".equals(path)) {
                  // 如果是根目錄的話
                  path = "/";
              }
              else {
                  path = "/" + path;
              }
              String docBase = folder.getAbsolutePath();
              // 建立Context物件用於儲存path和docBase
              Context context = new Context(path, docBase);
              // 將建立好的context放在Map中留待使用
              contextMap.put(context.getPath(), context);
          }
      
          /**
           * 從server.xml檔案中獲取配置資訊
           */
          private  void scanContextsByServerXml(){
              LogFactory.get().info("Scanning webapps from server.xml...");
              List<Context> contexts = ServerXmlUtil.getContext();
              for (Context context: contexts){
                  contextMap.put(context.getPath(), context);
              }
          }
      
          /**
           * 通過path獲取其對飲的context
           */
          public Context getContext(String path){
              return contextMap.get(path);
          }
      }
      
  • Bootstrap的改動,將儲存Context的雜湊表移到Host類中之後,其對應的方法也一併移動,當服務啟動的時候,根據配置檔案建立一個Host物件。

    • package jerrymice;
      
      import jerrymice.catalina.Context;
      import cn.hutool.core.io.FileUtil;
      import cn.hutool.core.thread.ThreadUtil;
      import cn.hutool.core.util.ArrayUtil;
      import cn.hutool.core.util.StrUtil;
      import cn.hutool.log.LogFactory;
      import cn.hutool.system.SystemUtil;
      import jerrymice.catalina.Host;
      import jerrymice.http.Request;
      import jerrymice.http.Response;
      import jerrymice.util.Constant;
      import jerrymice.util.ServerXmlUtil;
      import sun.awt.windows.WPrinterJob;
      
      import java.io.File;
      import java.io.IOException;
      import java.io.OutputStream;
      import java.net.ServerSocket;
      import java.net.Socket;
      import java.util.*;
      
      /**
       * @author :xiaosong
       * @description:專案的啟動類
       * @date :2020/7/28 20:41
       */
      public class Bootstrap {
          /**
           * 定義伺服器的埠號
           */
          final static int PORT = 10086;
          public static void main(String[] args) {
              try {
                  // 列印jvm資訊
                  logJvm();
                  // 在port埠上新建serverSocket
                  ServerSocket serverSocket = new ServerSocket(PORT);
                  // 建立host物件,host物件對於一個配置檔案來說是唯一的,所以在迴圈外建立
                  Host host = new Host();
                  // 外部使用一個while迴圈,當處理完一個Socket的連結請求之後,再處理下一個連結請求
                  while (true) {
                      Socket socket = serverSocket.accept();
                      // 使用lambda表示式代替Runnable
                      Runnable runnable = () -> {
                          try {
                              // 獲取輸入流,這個輸入流表示的是收到一個瀏覽器客戶端的請求
                              Request request = new Request(socket, host);
      
                              System.out.println("瀏覽器的輸入資訊: \r\n" + request.getRequestString());
                              Response response = new Response();
                              // 先將html資訊寫入到response的Writer的StringWriter中
                              String uri;
                              uri = request.getUri();
                              if (uri == null) {
                                  return;
                              }
                              Context context = request.getContext();
                              if ("/".equals(uri)) {
                                  String html = "Hello JerryMice";
                                  response.getWriter().println(html);
                              } else {
                                  // removePrefix()方法可以去掉字串指定的字首
                                  String fileName = StrUtil.removePrefix(uri, "/");
                                  File file = FileUtil.file(context.getDocBase(), fileName);
                                  if (file.exists()) {
                                      //如果檔案存在,那就去試圖訪問
                                      String fileContent = FileUtil.readUtf8String(file);
                                      // 寫入到response中
                                      response.getWriter().println(fileContent);
                                      // 判斷是否是模擬的耗時任務
                                      if ("timeConsume.html".equals(fileName)) {
                                          ThreadUtil.sleep(1000);
                                      }
                                  } else {
                                      System.out.println("File not found!");
                                  }
                              }
                              System.out.println(uri);
                              // 開啟輸出流,準備給客戶端輸出資訊
                              handle200(socket, response);
                          } catch (IOException e) {
                              LogFactory.get().error(e);
                          }
                      };
                      jerrymice.util.ThreadUtil.run(runnable);
                  }
              } catch (IOException e) {
                  LogFactory.get().error(e);
              }
          }
      
          private static void logJvm() {
              // 建立一個Map用於儲存各種資訊
              Map<String, String> infoMap = new LinkedHashMap<>();
              infoMap.put("Server version", "JerryMice 1.0.0");
              infoMap.put("Server build", "2020-08-03");
              infoMap.put("OS:\t", SystemUtil.get("os.name"));
              infoMap.put("OS version", SystemUtil.get("os.version"));
              infoMap.put("Architecture", SystemUtil.get("os.arch"));
              infoMap.put("Java Home", SystemUtil.get("java.home"));
              infoMap.put("JSM Version", SystemUtil.get("java.runtime.version"));
              infoMap.put("JVM Vendor", SystemUtil.get("java.vm.specification.vendor"));
              Set<String> keys = infoMap.keySet();
              for (String key : keys) {
                  // 呼叫hutool的LogFactory工廠函式獲取logger,logger會自動根據log4j.properties來對Log4j的Logger進行配置
                  LogFactory.get().info(key + ":\t\t" + infoMap.get(key));
              }
          }
      
          /**
           * @param socket:
           * @param response:Response物件,伺服器對瀏覽器請求的響應,可以通過response的getBody()獲取儲存在其中的html文字
           * @throws IOException
           */
          private static void handle200(Socket socket, Response response) throws IOException {
              // 獲取型別
              String contentType = response.getContentType();
              String headText = Constant.responseHead200;
              headText = StrUtil.format(headText, contentType);
              byte[] head = headText.getBytes();
              // 獲取response中的html文字,這個html文字是通過writer寫到stringWriter字元流上的
              byte[] body = response.getBody();
              byte[] responseBytes = new byte[head.length + body.length];
              ArrayUtil.copy(head, 0, responseBytes, 0, head.length);
              ArrayUtil.copy(body, 0, responseBytes, head.length, body.length);
      
              OutputStream outputStream = socket.getOutputStream();
              outputStream.write(responseBytes);
              socket.close();
          }
      }