编译java类文件后,javac启动服务器能正常访问servlet容器(基于socket和servlet编写)

作者: admin 分类: Tomcat 发布时间: 2019-11-13 14:14  阅读: 53 views

最近学习tomcat的结构,书是比较早的《how tomcat works》,上一章的示例是一个简单的web服务器,这一章是在之前的基础上扩展,增加了servlet的内容。整个编码完成后用java / javac 命令运行,可以访问servlet容器。

本示例总共有6个类
– HttpServer(一个ServerSocket服务器用来接收请求)
– Request (请求类,用来解释socket请求信息,实现了ServletRequest)
– Response (响应类,根据请求寻找资源并返回)
– StaticResourceProcessor (处理静态资源的请求)
– ServletProcessor (处理servlet请求)
– Constants (常量类)
– PrimitiveServlet (简单的servlet实现类)

1.HttpServer.java

package com.chl.webserver.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;


public class HttpServer {

    private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

    private boolean shutdown = false;

    public static void main(String[] args) {
        HttpServer hs = new HttpServer();
        hs.await();
    }

    //循环等待请求
    public void await() {
        ServerSocket ss = null;
        int port = 8080;
        try {
            ss = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
        }catch(IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

        while(!shutdown) {
            Socket socket = null;
            InputStream input = null;
            OutputStream output = null;
            try {

                socket = ss.accept();
                input = socket.getInputStream();
                output = socket.getOutputStream();

                Request req = new Request(input);
                req.parse();

                Response res = new Response(output);
                res.setRequest(req);

                if(req.getUri().startsWith("/servlet/")) {
                    ServletProcessor sp = new ServletProcessor();
                    sp.process(req, res);
                }else {
                    StaticResourceProcessor sr = new StaticResourceProcessor();
                    sr.process(req, res);
                }

                socket.close();
                shutdown = req.getUri().equals(SHUTDOWN_COMMAND);

            }catch(Exception e) {
                e.printStackTrace();
                System.exit(1);
            }
        }


    }

}

2.Request.java

package com.chl.webserver.servlet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;

import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Request implements ServletRequest {
    // 输入流
    private InputStream input;
    // 地址
    private String uri;

    public Request(InputStream input) {
        this.input = input;
    }

    public String getUri() {
        return uri;
    }

    //解析http请求中的原始数据。
    void parse() {
         //Read a set of characters from the socket
         StringBuffer request = new StringBuffer(2048);
         int i ;
         byte[] buffer = new byte[2048];
         try {
             i = input.read(buffer);
         }catch(IOException e) {
             e.printStackTrace();
             i = -1;
         }
         for(int j = 0; j< i;j++) {
             request.append((char)buffer[j]);
         }
         System.out.println(request.toString());
         uri = parseUri(request.toString());
     }

    //从 url中返回uri
    private String parseUri(String requestString) {
        int index1 , index2;
        index1 = requestString.indexOf(" ");
        if(index1 != -1) {
            index2 = requestString.indexOf(" ", index1 + 1);
            if(index2 > index1) {
                return requestString.substring(index1+1, index2);
            }
        }
        return null;
    }

    @Override
    public Object getAttribute(String name) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Enumeration<String> getAttributeNames() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getCharacterEncoding() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
        // TODO Auto-generated method stub

    }

    @Override
    public int getContentLength() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public long getContentLengthLong() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public String getContentType() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getParameter(String name) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Enumeration<String> getParameterNames() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String[] getParameterValues(String name) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getProtocol() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getScheme() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getServerName() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getServerPort() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getRemoteAddr() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getRemoteHost() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setAttribute(String name, Object o) {
        // TODO Auto-generated method stub

    }

    @Override
    public void removeAttribute(String name) {
        // TODO Auto-generated method stub

    }

    @Override
    public Locale getLocale() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Enumeration<Locale> getLocales() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isSecure() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public RequestDispatcher getRequestDispatcher(String path) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getRealPath(String path) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getRemotePort() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public String getLocalName() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getLocalAddr() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getLocalPort() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public ServletContext getServletContext() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public AsyncContext startAsync() throws IllegalStateException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
            throws IllegalStateException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isAsyncStarted() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isAsyncSupported() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public AsyncContext getAsyncContext() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public DispatcherType getDispatcherType() {
        // TODO Auto-generated method stub
        return null;
    }
}

3.Response.java

package com.chl.webserver.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Locale;

import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;

public class Response implements ServletResponse {

    private static final int BUFFER_SIZE = 1024;

    Request request;
    OutputStream output;
    PrintWriter writer;

    //构造函数创建output对象
    public Response(OutputStream output) {
        this.output = output;
    }

    //传递request对象给reponse
    public void setRequest(Request request) {
        this.request = request;
    }

    // 输出一个静态资源
    public void sendStaticResource() throws IOException{
        byte[] bytes = new byte[BUFFER_SIZE];
        FileInputStream fis = null;
        try {
            File file = new File(Constants.WEB_ROOT,request.getUri());
            if(file.exists()) {
                fis = new FileInputStream(file);
                int ch = fis.read(bytes,0,BUFFER_SIZE);
                while( ch != -1) {
                    output.write(bytes,0,BUFFER_SIZE);
                    ch = fis.read(bytes,0,BUFFER_SIZE);
                }
            }else { //如果不存在,则发送一个异常信息
                String errorMessage = "HTTP/1.1 404 File Not Found \r\n"+
                    "Content-Type : text/html\r\n"+
                    "Content-Length:23\r\n"+
                    "\r\n"+
                    "<h1>File Not Found</h1>";
                System.out.println(errorMessage);
                output.write(errorMessage.getBytes());
            }


        }catch(Exception e) {
            System.out.println(e.toString());
        }finally {
            if(fis != null)
                fis.close();
        }
    }

    @Override
    public String getCharacterEncoding() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getContentType() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PrintWriter getWriter() throws IOException {

        writer = new PrintWriter(output,true);

        return writer;
    }

    @Override
    public void setCharacterEncoding(String charset) {
        // TODO Auto-generated method stub

    }

    @Override
    public void setContentLength(int len) {
        // TODO Auto-generated method stub

    }

    @Override
    public void setContentLengthLong(long len) {
        // TODO Auto-generated method stub

    }

    @Override
    public void setContentType(String type) {
        // TODO Auto-generated method stub

    }

    @Override
    public void setBufferSize(int size) {
        // TODO Auto-generated method stub

    }

    @Override
    public int getBufferSize() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void flushBuffer() throws IOException {
        // TODO Auto-generated method stub

    }

    @Override
    public void resetBuffer() {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean isCommitted() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void reset() {
        // TODO Auto-generated method stub

    }

    @Override
    public void setLocale(Locale loc) {
        // TODO Auto-generated method stub

    }

    @Override
    public Locale getLocale() {
        // TODO Auto-generated method stub
        return null;
    }
}

4.StaticResourceProcessor.java

package com.chl.webserver.servlet;

import java.io.IOException;

/**
 * 处理对静态资源的请求
 * @author chenhailong
 *
 */
public class StaticResourceProcessor {

    public void process(Request request, Response response) {

        try {
            response.sendStaticResource();
        }catch(IOException e) {
            e.printStackTrace();
        }

    }
}

5.ServletProcessor.java

package com.chl.webserver.servlet;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;

import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class ServletProcessor {


    public void process(Request request, Response response) {

        String uri = request.getUri();
        String servletName = uri.substring(uri.lastIndexOf("/") + 1);
        URLClassLoader loader = null;

        try {
            URL[] urls = new URL[1];
            URLStreamHandler streamHandler = null;
            File classPath = new File(Constants.WEB_ROOT);
            System.out.println("classPath.getCanonicalPath()--" + classPath.getCanonicalPath());
            String repository = (new URL("file",null,classPath.getCanonicalPath() + File.separator)).toString();
            urls[0] = new URL(null, repository,streamHandler);
            loader = new URLClassLoader(urls);

        }catch(IOException e) {
            System.out.println(e.toString());
        }

        Class myClass = null;
        try {
            String curPackageName = ServletProcessor.class.getPackage().getName();
            servletName = curPackageName + "." + servletName;
            myClass = loader.loadClass(servletName);
        }catch(ClassNotFoundException e) {
        }

        Servlet servlet = null;
        try {
            servlet = (Servlet)myClass.newInstance();

            servlet.service((ServletRequest)request, (ServletResponse)response);

        }catch(Exception e) {
            System.out.println("+++"+e.toString());
        }catch(Throwable e) {
            System.out.println("###"+e.toString());
        }


    }


}

6.Constants.java

package com.chl.webserver.servlet;

import java.io.File;

public class Constants {

    public static final String WEB_ROOT = 
            System.getProperty("user.dir") + File.separator + "com/chl/webserver/servlet/";
}

Servlet编程是通过javax.servlet和javax.servlet.http这两个包的类和接口来实现的。所有的servlet必须实现或继承实现该接口的类。

7.PrimitiveServlet.java


package com.chl.webserver.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.*; /** * Servlet implementation class PrimitiveServlet * 所有的servlet程序都必须实现该接口或继承自实现了该接口的类。 */ public class PrimitiveServlet implements Servlet { @Override public void init(ServletConfig config) throws ServletException { //生命周期方法,可以通过覆盖该方法来初始化对象,如加载数据库,值初始化 } @Override public ServletConfig getServletConfig() { // TODO Auto-generated method stub return null; } @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { //req包含HTTP请求信息,res可以封装servlet的响应。 System.out.println("are you ok !"); PrintWriter out = res.getWriter(); out.println("Hello , Roses are red."); } @Override public String getServletInfo() { // TODO Auto-generated method stub return null; } @Override public void destroy() { //通常在servlet容器正在关闭正在被关闭或者servlet容器需要一些空闲内存的时候调用,尽在所有servlet线程的service方法已经退出或者超时淘汰的时候。 System.out.println("des !"); } }

先执行编译类文件

javac -classpath /Users/chenhailong/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar -g ./com/chl/webserver/servlet/*.java


./com/chl/webserver/servlet/Request.java:11: 错误: 找不到符号 import javax.servlet.AsyncContext; ^ 符号: 类 AsyncContext 位置: 程序包 javax.servlet ./com/chl/webserver/servlet/Request.java:12: 错误: 找不到符号 import javax.servlet.DispatcherType; ^ 符号: 类 DispatcherType 位置: 程序包 javax.servlet ./com/chl/webserver/servlet/Request.java:252: 错误: 找不到符号 public AsyncContext startAsync() throws IllegalStateException { ^ ... ... ... ./com/chl/webserver/servlet/Response.java:98: 错误: 方法不会覆盖或实现超类型的方法 @Override ^ 注: ./com/chl/webserver/servlet/Request.java使用或覆盖了已过时的 API。 注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。 15 个错误

处理:原因是我引入了 servlet-api-2.5.jar 和 javax.servlet-api-4.0.1.jar应该使用第二个包

修改jar包名称后可以看到编译成功

chenhailongdeMacBook-Pro:src chenhailong$ javac -classpath /Users/chenhailong/.m2/repository/javax/servlet/javax.servlet-api/4.0.1/javax.servlet-api-4.0.1.jar -g ./com/chl/webserver/servlet/*.java
注: ./com/chl/webserver/servlet/Request.java使用或覆盖了已过时的 API。
注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。

然后运行项目

chenhailongdeMacBook-Pro:servlet chenhailong$ java -classpath /Users/chenhailong/.m2/repository/javax/servlet/javax.servlet-api/4.0.1/javax.servlet-api-4.0.1.jar HttpServer
错误: 找不到或无法加载主类 HttpServer

是因为类中有写包名,运行的时候需要带上包名(从包最外层执行)

## 执行
chenhailongdeMacBook-Pro:src chenhailong$ java com.chl.webserver.servlet.HttpServer

## 查看运行状态

chenhailongdeMacBook-Pro:Java chenhailong$ jps -v
63738 HttpServer
63739 Jps -Dapplication.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home -Xms8m

可以看到 HttpServer已经正常运行了

然后在地址栏访问 http://127.0.0.1:8080/servlet/PrimitiveServlet会报错如下

chenhailongdeMacBook-Pro:src chenhailong$ java com.chl.webserver.servlet.HttpServer
Exception in thread "main" java.lang.NoClassDefFoundError: javax/servlet/ServletRequest
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at com.chl.webserver.servlet.HttpServer.await(HttpServer.java:43)
    at com.chl.webserver.servlet.HttpServer.main(HttpServer.java:19)
Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletRequest
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 14 more

这是由于启动时没有指定 classpath的servlet-api.jar包,重新运行如下:

java -classpath /Users/chenhailong/.m2/repository/javax/servlet/javax.servlet-api/4.0.1/javax.servlet-api-4.0.1.jar:./ com.chl.webserver.servlet.HttpServer

重新访问http://127.0.0.1:8080/servlet/PrimitiveServlet

java.lang.ClassNotFoundException: Primitiveservlet
java.lang.NullPointerException
GET /servlet/PrimitiveServlet HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

或者报错

Exception in thread "main" java.lang.NoClassDefFoundError: PrimitiveServlet (wrong name: com/chl/webserver/servlet/PrimitiveServlet)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at com.chl.webserver.servlet.ServletProcessor.process(ServletProcessor.java:39)
    at com.chl.webserver.servlet.HttpServer.await(HttpServer.java:51)
    at com.chl.webserver.servlet.HttpServer.main(HttpServer.java:19)

是因为 myClass = loader.loadClass(servletName);
这里的类名需要加 包名。
修改之后正常运行并输出内容

are you ok !

   原创文章,转载请标明本文链接: 编译java类文件后,javac启动服务器能正常访问servlet容器(基于socket和servlet编写)

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

更多阅读