これは、ワルシャワ大学で撮影したコースの一部として作成されたプロジェクトです。これは、JavaサーブレットAPIに準拠したJavaサーブレットをサポートするサーブレットコンテナの簡単な実装です。かなり裸の骨であるとしても、ディスパッチ、JSP、非同期サーブレットなどのサーブレットの基本的な機能を使用する簡単なアプリケーションをロードおよび実行できます。
サーブレット容器の他の多くの小さな実装は見つかりませんでしたので、このようなコンテナを作成する方法の例として、これを自由に使用してください。時間制限のために、それはかなり不完全でバグのようなプロジェクトであり、私がサーブレットAPIに忠実であり続けようとした間、私はあちこちでいくつかの自由を取りました。
アイデアは、この容器がTomcatと同様の方法で機能するということです。 ServletContainer :: addrouteを使用してサーブレットクラスを追加できますが、お好みの方法はコンポーネントスキャン機能を使用することです。つまり、アプリケーションがwarName.warファイルにzippedされ、 server/src/main/resources内のdeployディレクトリに配置された場合です。サーバーの起動にロードされ、 localhost:8000/warNameで利用可能になります。
デフォルトでは、サーバーはポート8000で実行されます。 Main機能で変更できます。この関数には、このサーバーを起動および構成する方法の例も含まれています。
このプロジェクトは、Gradleを使用して構築されました。サーバーとデモアプリケーション(Simple Bookデータベース)の2つのサブプロジェクトが含まれています。 Linuxでテストされており、Windows/macosで動作するかどうかはわかりません。
サーバーの使用を実行するには:
./gradlew server:run
サーバーを開始するには、 ServletContainerのインスタンスを作成する必要があります。
ServletContainer(int threads) ServletContainerの新しいインスタンスを作成します。与えられたスレッドの数が与えられたスレッドプールの作成に使用されます。このプールは、HTTPリクエストを処理するために使用されます。
void ServletContainer::start(int port)特定のポートでサーブレットコンテナを起動します。 .WARファイルはこの時点でロードされていないことに注意してください。 ServletContainer:servletScanが呼び出された場合にのみロードされます。
void ServletContainer::stop()容器を優雅に止めます。
void ServletContainer::servletScan() server/src/main/resources内のdeploy Directoryからすべての.WARファイルをロードします。 .jspファイルもロードされ、すぐに。クラスファイルに透過します。 @WebServletで注釈が付けられたクラスのみがロードされます。 1つの警告は、私が使用する.WARファイルには、他のほとんどの.WARファイルと同じであると確信していない特定のディレクトリ構造があることです。デモアプリケーションのwar Gradleタスクを確認して、この構造がどのように見えるかを確認できます。この方法は、start()メソッドの前にせいぜい1回呼び出す必要があります。
void ServletContainer::servletScan(String url) ServletContainer::servletScan()と同じですが、指定されたURLから.WARファイルをロードできます。
void ServletContainer::addRoute(Class<? extends HttpServlet> cls, String path)コンテナにサーブレットを追加します。サーブレットは、与えられたURLのみを処理します(詳細については、 FAQを参照)。
サーバーは、複数のクライアントの同時処理を一度にサポートします。各クライアントは、ThreadPoolから個別のスレッドで処理されます。デフォルトのプールサイズは4で、 Main機能で変更できます。
サーバーは、httpservletから継承する任意のクラスをサポートします。このようなクラスは、 ServletContainer::addRouteを使用して追加できます。
これらのクラスの最も重要な機能が実装されています。 (唯一の) PrintWriter 、セットヘッダー、および応答ステータスを使用してクライアントに書き込むことができます。 HTTPSERVLETREQUESTは、リクエストURL、HTTPメソッド(GET、POST、削除、パッチ)、ボディからのクエリパラメーターとパラメーター(POSTの場合)を抽出できます。データは、バッファを洗い流したり、 HttpServletResponseを閉じた後にクライアントに送信されます。 RequestDispatcherを使用して、JSPサーブレットを含む他のサーブレットにクエリを送信できます。
Asyncサーブレットのサポートがあります。 HttpServletRequest::startAsyncが実行された後、リクエストは非同期モードになります。このモードを使用するには2つの方法があります。任意のコードは、 AsyncContext::completeがある時点で実行される限り実行され、クライアントへの接続が終了します。 AsyncConext::start(Runnable)を使用することもできます。ここでは、ある時点でAsyncContext::complete必要があります。後者は、JavaサーブレットAPIと互換性があります。また、Timeout( AsyncContext::setTimeout )とAsyncListenerもサポートしています。 Asyncサーブレットは、 CompletableFutureを使用して実装されているため、同期クライアントに使用されるものとは別のThreadPoolを使用します。
アプリケーションが.WARにzipし、 deployフォルダーに移動した場合、自動的にロードされます。 @WebServletで注釈が付けられ、 HttpServletから継承されたすべてのクラスがサーブレットコンテナに追加され、 localhost:port/warName/servletUrlで入手できます。そのような各クラスには、値属性の@WebServletアノテーションで指定されたservletUrl正確に1つ持っている必要があります。アプリケーションがJSPを使用すると、.classに自動的にコンパイルされ、ロードされます。多くのアプリケーションをロードできますが、2つのアプリケーションで使用されている同じクラス名がある場合に何が起こるかわかりません。
サーバーは、.jspファイルを.classに送信できます。 JSPのほぼすべての構文のサポートがあります。それは次のとおりです:
<%@ page import/include=... %><% ... %><%! ... %><%= ... %><%-- %>${...} ${}構文は式言語を部分的にサポートします。単純な算術操作を実行でき、フォームinstance.property1.property2の任意の式はrequest.getAttribute("instance").getProperty1().getProperty2()に変換されます。 <% ... %> out.println(...)もクライアントに書き込みます。 JSPはRequestDispatcher::forwardを使用して表示できます。RocalHost localhost:8000/warName/jspFileName.jspで直接入手できます。サンプルJSPアクションはlocalhost:8000/library/jspで入手できます。私は自分自身を解析することを実装したので、奇妙なコードフォーマット/構文がそれを破るかもしれないことに留意してください。
サーブレットコンテナを実証するために、本データベースをシミュレートする簡単なアプリケーションを実装しました。 HTMLフォームを使用して、書籍を追加、削除、更新できます。すべての本はHTMLテーブルでも表示できます。フロントエンドはJSPを使用して完全に開発されています。アプリケーションは、 war Gradleタスクを使用してLibrary.WARにジップされ、展開に移動してサーバーが起動deployとロードされます。
エンドポイント:
ほとんどの機能とデモアプリケーションを確認するための30を超えるテストがあります。 FAQで説明されている問題のため、Intellijからそれらを実行することが好まれます。または、以下を使用して実行できます。
./gradlew test
このプロジェクトは大学のコースの一部であり、Junitを除く第三者図書館を使用することは許可されていませんでした。
すでに使用されているソケットポートには問題があります。私はそれを修正するために降りたことはありませんでしたが、ほとんどの場合、Intellijから実行するときに機能します。
現在、サーブレットが特定のURLを処理する解像度は非常に原始的です。基本的に、ServleturlがurlのservletUrlであるサーブレットをurl的に指定します。多くの場合、最長のプレフィックスを持つものが選択されています。これは、サーブレットが/app/homeに等しいservletUrl持っている場合、それは/app/home/nonexisting/ 、 /app/home/12345などを処理することを意味します。
誰でもこのプロジェクトに貢献できます。私はいくつかの良い最初の問題の問題を作成したので、それらを自由にチェックしてください:)