今回はリクエストを受け取ってからServletを呼び出すまでの処理を見る
「リクエスト処理編」となります。
第1回: 準備編
第2回: 起動編
【安価即出荷】 by :202102211558558165:福音商店 48-Inch Buster 36 Moss 犬用品 Large, Dog Moss by 36 Happy Hounds Bed,-マット、プレート
リクエストを待ち受けるConnector まずConnector関連の主要クラスにどのようなものがいるのか見てみましょう。
簡単なクラス図のようなものを用意したのでご覧ください。
AbstractEndpointの内部インターフェースであるHandlerの関連のクラスは
すべてが内部クラスや内部インターフェースであり、
しかも複数のクラス間で名前が同じだったり、継承後の名前も同じだったりするので
わかりづらいかもしれませんが、よく見れば理解できるはずです。
以下に簡単な解説を載せておきます。処理自体はこれから見ていきます。
(Apacheとの連携なし、かつ旧来のI/Oを使う場合の処理を見ます)
AcceptorがacceptSocketで要求を受けた後は、
Runnableの実装クラスであるSocketProcessorを別スレッドで実行しています。
SocketProcessorはJioEndpointが持っているhandler(AbstractEndpoint.Handlerの実装クラス)の
processを呼び出しています。
以下で見るように、今回の例だとhandlerの実装クラスは
Http11Protocol内のHttp11ConnectionHandlerとなります。
続いてHttp11ConnectionHandlerのprocessを見てみましょう。
と言いたいところですが、processが定義されているのは、親クラスの方なので、
親クラスAbstractConnectionHandlerの方も一緒に見ます。
Processor (Http11Processor)を生成し、processを呼び出しています。
(生成時、ProcessorがAdapterを使えるようにセットしています)
生成されるのはHttp11Processorですが、先ほどと同じくprocessが定義されているのは
親クラスの方なので親クラスの方を見てみましょう。
ここの処理では、Socketの入力ストリームを地道に読み込んで、Requestに詰め込んでいます。
HttpServletRequestのメソッドでリクエストに関わる様々な情報(HTTPメソッドの名前とか)を
取得できるのはこの処理のおかげです。
(ここではまだHttpServletRequestになっていませんが)
エラーが無ければAdapterのserviceが呼び出されます。
まず、connector.createRequest()でRequestを生成しています。
そして生成したRequestにsetCoyoteRequestで、引数で渡されたRequestをセットしています。
同じ名前のRequestが2つ登場しましたが、これらはパッケージが違います。
org.apache.coyoteパッケージのRequestはHttpServletRequestの実装クラスではありません。
片やorg.apache.catalina.connectorのRequestは上記ソースの通りHttpServletRequestの実装クラスです。
「ああ、じゃあこのorg.apache.catalina.connector.RequestがServletに渡されるわけね」
そう思うかもしれませんが・・・・・・
違います!
中を見てみると serServerPort() のようなセッターメソッドがあちこちに定義されています。
HttpServletRequestにこのようなメソッドは存在しないので直接は呼び出せませんが、
キャストすれば呼び出すことができます。これはマズイ。よろしくない。
ということでRequestFacadeというラッパークラスが登場しました。
org.apache.catalina.connector.RequestのgetRequest()で返しているやつです。
このクラスもまたHttpServletRequestの実装クラスです。
そしてこのクラスは、HttpServletRequestが定義しているセッター以外を持っていません。
できる限り処理は委譲して済ませています。
これで安心してServletにHttpServletRequestを渡すことができますね。
さて、残りはServiceのコンテナを取得してメソッドを呼び出す処理です。
Serviceが持ってるコンテナとは何か。前回に答えがあります。StandardEngineですね。
そしてgetPipeline() やらgetFirst() やら invoke() といったメソッドを呼び出しています。
次はここら辺のコードを見ていきましょう。
次回: リクエスト処理編その2
簡単なクラス図のようなものを用意したのでご覧ください。
AbstractEndpointの内部インターフェースであるHandlerの関連のクラスは
すべてが内部クラスや内部インターフェースであり、
しかも複数のクラス間で名前が同じだったり、継承後の名前も同じだったりするので
わかりづらいかもしれませんが、よく見れば理解できるはずです。
以下に簡単な解説を載せておきます。処理自体はこれから見ていきます。
-
AbstractEndpoint関連
リクエストを待ち受けるためのインターフェース Acceptor と、
リクエストがあった時のハンドリングを行うためのインターフェース Handler を持つ。 -
ProtocolHandler関連
プロトコルごとの処理をハンドリングする。
内部クラスとして、AbstractEndpoint.Handlerの実装クラスを持つ。
この実装クラスはAbstractEndpointの実装クラスに渡され、
リクエストがあった時に使用される。
対応しているプロトコルや処理に使うAPIごとのサブクラスが存在する。
分類としては 以下のようになっている。
Apacheサーバーとの連携を行うか 処理に使用するAPI クラス 行う 旧来のI/O API AjpProtocol 新しいI/O※1のAPI AjpNioProtocol APR※2のAPI AjpAprProtocol 行わない 旧来のI/O API Http11Protocol 新しいI/OのAPI Http11NioProtocol APRのAPI Http11AprProtocol ※1 java.nioパッケージのAPI (ノンブロッキングI/Oの機能を使う)
※2 Apache Portable Runtime のこと。Apacheサーバーのライブラリ。
APRのライブラリはTomcatに付属されていないため使用するには
Tomcatの公式ページからdllをダウンロードするか(Windowsに限る)、
ライブラリをビルドする必要がある。 -
Processor関連
HTTPリクエストの内容(リクエストヘッダやリクエストラインなど)の読み込みを行う。
リクエストの情報をオブジェクトにセットした後、Adapterのサービスを呼び出す。
Processorの処理自体は、ProtocolHandler内に定義されているAbstractEndpoint.Handlerの
実装クラスから呼び出される。
Processorの実装クラスの分類は、ProtocolHandlerの実装クラスの分類と同じ。 -
Adapter
Processorに渡されたリクエスト情報をラップしてTomcatのコンテナに渡す。
(Apacheとの連携なし、かつ旧来のI/Oを使う場合の処理を見ます)
public class JIoEndpoint extends AbstractEndpoint {
protected Handler handler = null;
protected class Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
while (running) {
/ ...中略
/ 要求を待ち受ける
Socket socket = serverSocketFactory.acceptSocket(serverSocket);
if (running && !paused && setSocketOptions(socket)) {
/ ソケットを渡して要求処理
if (!processSocket(socket)) {
closeSocket(socket);
}
} else {
closeSocket(socket);
福音商店のBuster,Dog,Bed,,36,by,48-Inch,Large,,Moss,by,Happy,Hounds:202102211558558165ならショッピング!ランキングや口コミも豊富なネット通販。更にお得なPayPay残高も!スマホアプリも充実で毎日どこからでも気になる商品をその場でお求めいただけます。ペット用品、生き物,犬用品,ベッド、マット、カバー,マット、プレート
}
/ ...中略
}
state = AcceptorState.ENDED;
}
}
protected boolean processSocket(Socket socket) {
SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
/ ...中略
/ 別スレッドで処理を実行する
getExecutor().execute(new SocketProcessor(wrapper));
Buster Dog Bed, 36 by 48-Inch Large, Moss by Happy Hounds
return true;
}
protected class SocketProcessor implements Runnable {
protected SocketWrapper<Socket> socket = null;
public SocketProcessor(SocketWrapper<Socket> socket) {
if (socket==null) throw new NullPointerException();
this.socket = socket;
}
@Override
public void run() {
boolean launch = false;
synchronized (socket) {
try {
SocketState state = SocketState.OPEN;
/ ...中略
if ((state != SocketState.CLOSED)) {
if (status == null) {
state = handler.process(socket, SocketStatus.OPEN);
} else {
state = handler.process(socket,status);
}
- 庭 ガーデニング ガーデンファニチャー ガーデンフェンス アイアンフェンス945 16枚組 JF092599-6 フェンス アイアン ガーデンフェンス ガーデニング 枠 柵 仕切
- GSX-R1000(01·02年) シートカウル FRP/白 A-TECH(エーテック)
- 電設資材 パナソニック BQE35303S2EV 分電盤 EV·PHEV充電回路·太陽光発電·エコキュート·IH対応住宅 リミッタースペース付
- セット商品 HUAWEI P30 ブリージングクリスタル SIMフリースマートフォン + BAND 3 PRO/QUICKSAND GOLD
- ブルガリ BVLGARI カードケース 名刺入れ パスケース ブラック シルバー カーフレザー シャイニー リザードスキン
- (業務用200セット) 三菱鉛筆 シャープペン替芯 ユニ 0.5mm U05202ND 3H 生活用品 インテリア 雑貨 文具 オフィス用品
- novation LaunchPad Pro MKIII フィジカルコントローラー MIDIコントローラー ノベーション
- テーラー東洋 Tailor Toyo 港商×C.T.M スペシャルエディション スーべニアジャケット TT13430 ネイビー×ブラック色
- [RHS71W30E13RCSTW_LPG+KOJI] リンナイ ビルトインコンロ デリシア プロパン 幅75cm 乾電池タイプ 標準取替工事付
- 送料無料 マルヨ食品 香住蟹みそ(瓶詰) 60g×48個 01050 他商品との同梱不可
- 自転車置き場 DIY おしゃれ コンパクト 自転車ラック サイクルスタンド 6台用
- 防災用品 真空パック アルミ転写毛布 10枚(ブランケット 防寒 対策 グッズ 帰宅困難 オフィス 自治体 備蓄 災害 避難 グッズ 最低限)
- 中古車 フェラーリ 458イタリア 正規ディーラー車 フル電動シート Bカメラ
- 大型送料加算商品 純正部品ダイハツ アトレーワゴンチャイルドシート(ISOFIX固定専用)純正品番 08795-K9001
- Audison Voce Av12 Subwoofer 300Mm【並行輸入品】
}
/ ...中略
}
}
socket = null;
/ Finish up this request
}
}
}
AcceptorがacceptSocketで要求を受けた後は、
Runnableの実装クラスであるSocketProcessorを別スレッドで実行しています。
SocketProcessorはJioEndpointが持っているhandler(AbstractEndpoint.Handlerの実装クラス)の
processを呼び出しています。
以下で見るように、今回の例だとhandlerの実装クラスは
Http11Protocol内のHttp11ConnectionHandlerとなります。
public class Connector extends LifecycleMBeanBase {
public Connector(String protocol) {
/ ...中略
setProtocol(protocol);
Class<?> clazz = Class.forName(protocolHandlerClassName);
/ handlerのインスタンスを生成する
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
}
public void setProtocol(String protocol) {
/ ...中略
if ("HTTP/1.1".equals(protocol)) {
/ Http11Protocolをhandlerのクラスとする
setProtocolHandlerClassName("org.apache.coyote.http11.Http11Protocol");
}
/ ...中略
}
}
public class Http11Protocol extends AbstractHttp11JsseProtocol {
public Http11Protocol() {
endpoint = new JIoEndpoint();
cHandler = new Http11ConnectionHandler(this);
/ JioEndpointにHttp11ConnectionHandlerをセットする
((JIoEndpoint) endpoint).setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
}
続いてHttp11ConnectionHandlerのprocessを見てみましょう。
と言いたいところですが、processが定義されているのは、親クラスの方なので、
親クラスAbstractConnectionHandlerの方も一緒に見ます。
public abstract class AbstractProtocol implements ProtocolHandler,
MBeanRegistration {
protected abstract static class AbstractConnectionHandler
<S,P extends AbstractProcessor<S>>
implements AbstractEndpoint.Handler {
public SocketState process(SocketWrapper<S> socket,
SocketStatus status) {
P processor = connections.remove(socket.getSocket());
/ ...中略
socket.setAsync(false);
try {
/ ...中略
/ processorを生成する
if (processor == null) {
processor = createProcessor();
}
/ ...中略
SocketState state = SocketState.CLOSED;
do {
if (processor.isAsync() || state == SocketState.ASYNC_END) {
/ ...中略
} else if (processor.isComet()) {
/ ...中略
} else {
state = processor.process(socket);
/ ...中略
}
/ ...中略
} while (state == SocketState.ASYNC_END);
/ ...中略
return state;
} catch(java.net.SocketException e) {
/ ...中略
}
/ ...中略
return SocketState.CLOSED;
}
}
protected abstract P createProcessor();
}
public class Http11Protocol extends AbstractHttp11JsseProtocol {
protected static class Http11ConnectionHandler
extends AbstractConnectionHandler<Socket, Http11Processor> implements Handler {
@Override
protected Http11Processor createProcessor() {
/ Http11Processorを生成する
Http11Processor processor = new Http11Processor(
proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint,
proto.getMaxTrailerSize());
/ Adapterを設定する
processor.setAdapter(proto.adapter);
/ ...中略
return processor;
}
}
}
Processor (Http11Processor)を生成し、processを呼び出しています。
(生成時、ProcessorがAdapterを使えるようにセットしています)
生成されるのはHttp11Processorですが、先ほどと同じくprocessが定義されているのは
親クラスの方なので親クラスの方を見てみましょう。
public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
@Override
public SocketState process(SocketWrapper<S> socketWrapper)
throws IOException {
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
/ Setting up the I/O
setSocketWrapper(socketWrapper);
getInputBuffer().init(socketWrapper, endpoint);
getOutputBuffer().init(socketWrapper, endpoint);
/ ...中略
while (!error && keepAlive && !comet && !isAsync() &&
!endpoint.isPaused()) {
/ Parsing the request header
try {
/ リクエストラインの解析
if (!getInputBuffer().parseRequestLine(keptAlive)) {
/ ...中略
}
if (endpoint.isPaused()) {
/ ...中略
} else {
/ リクエストヘッダーの解析
if (!getInputBuffer().parseHeaders()) {
/ ...中略
}
}
} catch (IOException e) {
/ ...中略
}
if (!error) {
try {
/ リクエストメソッドやユーザーエージェントなどの読み込み
【安価即出荷】 by :202102211558558165:福音商店 48-Inch Buster 36 Moss 犬用品 Large, Dog Moss by 36 Happy Hounds Bed,-マット、プレート
prepareRequest();
} catch (Throwable t) {
/ ...中略
}
}
if (!error) {
try {
/ Adapterのserviceを呼び出す
adapter.service(request, response);
/ ...中略
} catch (Throwable t) {
/ ...中略
}
}
/ ...中略
}
/ ...中略
}
}
ここの処理では、Socketの入力ストリームを地道に読み込んで、Requestに詰め込んでいます。
HttpServletRequestのメソッドでリクエストに関わる様々な情報(HTTPメソッドの名前とか)を
取得できるのはこの処理のおかげです。
(ここではまだHttpServletRequestになっていませんが)
エラーが無ければAdapterのserviceが呼び出されます。
public class CoyoteAdapter implements Adapter {
@Override
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
/ リクエストやレスポンスを生成する
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
/ ...中略
}
/ ...中略
try {
/ Parse and set Catalina and configuration specific
/ request parameters
req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
boolean postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
/ Serviceのコンテナを取得してメソッドを呼び出す
connector.getService().getContainer().getPipeline()
.getFirst().invoke(request, response);
/ ...中略
}
/ ...中略
} catch (IOException e) {
/ Ignore
} finally {
req.getRequestProcessor().setWorkerThreadName(null);
/ Recycle the wrapper request and response
if (!comet && !async) {
request.recycle();
response.recycle();
} else {
/ Clear converters so that the minimum amount of memory
/ is used by this processor
request.clearEncoders();
response.clearEncoders();
}
}
}
}
public class Connector extends LifecycleMBeanBase {
public Request createRequest() {
/ 生成するのは org.apache.catalina.connector.Request
Request request = new Request();
request.setConnector(this);
return (request);
}
}
/** org.apache.catalina.connector.Request */
public class Request implements HttpServletRequest {
/** CoyoteパッケージのRequest(HttpServletRequestではない) */
protected org.apache.coyote.Request coyoteRequest;
public void setCoyoteRequest(org.apache.coyote.Request coyoteRequest) {
this.coyoteRequest = coyoteRequest;
inputBuffer.setRequest(coyoteRequest);
}
@Override
public int getServerPort() {
return coyoteRequest.getServerPort();
}
/** セッターがある? */
public void setServerPort(int port) {
coyoteRequest.setServerPort(port);
}
public HttpServletRequest getRequest() {
if (facade == null) {
facade = new RequestFacade(this);/ 自身を渡す
}
return facade;
}
}
public class RequestFacade implements HttpServletRequest {
/** 処理を委譲するRequest */
protected Request request = null;
【安価即出荷】 by :202102211558558165:福音商店 48-Inch Buster 36 Moss 犬用品 Large, Dog Moss by 36 Happy Hounds Bed,-マット、プレート
public RequestFacade(Request request) {
this.request = request;
}
@Override
public int getServerPort() {
return request.getServerPort();/ 委譲
}
}
まず、connector.createRequest()でRequestを生成しています。
そして生成したRequestにsetCoyoteRequestで、引数で渡されたRequestをセットしています。
同じ名前のRequestが2つ登場しましたが、これらはパッケージが違います。
org.apache.coyoteパッケージのRequestはHttpServletRequestの実装クラスではありません。
片やorg.apache.catalina.connectorのRequestは上記ソースの通りHttpServletRequestの実装クラスです。
「ああ、じゃあこのorg.apache.catalina.connector.RequestがServletに渡されるわけね」
そう思うかもしれませんが・・・・・・
違います!
中を見てみると serServerPort() のようなセッターメソッドがあちこちに定義されています。
HttpServletRequestにこのようなメソッドは存在しないので直接は呼び出せませんが、
キャストすれば呼び出すことができます。これはマズイ。よろしくない。
ということでRequestFacadeというラッパークラスが登場しました。
org.apache.catalina.connector.RequestのgetRequest()で返しているやつです。
このクラスもまたHttpServletRequestの実装クラスです。
そしてこのクラスは、HttpServletRequestが定義しているセッター以外を持っていません。
できる限り処理は委譲して済ませています。
これで安心してServletにHttpServletRequestを渡すことができますね。
さて、残りはServiceのコンテナを取得してメソッドを呼び出す処理です。
Serviceが持ってるコンテナとは何か。前回に答えがあります。StandardEngineですね。
そしてgetPipeline() やらgetFirst() やら invoke() といったメソッドを呼び出しています。
次はここら辺のコードを見ていきましょう。
次回: リクエスト処理編その2
- 関連記事
-
- コードリーディング Tomcatのソースを読んでみよう リクエスト処理編その2
- コードリーディング Tomcatのソースを読んでみよう リクエスト処理編その1
- コードリーディング Tomcatのソースを読んでみよう 起動編
スポンサーサイト