SAStrutsを読む:サーブレットコンテナからStrutsまで
目的
リクエストがどこを通ってActionまで到達するのか把握
予備知識
- サーブレット
- JavaによるWebアプリケーション実装の仕様。インタフェース定義とかファイル構成とか
- サーブレットコンテナ
- サーブレットを動かすためのソフトウェアの仕様。
- Tomcat
- サーブレットコンテナ実装のひとつ。単体でwebサーバとして機能するしApacheとの連携も可。Tomcat6はServlet2.5対応。
- J2EE
- Javaのライブラリ規格、J2SEの拡張。サーブレットとかその他エンタープライジーなものが定義されてる。
- Servlet 2.5仕様(If you intend to build an implementation of... のあたりからダウンロード可能)
大まかな処理の流れ
リクエストをコンテナが処理→フィルタ(ルーティング等)→StrutsのActionServlet→SAStrutsのRequestProcessor→SAStrutsのActionWrapper→アクションクラス
コンテナからServletまで
web.xml
WEB-INF/web.xmlはデプロイメント記述子(Deployment descriptor)と呼ばれる設定ファイルで、Servlet仕様で決まっている(SRV.13あたり)。
サーブレットとURLをマッピングしたりフィルタをつけたりできる。
フィルタ
処理がサーブレットへ到達する経路にフィルタをかませてなんかすることができる。URLのかきかえとか。
処理の概念はこのへんがわかりやすい。
フィルタの実装はこんなぐあい:
class MyFilter extends Filter public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { Servletにリクエストが渡る前にやりたい処理(); // リクエストの書き換えとか chain.doFilter(request,response); // 次のフィルタ、あるいはサーブレット本体を起動する Servletがリクエストを処理したあとにやりたい処理(); // レスポンスの書き換えとか } }
このパターンS2AOPでも見たな。なんか名前ついてそうだけど。
web.xmlのfilter要素でフィルタを定義、filter-mappingでフィルタを適用する場所を決める。
デフォルトでは以下のフィルタが定義されている。
- encodingfilter
- リクエストのエンコーディングを設定する
- s2filter
- コメントに「S2Container用のフィルタです」としか書いてない……
- hotdeployfilter
- まあホットデプロイ用のなんかでしょうな
- routingfilter
- 渡されたURLを内部URLにかきかえる
- requestDumpFilter
- リクエストの内容をサーブレットが処理する前後にダンプする
重要なのはroutingfilter、s2filterあたりか。
マッピングは以下。
<filter-mapping> <filter-name>s2filter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> </filter-mapping> <filter-mapping> <filter-name>routingfilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping>
全URLに対して適用される。dispatcherの設定については、REQUESTが通常時、FORWARD/INCLUDEがそれぞれフォワード/インクルード時に呼ばれる。
ということだとおもう。
この場合ルーティングはリクエスト時のみ、S2コンテナのフィルタは全部のケースで適用される。
s2filter
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { S2Container container = SingletonS2ContainerFactory.getContainer(); ExternalContext externalContext = container.getExternalContext(); if (externalContext == null) { throw new EmptyRuntimeException("externalContext"); } final Object originalRequest = externalContext.getRequest(); final Object originalResponse = externalContext.getResponse(); try { externalContext.setRequest(request); externalContext.setResponse(response); chain.doFilter(request, response); } finally { externalContext.setRequest(originalRequest); externalContext.setResponse(originalResponse); invalidateSession(request); } }
外部コンテキストに設定されてるrequest,responseを渡されたので挿げ替えたり元に戻したり。外部コンテキストがなんなのかわからないので意味がわからないですね。
サーブレット本体:ここからStruts
servlet要素でサーブレットを定義、servlet-mapping要素でURLパターンとサーブレットをマッピングする。
デフォルトで生成される設定では以下のようになっている。
<servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <!-- init-param 略 --> <load-on-startup>1</load-on-startup> </servlet> <servlet> <servlet-name>s2container</servlet-name> <servlet-class>org.seasar.framework.container.servlet.S2ContainerServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>s2container</servlet-name> <url-pattern>/s2container</url-pattern> </servlet-mapping>
*.doというURLに対してlorg.apache.struts.action.ActionServletというサーブレットが呼ばれる。
/s2containerに対してS2ContainerServletが呼ばれる。
routingfilterでフォワードされる先はアクションパス.doなのでStrutsのActionServletが呼ばれる。
サーブレットに来たGETリクエストはdoGetが処理する。ActionServletにおいてはprocess()に委譲してるだけ。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { process(request, response); } protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { ModuleUtils.getInstance().selectModule(request, getServletContext()); ModuleConfig config = getModuleConfig(request); RequestProcessor processor = getProcessorForModule(config); if (processor == null) { processor = getRequestProcessor(config); } processor.process(request, response); }
lRequestProcessorを取得して丸投げ。
さてこのRequestProcessorがどこで決まるかというと、struts-config.xmlのcontroller要素。
<controller maxFileSize="1024K" bufferSize="1024" processorClass="org.seasar.struts.action.S2RequestProcessor" multipartClass="org.seasar.struts.upload.S2MultipartRequestHandler"/>
processorClass,multipartClassがSAStrutsのものになっている。
multipartClassはマルチパートなリクエストを処理するためのもので、通常はS2RequestProcessorが呼ばれることがわかる。
S2RequestProcessor: ここからSAStruts
これがメインの処理かな。
このクラスはlRequestProcessorを継承している。
public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { request = processMultipart(request); String path = processPath(request, response); if (path == null) { return; } processLocale(request, response); processContent(request, response); processNoCache(request, response); if (!processPreprocess(request, response)) { return; } processCachedMessages(request, response);
このへんのprocessなんとかは親クラスのもの。前処理を行ったあとでアクションへのディスパッチを開始する。
ActionMapping mapping = processMapping(request, response, path); if (mapping == null) { return; }
ActionMappingはStrutsのクラス。なんかdeprecatedとか書いてあるけど……。
アクションに処理させるためのクラスだと思う。
processMapping(path)はmoduleConfig.findActionConfig(path)でアクションマッピングを取得している。どこから得てるのかはとりあえず保留。
ActionForm form = processActionForm(request, response, mapping);
ActionFormもstrutsのクラス。まあActionFormですな。
その後も前準備がしばらく続くけど略。
Action action = processActionCreate(request, response, mapping); if (action == null) { return; } ActionForward forward = processActionPerform(request, response, action, form, mapping); processForwardConfig(request, response, forward); }
えーと……(未完)
まとめ
Strutsとがっちりかみあってるあたりからわけがわからなくなった
つづき: SAStrutsを読む:Struts以降 - <s>gnarl,</s>技術メモ”’<marquee><textarea>¥
関連:S2AOPを読む - <s>gnarl,</s>技術メモ”’<marquee><textarea>¥