هذا مشروع تم إنشاؤه كجزء من دورة درست في جامعة وارسو. إنه تطبيق بسيط لحاوية Servlet التي تدعم Java Servlets متوافقة مع Java Servlet API. على الرغم من أنه Barebone تمامًا ، إلا أنه يمكنه تحميل وتشغيل تطبيقات بسيطة تستخدم وظائف Servlets الأساسية بما في ذلك الإرسال و JSP و Servlets Async.
لم أجد العديد من التطبيقات الصغيرة الأخرى لحاويات Servlet ، لذا لا تتردد في استخدام هذا كمثال على كيفية إنشاء حاوية مثل هذا. ضع في اعتبارك أنه نظرًا لقيود الوقت ، فهو مشروع غير كامل وعربات التي تجرها الدواب ، وبينما حاولت أن أظل مخلصًا ل Servlet API ، أخذت بعض الحريات هنا وهناك.
والفكرة هي أن هذه الحاوية تعمل بطريقة مماثلة مثل tomcat. يمكنك إضافة فئات Servlet باستخدام ServletContainer :: addRoute ولكن الطريقة المفضلة هي استخدام وظيفة مسح المكونات. وذلك إذا تم ضبط التطبيق في ملف warName.war ووضعه في دليل deploy داخل server/src/main/resources ، سيتم تحميله على بدء الخادم ومتوفر تحت localhost:8000/warName .
بشكل افتراضي ، يعمل الخادم على المنفذ 8000. يمكن تغييره في الوظيفة Main . تحتوي هذه الوظيفة أيضًا على مثال على كيفية بدء هذا الخادم وتكوينه.
تم بناء المشروع باستخدام Gradle. أنه يحتوي على اثنين من المشاريع الفرعية: الخادم والتطبيق التجريبي (قاعدة بيانات كتب بسيطة). تم اختباره تحت Linux ولا أعرف ما إذا كان يعمل حتى تحت Windows/MacOS.
لتشغيل استخدام الخادم:
./gradlew server:run
يتطلب بدء تشغيل الخادم منا إنشاء مثيل ServletContainer .
ServletContainer(int threads) يخلق مثيلًا جديدًا لـ ServletContainer . مع عدد معين من المواضيع المستخدمة لإنشاء ThreadPool. سيتم استخدام هذا التجمع للتعامل مع طلبات HTTP.
void ServletContainer::start(int port) تبدأ حاوية servlet على منفذ معين. ضع في اعتبارك أنه لم يتم تحميل ملفات war في هذه المرحلة. يتم تحميلها فقط عند ServletContainer:servletScan يسمى.
void ServletContainer::stop()توقف برشاقة الحاوية.
void ServletContainer::servletScan() تقوم بتحميل جميع ملفات .WAR من deploy الدليل داخل server/src/main/resources . سيتم تحميل ملفات .jsp أيضًا وتنقلها على الفور إلى ملفات .class. سيتم تحميل الفصول المشروحة مع @WebServlet فقط. أحد التحذيرات هو أن ملفات .war التي أستخدمها لها بنية دليل محددة لست متأكدًا من أنها هي نفسها كما في معظم ملفات .WAR الأخرى. يمكنك التحقق من مهمة war Gradle في التطبيق التجريبي لمعرفة شكل هذا الهيكل. يجب استدعاء هذه الطريقة مرة واحدة على الأكثر قبل بدء ().
void ServletContainer::servletScan(String url) مثل ServletContainer::servletScan() ولكن يمكن تحميل ملفات .war من عنوان URL المعطى.
void ServletContainer::addRoute(Class<? extends HttpServlet> cls, String path)يضيف servlet إلى الحاوية. سوف Servlet التعامل مع عنوان URL المعطى فقط (انظر الأسئلة الشائعة لمزيد من التفاصيل).
يدعم الخادم التعامل المتزامن مع العديد من العملاء في وقت واحد. سيتم التعامل مع كل عميل بواسطة مؤشر ترابط منفصل عن ThreadPool. حجم التجمع الافتراضي هو 4 ويمكن تغييره في الوظيفة Main .
سيدعم الخادم أي فئة ترث من httpservlet. يمكن إضافة مثل هذا الفصل باستخدام ServletContainer::addRoute .
يتم تنفيذ أهم وظائف هذه الفئات. يمكنك الكتابة إلى العميل باستخدام (فقط) PrintWriter ، وضبط الرؤوس وحالة الاستجابة. يمكن لـ HttPservletRequest استخراج عنوان URL للطلب ، وطريقة HTTP (Get ، Post ، Delete ، Patch) ، معلمات الاستعلام والمعلمات من الجسم (في حالة Post). يتم إرسال البيانات إلى العميل بعد مسح المخزن المؤقت أو إغلاق HttpServletResponse . باستخدام RequestDispatcher ، يمكنك إرسال استفسارات إلى servlets الأخرى بما في ذلك JSP Servlets.
هناك دعم ل servlets async. بعد تنفيذ HttpServletRequest::startAsync ، يذهب الطلب إلى الوضع غير المتزامن. هناك طريقتان لاستخدام هذا الوضع. سيتم تشغيل أي رمز طالما يتم تنفيذ AsyncContext::complete في مرحلة ما ، والذي ينتهي الاتصال بالعميل. يمكنك أيضًا استخدام AsyncConext::start(Runnable) ، هنا تحتاج أيضًا إلى تنفيذ AsyncContext::complete في مرحلة ما. هذا الأخير متوافق مع Java Servlet API. كما يدعم المهلة ( AsyncContext::setTimeout ) و AsyncListener . يتم تنفيذ Servlets Async باستخدام CompletableFuture حتى يستخدموا Threadpool منفصلة عن تلك المستخدمة للعملاء المتزامنين.
إذا تم ضرب التطبيق إلى .war ونقله إلى مجلد deploy ، فسيتم تحميله تلقائيًا. ستتم إضافة جميع الفصول المشروحة بـ @WebServlet وراثة من HttpServlet إلى حاوية Servlet وستكون متاحة في localhost:port/warName/servletUrl . يجب أن يكون لكل فئة من هذا القبيل بالضبط servletUrl محدد في التعليق التوضيحي @WebServlet في سمة القيمة. إذا كان التطبيق يستخدم JSP ، فسيتم تجميعه تلقائيًا في .class وتحميله. ومع ذلك ، يمكن تحميل العديد من التطبيقات ، ومع ذلك ، لا أعرف ما يحدث عندما يكون هناك نفس أسماء الفئات المستخدمة في تطبيقين.
يمكن للخادم transpile .jsp ملفات إلى .class. هناك دعم لجميع بناء الجملة تقريبًا في JSP. ويشمل ذلك:
<%@ page import/include=... %><% ... %><%! ... %><%= ... %><%-- %>${...} بناء الجملة ${} يدعم لغة التعبير جزئيًا. يمكن تنفيذ عمليات الحساب البسيطة ، وسيتم تحويل أي تعبير عن instance.property1.property2 النموذج request.getAttribute("instance").getProperty1().getProperty2() في <% ... %> هناك أيضًا out.println(...) الذي يكتب مباشرة إلى العميل. يمكن عرض JSP باستخدام RequestDispatcher::forward أو متاحًا مباشرة في localhost:8000/warName/jspFileName.jsp . يتوفر إجراء نموذج JSP في localhost:8000/library/jsp . ضع في اعتبارك أنني قمت بتطبيق تحليل نفسي حتى تنسيق/بناء الجملة الغريب قد يكسره.
لإظهار حاوية Servlet ، قمت بتطبيق تطبيق بسيط يحاكي قاعدة بيانات الكتب. يمكن إضافة الكتب وإزالتها وتحديثها باستخدام نماذج HTML. يمكن أيضًا مشاهدة جميع الكتب في جدول HTML. تم تطوير الواجهة الأمامية بالكامل باستخدام JSP. يتم ضرب التطبيق إلى المكتبة. war باستخدام مهمة war Gradle ، تم نقلها إلى deploy وتحميلها على الخادم عند بدء تشغيله.
نقاط النهاية:
هناك أكثر من 30 اختبارًا للتحقق من معظم الوظائف والتطبيق التجريبي. من المفضل أن يديرهم من Intellij بسبب المشاكل الموضحة في الأسئلة الشائعة . بدلاً من ذلك ، يمكن تشغيلها باستخدام:
./gradlew test
كان هذا المشروع جزءًا من دورة جامعية ولم يُسمح لي باستخدام أي مكتبات طرف ثالثة باستثناء Junit.
هناك مشاكل مع منافذ المقبس قيد الاستخدام بالفعل. لم أنزل أبدًا لإصلاحه ولكن في معظم الأوقات تعمل عند الركض من Intellij.
في الوقت الحالي ، يجب أن يتعامل دقة Servlet مع عنوان URL المحدد إلى حد كبير. يبحث خادم url المعطى بشكل أساسي عن servlet الذي يعد servletUrl بادئة عنوان url . إذا كان هناك العديد من الأشخاص الذين لديهم أطول بادئة. هذا يعني أنه إذا كان servlet يحتوي على servletUrl يساوي /app/home فسيتعامل أيضًا مع /app/home/nonexisting/ ، /app/home/12345 إلخ.
أي شخص مرحب به للمساهمة في هذا المشروع. لقد قمت بإنشاء بعض المشكلات الجيدة في القضية ، لذلك لا تتردد في التحقق منها :)