/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package javax.servlet;
import java.io.IOException;
/**
* Defines methods that all servlets must implement.
*
* <p>A servlet is a small Java program that runs within a Web server.
* Servlets receive and respond to requests from Web clients,
* usually across HTTP, the HyperText Transfer Protocol.
*
* <p>To implement this interface, you can write a generic servlet
* that extends
* <code>javax.servlet.GenericServlet</code> or an HTTP servlet that
* extends <code>javax.servlet.http.HttpServlet</code>.
*
* <p>This interface defines methods to initialize a servlet,
* to service requests, and to remove a servlet from the server.
* These are known as life-cycle methods and are called in the
* following sequence:
* <ol>
* <li>The servlet is constructed, then initialized with the <code>init</code> method.
* <li>Any calls from clients to the <code>service</code> method are handled.
* <li>The servlet is taken out of service, then destroyed with the
* <code>destroy</code> method, then garbage collected and finalized.
* </ol>
*
* <p>In addition to the life-cycle methods, this interface
* provides the <code>getServletConfig</code> method, which the servlet
* can use to get any startup information, and the <code>getServletInfo</code>
* method, which allows the servlet to return basic information about itself,
* such as author, version, and copyright.
*
* @author Various
* @version $Version$
*
* @see GenericServlet
* @see javax.servlet.http.HttpServlet
*
*/
public interface Servlet {
/**
* Called by the servlet container to indicate to a servlet that the
* servlet is being placed into service.
*
* <p>The servlet container calls the <code>init</code>
* method exactly once after instantiating the servlet.
* The <code>init</code> method must complete successfully
* before the servlet can receive any requests.
*
* <p>The servlet container cannot place the servlet into service
* if the <code>init</code> method
* <ol>
* <li>Throws a <code>ServletException</code>
* <li>Does not return within a time period defined by the Web server
* </ol>
*
*
* @param config a <code>ServletConfig</code> object
* containing the servlet's
* configuration and initialization parameters
*
* @exception ServletException if an exception has occurred that
* interferes with the servlet's normal
* operation
*
* @see UnavailableException
* @see #getServletConfig
*
*/
public void init(ServletConfig config) throws ServletException;
/**
*
* Returns a {@link ServletConfig} object, which contains
* initialization and startup parameters for this servlet.
* The <code>ServletConfig</code> object returned is the one
* passed to the <code>init</code> method.
*
* <p>Implementations of this interface are responsible for storing the
* <code>ServletConfig</code> object so that this
* method can return it. The {@link GenericServlet}
* class, which implements this interface, already does this.
*
* @return the <code>ServletConfig</code> object
* that initializes this servlet
*
* @see #init
*
*/
public ServletConfig getServletConfig();
/**
* Called by the servlet container to allow the servlet to respond to
* a request.
*
* <p>This method is only called after the servlet's <code>init()</code>
* method has completed successfully.
*
* <p> The status code of the response always should be set for a servlet
* that throws or sends an error.
*
*
* <p>Servlets typically run inside multithreaded servlet containers
* that can handle multiple requests concurrently. Developers must
* be aware to synchronize access to any shared resources such as files,
* network connections, and as well as the servlet's class and instance
* variables.
* More information on multithreaded programming in Java is available in
* <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
* the Java tutorial on multi-threaded programming</a>.
*
*
* @param req the <code>ServletRequest</code> object that contains
* the client's request
*
* @param res the <code>ServletResponse</code> object that contains
* the servlet's response
*
* @exception ServletException if an exception occurs that interferes
* with the servlet's normal operation
*
* @exception IOException if an input or output exception occurs
*
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
/**
* Returns information about the servlet, such
* as author, version, and copyright.
*
* <p>The string that this method returns should
* be plain text and not markup of any kind (such as HTML, XML,
* etc.).
*
* @return a <code>String</code> containing servlet information
*
*/
public String getServletInfo();
/**
*
* Called by the servlet container to indicate to a servlet that the
* servlet is being taken out of service. This method is
* only called once all threads within the servlet's
* <code>service</code> method have exited or after a timeout
* period has passed. After the servlet container calls this
* method, it will not call the <code>service</code> method again
* on this servlet.
*
* <p>This method gives the servlet an opportunity
* to clean up any resources that are being held (for example, memory,
* file handles, threads) and make sure that any persistent state is
* synchronized with the servlet's current state in memory.
*
*/
public void destroy();
}
在Servlet接口中声明了5个方法里,init(),service(),destory()方法是与servlet的生命周期相关的方法,当实例化某个servlet类后,Servlet容器会调用其init()方法进行初始化,至于Servlet容器是如何调用,后面会分析。后面会被类与类之间的衔接来做一定的分析。在servlet接收任何请求之前,必须是经过正确初始化的。一般情况下init()方法可以留空。
当servlet的一个客户端请求到达后,servlet容器就调用相应的servlet的service()方法,并将javax.serlvet.servletRequest对象和javax.servlet.servletResponse对象作为参数传入,ServletResquest对象包含客户端的HTTP请求的信息,ServletResponse对象则封装servlet的请求信息。这两个类的设计十分有趣,在后面就具体分析这两个类。在servlet对象的整个生命周期内,service()方法会被多次调用。
在将servlet实例从服务中移除前,servlet容器会调用servlet实例的destory()方法,一般当Servlet容器关闭或servlet容器要释放内存时,才会将servlet实例移除,而且只有当servlet实例的service()方法中的线程都退出或执行超时后,才会调用destory()方法,当servlet容器调用了某个servlet实例的destory()方法后,它就不再调用该servlet实例的service()方法了,调用destory()方法让servlet对象有机会去清理自身持有的资源,如内存,文件句柄和线程等,确保有所的持久化与内存中该servlet对象的当前状态同步。
下面<how tomcat work>中的一个servlet类:
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
public class PrimitiveServlet implements Servlet {
public void init(ServletConfig config) throws ServletException {
System.out.println("init");
}
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
System.out.println("from service");
PrintWriter out = response.getWriter();
out.println("Hello. Roses are red.");
out.print("Violets are blue.");
}
public void destroy() {
System.out.println("destroy");
}
public String getServletInfo() {
return null;
}
public ServletConfig getServletConfig() {
return null;
}
}
一个功能齐全的servlet容器有以下几件事要做:
1,当第一次调用某个servlet,要载入该servlet类,并调用其init()方法(仅此一次);
2,针对每个request请求,创建一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例;
3,调用该servlet的service()方法,将servletRequest对象和servletResponse对象作为参数传入;
4,当关闭该servlet类时,调用其destory()方法,并卸载该servlet类。
那么从客户连接到tomcat上来,最重要要做的事就是如何处理客户的请求并且把请求传递给Servlet容器,虽然看起来很简单,但是这整个过程确是如此的繁杂,包括字符串的解析,以及cookies,session,参数请求等等的工作。那么我们首先的工作就是分析客户连接到tomcat上来这一块,也就是tomcat的默认连接器。
Tomcat的连接器必须实现org.apache.catalina.Connecor接口,在接口中声明了很多方法,其中最重要的是getContainer(),setContainer(),createRequest(),createResponse()方法。为什么这么说呢?看看以下分析:
setContainer()方法用于将连接器和某个servlet容器相关联。如果不跟特定的servlet容器关联起来,我靠,你准备要把包装好的HttpRequest和HttpResponse对象传给谁阿??
getContainer()方法返回与当前连接器相关联的servlet容器。createRequest()方法会引入的HTTP请求创建request对象,相应的,createResponse()方法会创建一个response对象。
org.apache.catalina.connector.http.HttpConnector类实现了Connector接口,首先大概说以下HttpConnector类做的一些工作:
1,首先它实现了org.apache.catalina.Connector接口(使其可以成为Catalina中的连接器),又实现了java.lang.Runnble接口(确保它的实例在自己的线程中运行)和实现了org.apache.catalina.Lifecycle接口,Lifecycle接口用于维护每个实现了该接口的每个Catalina组件的生命周期。但是这个接口目前不是重点,在后面会讲到。
2,由于HttpConnector实现了Lifecycle接口,因此当创建一个HttpConnector实例后,就应该调用其initialize()方法和start()方法,这两个方法如何调用在后面会详细讲到,现在就记住一点:这两个方法只应该被调用一次。
3,HttpConnector类还负责创建服务器套接字,这是最重要的一点,没有这个,啥事都不用做了。HttpConnector类的initialize()方法会调用一个私有方法open(),后者返回一个java.net.ServerSocket实例,赋值给成员变量serverSocket。但是,这里没有直接调用ServerSocket类的构造函数,而是通过open()方法从一个服务器套接字工厂得到一个实例。
4,维护HttpProcessor实例:HttpProcessor实例是用来处理HTTP请求的,在Tomcat的默认连接器中,HttpConnector实例有一个HttpProcessor对象池,每个HttpProcessor实例都运行在其自己的线程中。这样HttpConnector实例就可以同时处理多个HTTP请求了。
好了,接下来就是具体看看它们这几个步骤中tomcat源码的实现:
首先我们从连接用户开始入手:
org.apache.catalina.connector.http.HttpConnector
/**
* The shutdown signal to our background thread
*/
private boolean stopped = false;
/**
* Use TCP no delay ?
*/
private boolean tcpNoDelay = true;
/**
* Timeout value on the incoming connection.
* Note : a value of 0 means no timeout.
*/
private int connectionTimeout = Constants.DEFAULT_CONNECTION_TIMEOUT;
/**
* Has this component been started yet?
*/
private boolean started = false;
/**
* The thread synchronization object.
*/
private Object threadSync = new Object();
//该方法运行在一个线程中
// ---------------------------------------------- Background Thread Methods
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
public void run() {
// Loop until we receive a shutdown command
//如果tomcat接收到shutdown的命令,那么将会关闭连接器
while (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
// if (debug >= 3)
// log("run: Waiting on serverSocket.accept()");
socket = serverSocket.accept();//服务器等待用户的连接
// if (debug >= 3)
// log("run: Returned from serverSocket.accept()");
if (connectionTimeout > 0)
socket.setSoTimeout(connectionTimeout);//设置超时时间
socket.setTcpNoDelay(tcpNoDelay);//启用/禁用 TCP_NODELAY
} catch (AccessControlException ace) {
log("socket accept security exception", ace);
continue;
} catch (IOException e) {
// if (debug >= 3)
// log("run: Accept returned IOException", e);
try {
// If reopening fails, exit
synchronized (threadSync) {
if (started && !stopped)//如何其他组件还在运行或者socket还没关闭
log("accept error: ", e);
if (!stopped) {//如果socket还没关闭
// if (debug >= 3)
// log("run: Closing server socket");
serverSocket.close();//关闭服务器
// if (debug >= 3)
// log("run: Reopening server socket");
serverSocket = open();//创建新的serverSocket对象
}
}
// if (debug >= 3)
// log("run: IOException processing completed");
} catch (IOException ioe) {
log("socket reopen, io problem: ", ioe);
break;
} catch (KeyStoreException kse) {
log("socket reopen, keystore problem: ", kse);
break;
} catch (NoSuchAlgorithmException nsae) {
log("socket reopen, keystore algorithm problem: ", nsae);
break;
} catch (CertificateException ce) {
log("socket reopen, certificate problem: ", ce);
break;
} catch (UnrecoverableKeyException uke) {
log("socket reopen, unrecoverable key: ", uke);
break;
} catch (KeyManagementException kme) {
log("socket reopen, key management problem: ", kme);
break;
}
continue;
}
// Hand this socket off to an appropriate processor
HttpProcessor processor = createProcessor();//从HttpProcessor获得一个HttpProcessor对象或者是当池中没有HttpProcessor对象。
if (processor == null) {
try {
log(sm.getString("httpConnector.noProcessor"));
socket.close();//关闭socket
} catch (IOException e) {
;
}
continue;
}
// if (debug >= 3)
// log("run: Assigning socket to processor " + processor);
processor.assign(socket);//处理HTTP请求
// The processor will recycle itself when it finishes
}
// Notify the threadStop() method that we have shut ourselves down
// if (debug >= 3)
// log("run: Notifying threadStop() that we have shut down");
synchronized (threadSync) {
threadSync.notifyAll();//唤醒有所的线程
}
}
这段代码衍生出来的需要注意的东西有很多,但是先说一点,上面的log()方法是日志记录,这里暂时不说它,后面会详细的说它。好了,现在要从这段代码开始来把握几点需要注意的地方。有些地方需要加上UML来进行解释会比较好!
第一点需要关注的地方:serverSocket = open();我们来看看open()方法做了些什么事情先。首先它是一个私有的方法,也就是用户不能去重写它的方法。
org.apache.catalina.connector.http.HttpConnector
/**
* The IP address on which to bind, if any. If <code>null</code>, all
* addresses on the server will be bound.
*/
private String address = null;
/**
* The port number on which we listen for HTTP requests.
*/
private int port = 8080;
/**
* The accept count for this Connector.
*/
private int acceptCount = 10;
private ServerSocket open()
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException,
KeyManagementException
{
// Acquire the server socket factory for this Connector
ServerSocketFactory factory = getFactory();利用工厂方法来创建一个套接字
// If no address is specified, open a connection on all addresses
if (address == null) {
log(sm.getString("httpConnector.allAddresses"));
try {
return (factory.createSocket(port, acceptCount));//返回创建好了的套接字
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + port);
}
}
// Open a server socket on the specified address
try {
InetAddress is = InetAddress.getByName(address);//在给定主机名的情况下确定主机的IP地址
log(sm.getString("httpConnector.anAddress", address));
try {
return (factory.createSocket(port, acceptCount, is));
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + address +
":" + port);
}
} catch (Exception e) {
log(sm.getString("httpConnector.noAddress", address));
try {
return (factory.createSocket(port, acceptCount));
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + port);
}
}
}
看看open()方法:很简单,不多说。主要要加索,保证线程安全
/**
* Return the server socket factory used by this Container.
*/
public ServerSocketFactory getFactory() {
if (this.factory == null) {
synchronized (this) {
this.factory = new DefaultServerSocketFactory();
}
}
return (this.factory);
}
那么从上面的代码用到了工厂设计模式,具体继续跟踪源码的实现:
apache.catalina.net.ServerSocketFactory,该类是一个接口,提供了三个方法:
其实也就是java.net.ServerSocket的三个方法构造,只不过换了以下名字调用而已,它的子类DefaultServerSocketFactory实现了这个接口,并且这个类是fianl类,不可以被集成。具体看看这个类的实现:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.net;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.KeyManagementException;
import java.security.cert.CertificateException;
import org.apache.catalina.net.ServerSocketFactory;
/**
* Default server socket factory, which returns unadorned server sockts.
*
* @author [email protected]
* @author Harish Prabandham
* @author Craig R. McClanahan
*/
public final class DefaultServerSocketFactory implements ServerSocketFactory {
// --------------------------------------------------------- Public Methods
/**
* Returns a server socket which uses all network interfaces on
* the host, and is bound to a the specified port. The socket is
* configured with the socket options (such as accept timeout)
* given to this factory.
*
* @param port the port to listen to
*
* @exception IOException input/output or network error
* @exception KeyStoreException error instantiating the
* KeyStore from file (SSL only)
* @exception NoSuchAlgorithmException KeyStore algorithm unsupported
* by current provider (SSL only)
* @exception CertificateException general certificate error (SSL only)
* @exception UnrecoverableKeyException internal KeyStore problem with
* the certificate (SSL only)
* @exception KeyManagementException problem in the key management
* layer (SSL only)
*/
public ServerSocket createSocket (int port)
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException,
KeyManagementException {
return (new ServerSocket(port));
}
/**
* Returns a server socket which uses all network interfaces on
* the host, is bound to a the specified port, and uses the
* specified connection backlog. The socket is configured with
* the socket options (such as accept timeout) given to this factory.
*
* @param port the port to listen to
* @param backlog how many connections are queued
*
* @exception IOException input/output or network error
* @exception KeyStoreException error instantiating the
* KeyStore from file (SSL only)
* @exception NoSuchAlgorithmException KeyStore algorithm unsupported
* by current provider (SSL only)
* @exception CertificateException general certificate error (SSL only)
* @exception UnrecoverableKeyException internal KeyStore problem with
* the certificate (SSL only)
* @exception KeyManagementException problem in the key management
* layer (SSL only)
*/
public ServerSocket createSocket (int port, int backlog)
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException,
KeyManagementException {
return (new ServerSocket(port, backlog));
}
/**
* Returns a server socket which uses only the specified network
* interface on the local host, is bound to a the specified port,
* and uses the specified connection backlog. The socket is configured
* with the socket options (such as accept timeout) given to this factory.
*
* @param port the port to listen to
* @param backlog how many connections are queued
* @param ifAddress the network interface address to use
*
* @exception IOException input/output or network error
* @exception KeyStoreException error instantiating the
* KeyStore from file (SSL only)
* @exception NoSuchAlgorithmException KeyStore algorithm unsupported
* by current provider (SSL only)
* @exception CertificateException general certificate error (SSL only)
* @exception UnrecoverableKeyException internal KeyStore problem with
* the certificate (SSL only)
* @exception KeyManagementException problem in the key management
* layer (SSL only)
*/
public ServerSocket createSocket (int port, int backlog,
InetAddress ifAddress)
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException,
KeyManagementException {
return (new ServerSocket(port, backlog, ifAddress));
}
是吧,是java.nio.ServerSocket类的三个构造方法吧。port端口号,backlog可接收的队列长度,ifAddress要将服务器绑定到的InetAddress