أساسيات جافا للويب والـ Servlets

إعداد مشروع Java للويب

18 دقيقة الدرس 2 من 13

إعداد مشروع Java للويب

قبل أن تكتب servlet واحدة عليك أن تفهم التخطيط المعياري على القرص الذي يتوقعه كل حاوي Jakarta EE، وكيف تحزم تطبيقك في ملف WAR، وكيف تشغّل ذلك الملف داخل Apache Tomcat. هذا الدرس عملي بالكامل: في نهايته ستمتلك هيكل مشروع يمكنك إعادة استخدامه مع كل servlet ستكتبها في هذا البرنامج التعليمي.

التخطيط المعياري لدليل تطبيق الويب

يتبع تطبيق ويب Jakarta EE عقدًا دقيقًا للأدلة على القرص معرَّفًا بموجب مواصفة Servlet. كل حاوٍ متوافق — Tomcat أو Jetty أو WildFly — يحترم نفس التخطيط:

my-webapp/ ├── src/ │ └── main/ │ ├── java/ <-- فئات servlet والمساعِدة │ │ └── com/example/web/ │ └── webapp/ <-- جذر الويب (يُعيَّن على جذر عنوان URL) │ ├── index.html │ ├── css/ │ ├── js/ │ └── WEB-INF/ <-- محمي، لا يُخدَّم مباشرةً أبدًا │ ├── web.xml <-- واصف النشر (اختياري لكن شائع) │ ├── classes/ <-- ملفات .class المُصرَّفة (تُبنى تلقائيًا) │ └── lib/ <-- ملفات JAR التابعة المحزومة مع التطبيق └── pom.xml <-- ملف بناء Maven

القاعدة الأهم التي يجب حفظها: كل شيء تحت WEB-INF/ غير مرئي للمتصفح. طلب للمسار /WEB-INF/web.xml يُعيد الحاوي استجابة 404 وليس الملف نفسه. هكذا تُحفظ JSPs التي يجب ألا تُطلب مباشرةً، والإعدادات الحساسة، والفئات المُصرَّفة. أما كل ما يُوضع خارج WEB-INF/ — HTML والصور وCSS — فهو متاح للعموم.

WEB-INF ليست حاجزًا أمنيًا — بل هي حاجز رؤية. يفرضه الحاوي، لكن لا يزال يجب ألا تضع كلمات المرور أو المفاتيح في web.xml. استخدم متغيرات البيئة أو مدير الأسرار لبيانات الاعتماد، تمامًا كما تفعل في أي تطبيق آخر على جانب الخادم.

واصف النشر: web.xml

ملف WEB-INF/web.xml هو واصف النشر. إنه ملف XML يخبر الحاوي بمحتوى تطبيقك: أي servlets موجودة، وأي أنماط URL تُعيَّن عليها، ومهلة الجلسة، وملفات الترحيب، وصفحات الأخطاء، وقيود الأمان. منذ Servlet 3.0، حلّت التعليقات التوضيحية (annotations) محل معظم محتواه في الحالات البسيطة، لكن web.xml لا يزال مهمًا للإعدادات التي لا تستطيع التعليقات التوضيحية التعبير عنها وللبيئات التي تُعطَّل فيها مسح التعليقات.

يبدو الحد الأدنى من web.xml الصالح لـ Jakarta EE 10 (Servlet 6.0) هكذا:

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" version="6.0"> <display-name>My First Web App</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <session-config> <session-timeout>30</session-timeout> <!-- بالدقائق --> </session-config> </web-app>

لا تُعلَن أي تعيينات للـ servlet هنا لأن الدرس التالي يقدّم تعليق @WebServlet التوضيحي، وهو الطريقة الحديثة لربط عنوان URL بفئة servlet دون لمس web.xml.

احتفظ بـ web.xml حتى عند استخدام التعليقات التوضيحية. يتيح لك web.xml الفارغ لكن الصالح تجاوز الإعدادات المستندة إلى التعليقات التوضيحية لكل بيئة (مثل مهلة جلسة مختلفة في الإنتاج) دون إعادة الترجمة. كما يُعدّ توثيقًا صريحًا لعقد التطبيق.

إعداد بناء Maven (pom.xml)

نوع التعبئة war في Maven يعرف تخطيط تطبيق الويب وينتج WAR بهيكل صحيح تلقائيًا. إليك حدًّا أدنى من pom.xml يستهدف Jakarta EE 10 / Tomcat 10.1:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>my-webapp</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- Servlet API — يوفرها Tomcat وقت التشغيل، لا تُحزَّم --> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.0.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>my-webapp</finalName> </build> </project>

نطاق provided على تبعية Servlet API بالغ الأهمية. يعني أن Maven يُصرِّف بمقابل الـ API لكنه لا يحزمها داخل ملف WAR. فـ Tomcat يشحن الـ API مسبقًا؛ وإن حزمتها أنت أيضًا ستتعرض لتعارضات في محمّل الفئات وأخطاء ClassCastException يصعب تشخيصها وقت التشغيل.

Tomcat 10 وما فوق يستخدم jakarta.* وليس javax.*. إن كانت استيراداتك لا تزال تقرأ import javax.servlet.* فأنت تستهدف فضاء أسماء Java EE القديم وكودك لن يُنشر على Tomcat 10 أو أي حاوي Jakarta EE 10. حدث إعادة التسمية مع مواصفة Jakarta EE 9 عام 2020. تحقق دائمًا من إصدار Tomcat مقابل إصدار servlet-api الذي تعتمد عليه.

بناء ملف WAR

ملف WAR (Web Application Archive) ما هو إلا ملف ZIP بامتداد .war يحتوي على التخطيط الدليلي الموصوف أعلاه. لبنائه:

mvn clean package

يُصرِّف Maven مصادر Java الخاصة بك وينسخها إلى WEB-INF/classes/، وينسخ ملفات JAR التابعة (تلك التي لم تُعطَ نطاق provided) إلى WEB-INF/lib/، ويشمل كل شيء تحت src/main/webapp/، وينتج target/my-webapp.war. يمكنك التحقق من المحتويات:

jar tf target/my-webapp.war

المخرجات المتوقعة تشمل إدخالات مثل WEB-INF/web.xml وWEB-INF/classes/com/example/web/HelloServlet.class وأي أصول ثابتة.

النشر على Tomcat

يأتي Tomcat 10.1 مزوَّدًا بدليل webapps/. ضع ملف WAR هناك ويُنشره Tomcat تلقائيًا:

# انسخ ملف WAR إلى دليل webapps في Tomcat cp target/my-webapp.war $CATALINA_HOME/webapps/ # ابدأ تشغيل Tomcat (إن لم يكن يعمل بالفعل) $CATALINA_HOME/bin/startup.sh # Linux / macOS %CATALINA_HOME%\bin\startup.bat # Windows

يستخرج Tomcat ملف WAR في دليل مطابق (مثل webapps/my-webapp/) ويصبح التطبيق متاحًا على http://localhost:8080/my-webapp/. مسار السياق (/my-webapp) مشتقّ من اسم ملف WAR. للنشر على المسار الجذري، أعد تسمية ملف WAR إلى ROOT.war.

للتطوير، يُتيح لك مكوّن Maven الإضافي لـ Tomcat النشر الفوري دون نسخ يدوي:

<plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat10-maven-plugin</artifactId> <version>2.0-beta-1</version> <configuration> <port>8080</port> <path>/my-webapp</path> </configuration> </plugin>
mvn tomcat10:run

هذا يُشغّل نسخة Tomcat مدمجة، ينشر تطبيقك ويبقيه قيد التشغيل. تحصل على دورة تحرير-تصريف-تحديث سريعة دون لمس تثبيت Tomcat مستقل.

التحقق من السجلات

يكتب Tomcat مخرجات بدء التشغيل والنشر في $CATALINA_HOME/logs/catalina.out. إذا فشل نشر WAR — بسبب فضاء أسماء خاطئ في web.xml، أو تعيين servlet معطوب، أو تعارض في محمّل الفئات — ستجد رسالة الخطأ هنا. قراءة هذا السجل هي الخطوة الأولى متى ما ساء أمر النشر.

الخلاصة

أنت الآن تمتلك نموذجًا ذهنيًا قابلًا لإعادة الاستخدام: المحتوى الثابت يقع تحت جذر webapp؛ كل شيء حساس يقع تحت WEB-INF/؛ يصف web.xml عقد التطبيق؛ نطاق provided للـ Servlet API يتفادى تعارضات محمّل الفئات؛ يُنتج mvn clean package ملف WAR؛ ووضعه في webapps/ ينشره. في الدرس التالي ستكتب أول servlet حقيقية وتُعيّنها على عنوان URL.