================================================================================
 employee-form  -  Topic 3: JSP and SERVLET  (all six sessions)
================================================================================

Lab project for "100 Hours Java Backend Developer", STAGE 2, Topic 3 - JSP and
SERVLET (ICT Academy / Vidya Sagar Institute).

It covers all six sessions of the deck in ONE Dynamic Web Project, so each
session builds on the previous one instead of repeating the setup:

    Session 1  Forms and Session management
    Session 2  Database Connectivity            (JSTL <sql:*> tags)
    Session 3  CRUD operations in database      (user management)
    Session 4  Listing Data on Web Page
    Session 5  Adding JSTL support
    Session 6  Uploading Files with Servlets and JSP

Session 1 shows the full form round-trip:

    JSP form  ->  Servlet (doPost)  ->  JavaBean  ->  DAO (JDBC)  ->  MySQL

Session 3 extends the same shape to all four CRUD operations. Everything else
builds on those two paths.

Teaching notes (the annotated, PPT-by-PPT version) live separately in:

    D:\sandeep\jsp-topic3\
        "Session 1 - Forms and Session management"\
        forms_and_session_management.jsp

This project is the runnable form of that material.


--------------------------------------------------------------------------------
1. APPLICATION URL
--------------------------------------------------------------------------------

    Index of every link (START HERE):

        http://localhost:9090/employee-form/

    Session 1 entry point:

        http://localhost:9090/employee-form/employeeregister.jsp

    LOGIN (the application is guarded by an authentication filter):

        http://localhost:9090/employee-form/login.jsp

        username: admin      password: admin

    Every URL above redirects to the login page until you have signed in.
    Details in section 8.0.

    Requires the Eclipse-managed Tomcat to be running - see section 7.


--------------------------------------------------------------------------------
2. TECHNOLOGIES USED
--------------------------------------------------------------------------------

    Project type     Dynamic Web Project (WTP)
    Java             JDK 1.8 (source / target 1.8)
    Web standard     Jakarta EE / Java EE Web 4.0 (jst.web facet 4.0)
    Server runtime   Apache Tomcat 9.0.x
    Servlet API      javax.servlet (servlet-api.jar shipped with Tomcat 9)
    IDE              Eclipse IDE for Enterprise Java and Web Developers
    Database         MySQL 8 - three schemas: employees, TEST, demo
    Driver           mysql-connector-java-8.0.13.jar
    JSTL             jstl-1.2.jar + standard-1.1.2.jar  (Sessions 2 and 5)
    File upload      commons-fileupload-1.4.jar + commons-io-2.11.0.jar
                     (Session 6, alternative to the Servlet Part API)
    Build tool       None (Eclipse only - no Maven / Gradle in this project)

    Configuration    src/main/webapp/WEB-INF/db.properties
                     One file holds the driver, URL, user and password for
                     every session. See section 4.


--------------------------------------------------------------------------------
3. PREREQUISITES
--------------------------------------------------------------------------------

    a) JDK 8 installed. Register it under
       Window > Preferences > Java > Installed JREs > Add... and set it as the
       Workspace default.

    b) Eclipse IDE for Enterprise Java and Web Developers (the package that
       ships "Eclipse Web Developer Tools"). The JST facets and the Servers
       view come from that package.

    c) Apache Tomcat 9.0.x extracted somewhere. This machine uses:
       D:\sandeep\javaBasics-workspace\apache-tomcat-9.0.122

    d) MySQL 8 running, with the three schemas created (section 4).

    e) This project folder imported as an existing project (section 7, STEP 1).
       Do NOT create a new project - the .project / .classpath / .settings
       files already carry the correct configuration.


--------------------------------------------------------------------------------
4. DATABASE SETUP
--------------------------------------------------------------------------------

EASIEST - run the included script. It creates every schema and table this
project touches, seeds the demo rows, and prints a row-count check at the end.

    File: db-setup.sql  (project root)

    MySQL Workbench :  open the file, press the lightning bolt
    command line    :  mysql -u root -p < db-setup.sql

Safe to re-run - everything is IF NOT EXISTS / INSERT IGNORE, so it never
duplicates a seed row and never deletes data you entered yourself.

What it creates:

    employees.employee      Session 1  registration form (EmployeeDao)
    employees.emp_salary    Session 4  listing demo (7 rows, total 280000)
    TEST.Employees          Session 2  JSTL <sql:*> tags (4 rows, ids 100-103)
    demo.users              Session 3  CRUD demo (3 rows, ids 1-3)


4.1  CREDENTIALS - one place, not five

    File: src/main/webapp/WEB-INF/db.properties

        jdbc.driver = com.mysql.cj.jdbc.Driver
        jdbc.user   = root
        jdbc.pass   = system      <-- change this to YOUR MySQL password

        jdbc.url        = jdbc:mysql://localhost:3306/employees?...
        jdbc.url.test   = jdbc:mysql://localhost:3306/TEST?...
        jdbc.url.demo   = jdbc:mysql://localhost:3306/demo?...

    com.vs.employee.dao.DbConfig reads this file once and exposes it as
    driver(), user(), password() and url(schema). Every DAO and every JSTL
    SQL page calls DbConfig, so changing jdbc.pass fixes all six sessions at
    once.

    Wrong password = "Access denied for user 'root'@'localhost'". That is the
    most common first-run failure and it is NOT a code bug.

    The file sits under WEB-INF/ so it is NOT downloadable over HTTP. Putting
    db.properties in the webapp root would let anyone read your password by
    requesting /employee-form/db.properties.


4.2  Why the URL carries these two parameters

    serverTimezone=UTC           without it the connector throws
                                 "The server time zone value ... is
                                  unrecognized"
    allowPublicKeyRetrieval=true MySQL 8 defaults to caching_sha2_password;
                                 without this the connector throws
                                 "Public Key Retrieval is not allowed"

    Also useSSL=false - the local MySQL has no TLS configured, and connector
    8.x otherwise warns about connecting in cleartext.


4.3  Confirm the data landed

    SELECT 'employees.employee' t, COUNT(*) c FROM employees.employee
    UNION ALL SELECT 'employees.emp_salary', COUNT(*) FROM employees.emp_salary
    UNION ALL SELECT 'TEST.Employees',       COUNT(*) FROM TEST.Employees
    UNION ALL SELECT 'demo.users',           COUNT(*) FROM demo.users;

    Expected on a fresh run:  0 / 7 / 4 / 3
    (employees.employee starts empty on purpose - see section 10, item (a).)


--------------------------------------------------------------------------------
5. PROJECT STRUCTURE
--------------------------------------------------------------------------------

    employee-form/
     |
     +-- readme.txt                     This file
     +-- db-setup.sql                   Creates and seeds all four tables
     +-- .classpath                     JRE + servlet-api.jar + web containers
     +-- .project                       Eclipse project descriptor
     +-- build/classes/                 Compiled .class output folder
     |
     +-- .settings/                     Facets, JDT compiler, JSDT settings
     |
     +-- src/main/java/com/vs/employee/
     |       Employee.java             JavaBean: id, firstName, lastName,
     |                                 username, password, address, contact
     |       model/User.java           Session 3 model bean
     |       dao/DbConfig.java         Reads WEB-INF/db.properties
     |       dao/EmployeeDao.java      Session 1  INSERT + full CRUD
     |       dao/UserDao.java          Session 3  users CRUD
     |       dao/EmployeeSalaryDao.java Session 4 emp_salary reader
     |       web/AuthenticationFilter.java  @WebFilter("/*") - every request
     |                                   needs a logged-in session, otherwise
     |                                   redirect to /login.jsp
     |       web/LoginServlet.java         @WebServlet("/login") - admin/admin
     |       web/EmployeeServlet.java      @WebServlet("/register")
     |       web/SessionDemoServlet.java   @WebServlet("/sessionDemo")
     |       web/LogoutServlet.java        @WebServlet("/logout")
     |       web/UserServlet.java          @WebServlet("/users")   Session 3
     |       web/DisplayEmployeeServlet.java  @WebServlet("/display")
     |       web/DisplayJspServlet.java       @WebServlet("/displayJsp")
     |       web/MultiPartServlet.java     @WebServlet("/multiPartServlet")
     |       web/CommonsFileUploadServlet.java @WebServlet("/commonsUploadServlet")
     |       web/UploadedFilesServlet.java @WebServlet("/uploadedFiles")
     |
     +-- src/main/webapp/
     |       index.jsp                  Index of all links (START HERE)
     |       login.jsp                  Login form (admin / admin) - the ONLY
     |                                   page reachable without a session
     |       employeeregister.jsp       Session 1 form + dummy data button
     |       employeedetails.jsp        Success page (after the redirect)
     |       rewriteurl.jsp             c:url URL rewriting + hidden field
     |       session2/index.jsp         Session 2 setup + links
     |       session2/jstl_sql_select.jsp
     |       session2/jstl_sql_insert.jsp
     |       session2/jstl_sql_update.jsp
     |       session2/jstl_sql_delete.jsp
     |       session3/index.jsp         Session 3 setup + links
     |       session3/user-list.jsp     READ / DELETE screen
     |       session3/user-form.jsp     CREATE / UPDATE form
     |       session4/index.jsp         Session 4 setup + links
     |       session4/display-jsp.jsp   Table rendered by JSTL
     |       session5/index.jsp         taglib URI reference
     |       session5/jstl-core.jsp     11 core tags demonstrated
     |       session5/jstl-bean.jsp     jsp:useBean vs EL, lists, maps
     |       session5/jstl-formatting.jsp  fmt + fn tags
     |       session6/index.jsp         Part API vs Commons FileUpload
     |       session6/multiPartForm.jsp
     |       session6/commonsFileUploadForm.jsp
     |       WEB-INF/db.properties      JDBC credentials (section 4.1)
     |       WEB-INF/web.xml            Minimal 4.0 descriptor. All servlets
     |                                   are declared with @WebServlet, so no
     |                                   <servlet> entries are needed.
     |       WEB-INF/classes/messages.properties   fmt: message bundle
     |       WEB-INF/lib/
     |           mysql-connector-java-8.0.13.jar
     |           jstl-1.2.jar
     |           standard-1.1.2.jar
     |           commons-fileupload-1.4.jar
     |           commons-io-2.11.0.jar
     |       META-INF/MANIFEST.MF
     |
     +-- uploads/                       Created at runtime by Session 6.
                                        Lives inside the deployed webapp, so
                                        Tomcat will SERVE files placed here -
                                        see section 10, item (m).

    Context path when deployed by Eclipse: /employee-form
    (it comes from the project name, so keep the project named
     "employee-form")


--------------------------------------------------------------------------------
6. THE SIX SESSIONS AT A GLANCE
--------------------------------------------------------------------------------

SESSION 1 - Forms and Session management
    JSP form posts to /register, EmployeeServlet maps the six parameters onto
    the Employee bean, EmployeeDao writes the row with a PreparedStatement.
    Then the HttpSession API: isNew, getId, getAttribute, invalidate.
    Schema: employees.employee

SESSION 2 - Database Connectivity
    Four JSP pages using the JSTL SQL tag library - no Java code at all.
    sql:setDataSource, sql:query, sql:update, sql:param, c:forEach.
    Schema: TEST.Employees

SESSION 3 - CRUD operations in database
    Create / Read / Update / Delete on demo.users through one controller
    servlet with an "action" parameter, plus two JSPs. Demonstrates
    POST/Redirect/GET so a refresh cannot re-submit.
    Schema: demo.users

SESSION 4 - Listing Data on Web Page
    The same query twice, on purpose: DisplayEmployeeServlet builds the HTML
    with out.println() (the PPT's approach), DisplayJspServlet puts the rows
    in a request attribute and lets display-jsp.jsp render them. Compare them.
    Schema: employees.emp_salary

SESSION 5 - Adding JSTL support
    The five tag libraries with the correct URIs, and 20+ tags demonstrated:
    out, set, if, choose, forEach, forTokens, url, param, catch, remove, the
    fmt number/date tags, and 14 fn string functions. No database needed.

SESSION 6 - Uploading Files with Servlets and JSP
    Two implementations of the same form: the Servlet Part API (no extra jars)
    and Apache Commons FileUpload. Both write to uploads/, and both refuse to
    overwrite an existing file.
    No schema.


--------------------------------------------------------------------------------
7. SETUP AND RUNNING IN ECLIPSE  (step by step)
--------------------------------------------------------------------------------

STEP 1 - Import the project
    1. Start Eclipse.
    2. File > Import... > General > Existing Projects into Workspace > Next.
    3. Select root directory:
           D:\sandeep\javaBasics-workspace\employee-form
    4. Tick "employee-form" in the Projects list > Finish.

    If Eclipse marks the project red, work through STEP 2 and STEP 3 - those
    are environment path issues, not a broken project.


STEP 2 - Confirm the JRE is Java 8
    1. Right-click "employee-form" > Properties > Java Build Path > Libraries.
    2. Select the JRE System Library. It should read "JavaSE-1.8".
       If it shows JavaSE-20 or has a red cross, click "Edit..." and point it
       at your JDK 1.8, or at the Workspace default JRE.

    Why this matters: the project was originally generated with the Java 20
    facet while Tomcat runs on JDK 8. Class files compiled above major
    version 52 are silently not loaded by the container, and every servlet
    URL returns 404 with NO error in the log. The facet and the compiler
    settings are already corrected in this project.


STEP 3 - Fix the servlet-api.jar path  (IMPORTANT on a new machine)
    .classpath stores an ABSOLUTE path to Tomcat's jar:

        <classpathentry kind="lib"
            path="D:/sandeep/javaBasics-workspace/apache-tomcat-9.0.122/lib/servlet-api.jar"/>

    If Tomcat lives elsewhere the entry shows as missing and the servlets
    will not compile.

    Fix it either way:

    Option A (recommended) - let Eclipse manage it:
        Project > Properties > Java Build Path > Libraries
        > select the broken servlet-api.jar > Remove
        > Add Library... > "Server Runtime" > Next
        > pick "Apache Tomcat v9.0" > Finish > Apply and Close.

    Option B - browse to your own copy:
        Add JARs... > <your-tomcat>\lib\servlet-api.jar > Apply and Close.


STEP 4 - Verify the facets
    Right-click "employee-form" > Properties > Facets. You should see:

        Dynamic Web Module          4.0
        Java                        1.8
        JavaScript / wst.jsdt.web   1.0


STEP 5 - Configure the Tomcat server
    1. Window > Show View > Servers.
    2. Create a server if none is listed:
       "Apache Tomcat" > "Tomcat v9.0 Server" > Next.
    3. Tomcat installation directory -> your Tomcat 9 folder (the one holding
       bin\ and conf\) > Next > Finish.
    4. Open "Server Locations". The Application name / path must read:

           /employee-form

       This is what makes
       http://localhost:9090/employee-form/... resolve.
    5. Open the "Ports" tab and set HTTP to 9090. Port 9090 is what every URL
       in this file assumes.


STEP 6 - Add the project to the server
    Servers view > right-click the Tomcat server > "Add and Remove Projects..."
    > select "employee-form" > Add > Finish.


STEP 7 - Run
    1. Right-click the Tomcat server > "Start". Wait for "Server startup in".
    2. Right-click the "employee-form" project > "Run As" > "Run on Server".
    3. Open:

           http://localhost:9090/employee-form/

    The index page links to all six sessions.


--------------------------------------------------------------------------------
8. HOW TO USE THE APPLICATION  (by session)
--------------------------------------------------------------------------------

8.0  LOGIN FIRST  (every page is behind AuthenticationFilter)

    1. Open    http://localhost:9090/employee-form/
       AuthenticationFilter sees there is no session attribute
       "loggedInUser" and redirects you to
       http://localhost:9090/employee-form/login.jsp
    2. Type  admin / admin  and press Login. LoginServlet compares the two
       values, stores the username in the session, and REDIRECTS to index.jsp
       (POST/Redirect/GET, so a refresh cannot re-post the form).
    3. Every page now loads normally - the filter lets requests through while
       the session carries "loggedInUser".
    4. Click "Logout" (Session 1 section) or open .../logout. The session is
       invalidated and you land back on the login page; the next page you ask
       for is redirected again.
    5. Check it: open the login page in a second browser (or a private window)
       and go straight to index.jsp - you are bounced back to the form.

    Wrong password -> a red message on the form, nothing in the log.
    The accepted pair is a constant in LoginServlet.java
    (USERNAME / PASSWORD), so it is one edit to change.
    Only /login.jsp, /login and static css/js/images are public; everything
    else - JSPs, servlets, file downloads - needs the session.


SESSION 1 - registration form and sessions
    1. Open    http://localhost:9090/employee-form/employeeregister.jsp
    2. Press "Load Dummy Data" to fill all six fields with sample values, or
       type your own.
    3. Press "Submit". The browser POSTs to /employee-form/register.
    4. EmployeeServlet reads the six parameters, maps them onto the Employee
       bean, and calls EmployeeDao.registerEmployee(...).
    5. On success the servlet redirects to employeedetails.jsp, which shows
       "User successfully registered!".
    6. Check the row landed:   SELECT * FROM employees.employee;
    7. Submit a SECOND time. The corrected DAO omits `id` from the INSERT so
       MySQL assigns 1, then 2, then 3... This is the check that proves the
       fix - see section 10, item (a).
    8. Open    http://localhost:9090/employee-form/sessionDemo
       The page reports isNew(), getId(), getAttribute(), the creation time
       and the last-accessed time.
    9. Refresh it. isNew() is now false and getId() is unchanged - the
       container sent the JSESSIONID cookie back.
    10. Click "logout (invalidate)". You land back on the demo with a brand new
        session id, because invalidate() expired the old one.
    11. Open rewriteurl.jsp to see c:url and response.encodeURL() appending
        ;jsessionid=... (visible only when the browser is not keeping cookies).


SESSION 2 - JSTL SQL tags
    1. Run db-setup.sql first (TEST.Employees must exist).
    2. Open  http://localhost:9090/employee-form/session2/jstl_sql_select.jsp
       Four rows appear - built entirely from <sql:query> + <c:forEach>.
    3. Click through INSERT, UPDATE and DELETE from the links on each page.
       Every page re-runs the SELECT afterwards so you see the effect.
    4. Things worth noticing while you click:
         - INSERT takes an explicit "?insert=1" parameter, so reloading the
           page does not insert again.
         - UPDATE validates the age as a number first. Try age=abc.
         - DELETE asks for confirm=yes and shows a JavaScript confirm().
           Reload after deleting and it does not delete again.


SESSION 3 - CRUD
    1. Run db-setup.sql first (demo.users must exist).
    2. Open  http://localhost:9090/employee-form/users?action=list
    3. "Create new user" -> the form has a "Load Dummy Data" button too.
       Press Create. The URL comes back to ?action=list - that is
       POST/Redirect/GET. Refresh now: nothing is submitted a second time.
    4. Press "edit" on any row. The form is pre-filled; press Update.
    5. Press "delete" on any row. A confirm() box appears first.
    6. Try validation: submit with a blank name, or with an email that has no
       @. You get a red message instead of a database error.
    7. The message at the top of the list is read once and then removed with
       <c:remove>, so it does not repeat on refresh.


SESSION 4 - listing data
    1. Open  http://localhost:9090/employee-form/display
       Seven rows, HTML generated line by line with out.println(). This is
       what the PPT shows.
    2. Click "same data, servlet + JSP split" ->
       http://localhost:9090/employee-form/displayJsp
       Same query, same seven rows, plus the total payroll (280000) formatted
       as currency.
    3. Compare the two source files. The JSP version is the shape you want in
       real code: data in Java, markup in the JSP.


SESSION 5 - JSTL support
    1. Open  http://localhost:9090/employee-form/session5/index.jsp
       It lists the five tag libraries and their URIs, and calls out the
       wrong https:// URIs used by the PPT.
    2. Work through jstl-core.jsp (11 tags), jstl-bean.jsp (jsp:useBean vs
       EL, iterating a List and a Map), then jstl-formatting.jsp (fmt
       number/date, a resource bundle, and 14 fn string functions).
    3. The bottom of each page links to the next.


SESSION 6 - file upload
    1. Open  http://localhost:9090/employee-form/session6/multiPartForm.jsp
    2. Choose a file, press Upload. You get the saved name and byte count.
    3. Upload the SAME file again. The second one comes back as
       report-1.txt style - it never overwrites.
    4. Try the Commons FileUpload form at
       session6/commonsFileUploadForm.jsp. The HTML form is identical; only
       the servlet it posts to differs.
    5. See everything uploaded so far:
       http://localhost:9090/employee-form/uploadedFiles
    6. Worth testing: upload a file larger than 5 MB (reported, not saved),
       and click Upload without choosing anything (reported as "No file was
       received").


--------------------------------------------------------------------------------
9. ALL AVAILABLE URLS  (server running on port 9090)
--------------------------------------------------------------------------------

    Index                         http://localhost:9090/employee-form/

    Login (admin / admin)         .../login.jsp   -> POST .../login

    Session 1
      Registration form           .../employeeregister.jsp
      Success page                .../employeedetails.jsp
      Session demo                .../sessionDemo
      Logout / invalidate         .../logout   -> redirects to .../login.jsp
      URL rewriting sample        .../rewriteurl.jsp

    Session 2
      Index and setup             .../session2/index.jsp
      SELECT                      .../session2/jstl_sql_select.jsp
      INSERT                      .../session2/jstl_sql_insert.jsp
      UPDATE                      .../session2/jstl_sql_update.jsp
      DELETE                      .../session2/jstl_sql_delete.jsp

    Session 3
      Index and setup             .../session3/index.jsp
      User list (READ)            .../users?action=list
      Create form                 .../users?action=create
      Edit form                   .../users?action=edit&id=1

    Session 4
      Index and setup             .../session4/index.jsp
      Raw servlet (out.println)   .../display
      Servlet + JSP split         .../displayJsp

    Session 5
      Index and taglib URIs       .../session5/index.jsp
      Core tags                   .../session5/jstl-core.jsp
      JavaBean example            .../session5/jstl-bean.jsp
      fmt and fn tags             .../session5/jstl-formatting.jsp

    Session 6
      Index and comparison        .../session6/index.jsp
      Upload form (Part API)      .../session6/multiPartForm.jsp
      Upload form (Commons)       .../session6/commonsFileUploadForm.jsp
      List uploaded files         .../uploadedFiles

    Config reference              .../readme.txt

    POST-only targets (opening them in a browser returns HTTP 405, which is
    correct):  /register, /multiPartServlet, /commonsUploadServlet,
               and the save/delete forms under /users.


--------------------------------------------------------------------------------
10. DEVIATIONS FROM THE PPT  (and why)
--------------------------------------------------------------------------------

The teaching file in D:\sandeep\jsp-topic3 reproduces the PPT code faithfully
and flags each problem inline. This runnable project corrects them, because
the point of the lab is to see the thing work.

    (a) Hard-coded primary key  [Session 1]
        The PPT binds  id = 1  explicitly:
            preparedStatement.setInt(1, 1);
        with `id` also listed in the INSERT column list. The FIRST submit
        works; every submit after it fails with
            Duplicate entry '1' for key 'PRIMARY'
        Fix: drop `id` from the INSERT and let AUTO_INCREMENT assign it.
        This is also why employees.employee is left empty in db-setup.sql.

    (b) Deprecated driver class  [Sessions 1-4]
        The PPT calls Class.forName("com.mysql.jdbc.Driver"). Connector 8.x
        prints a deprecation warning for that name and it no longer exists.
        Fix: com.mysql.cj.jdbc.Driver.

    (c) Incomplete JDBC URL  [Sessions 1-4]
        The PPT uses only ?useSSL=false. Against MySQL 8 with connector
        8.0.13 that throws "Public Key Retrieval is not allowed".
        Fix: serverTimezone=UTC and allowPublicKeyRetrieval=true (section 4.2).

    (d) Servlet methods left empty  [Session 3]
        The PPT's Session 3 skeleton has empty method bodies, so nothing is
        created, read, updated or deleted. Every method here is implemented,
        with validation before the write and POST/Redirect/GET after it so a
        refresh cannot re-submit.

    (e) Undefined ${snapshot} in DELETE  [Session 2]
        The PPT's delete page reads ${empId}, which is never set anywhere in
        the deck, so the parameter binds as null and MySQL rejects it.
        Fix: read empId from the request and require confirm=yes.

    (f) Invalid UPDATE statement  [Session 2]
        The slide shows:
            UPDATE Employees SET WHERE last = 'Ali'
        SET has no column list, and a positional parameter follows a literal
        that is not in the column list. MySQL rejects this outright.
        Fix: UPDATE Employees SET age = ? WHERE id = ? with both values bound.

    (g) Hard-coded id = 104 on INSERT  [Session 2]
        Works exactly once, then every reload fails with
            Duplicate entry '104' for key 'PRIMARY'
        Fix: AUTO_INCREMENT, so the page can be demonstrated repeatedly.

    (h) "&" inside a JSP tag attribute  [Session 2]
        Writing url="...&amp;serverTimezone=UTC" inside a tag attribute does
        NOT produce "&" - the container hands the literal text "&amp;" to the
        tag and MySQL answers:
            Malformed database URL, failed to parse the connection string
        Fix: read the URL from DbConfig and pass it through an EL expression.

    (i) Oracle via the JDBC-ODBC bridge  [Session 4]
        The PPT does:
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
            DriverManager.getConnection("jdbc:odbc:mydsn", "system", "pintu");
        That driver was REMOVED from the JDK in Java 7, so it cannot run on
        JDK 8 or later at all. It also needs a DSN created in
        Control Panel > Data Sources (ODBC), which is not present on most
        64-bit-only Windows installs. The catch block prints the word
        "error" and nothing else, so a wrong password, a missing table and a
        typo all look identical.
        Fix: MySQL connector already in WEB-INF/lib, the real message
        printed, and the failing statement logged.

    (j) Wrong taglib URIs  [Session 5]
        The deck writes:
            uri="https://java.sun.com/jsp/jstl/core"
        The TLD URIs are identifiers, not web addresses, and the java.sun.com
        ones are http://. The https form matches no TLD, so the container
        reports "The absolute uri: ... cannot be resolved in the web.xml".
        Note Session 2 of the SAME deck gets this right with http:// - the
        two sessions contradict each other. Every page here uses http://.

    (k) Four JSTL tags that do not exist as written  [Session 5]
        c:out has no defaultValue attribute (use the EL ternary);
        c:forTokens takes items=, not tokens= (tokens is a varStatus
        property); LoopTagStatus has no even/odd property (derive it from
        index); and <jsp:useBean type="java.util.Date"> throws
        InstantiationException because Date is a value type, not a bean.
        Each fails at compile time or on first access with an error that
        reads like a configuration problem. All four are corrected.

    (l) Unsafe file name handling  [Session 6]
        The PPT writes the client-supplied name straight to disk, so a name
        like ../../../conf/server.xml escapes the upload directory (path
        traversal), and a re-upload silently overwrites the previous file.
        A cancelled browse box also sends a part with an empty name, which
        makes write() throw.
        Fix: reduce the name to its last path segment, sanitise the
        characters, skip empty names, and never overwrite - plus a size check
        that reports the file instead of dropping it.

    (m) Uploads served from inside the webapp  [Session 6]
        uploads/ sits under the deployed webapp, so Tomcat will happily serve
        an uploaded .html file. Documented here rather than "fixed", because
        keeping the file visible is what makes the demo work. In a real
        application put it outside the webapp root, disable execute
        permission on it, or store the file as a BLOB in the database.


One deliberate non-change: the explicit Class.forName(DRIVER) call is KEPT,
even though JDBC 4.0+ normally auto-registers drivers. DriverManager holds a
single static driver list per JVM, initialised from whichever classloader
loaded it first - inside Tomcat that is often not this webapp's loader, and
the symptom is "No suitable driver found" even though the jar is sitting in
WEB-INF/lib. The explicit load makes registration deterministic.


--------------------------------------------------------------------------------
11. COMMON PROBLEMS AND FIXES
--------------------------------------------------------------------------------

    "Sent back to the login page even though I typed the URL correctly"
        -> AuthenticationFilter is guarding the application (section 8.0).
           Sign in at .../login.jsp with admin / admin. After /logout the
           session is invalidated, so the redirect is expected behaviour,
           not a bug.

    "Access denied for user 'root'@'localhost'"
        -> jdbc.pass in WEB-INF/db.properties does not match your server
           (section 4.1). The MOST common first-run failure, and NOT a code
           bug. One edit fixes all six sessions.

    "No suitable driver found for jdbc:mysql://..."
        -> The driver class was never registered, OR the jar is missing.
           Confirm WEB-INF/lib/mysql-connector-java-8.0.13.jar exists, then
           do a full republish: right-click project > Refresh, restart the
           server in the Servers view.

    "Public Key Retrieval is not allowed"
        -> allowPublicKeyRetrieval=true is missing from the URL (section 4.2).

    "The server time zone value ... is unrecognized"
        -> serverTimezone is missing from the URL (section 4.2).

    "Unknown database 'employees'" / 'TEST' / 'demo'
        -> db-setup.sql was never run (section 4).

    "The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved"
        -> jstl-1.2.jar or standard-1.1.2.jar is missing from WEB-INF/lib,
           OR a page uses the https:// form from the PPT (section 10, item j).

    "Attribute [...] invalid for tag [...]" or
    "attribute [items] is mandatory for tag [forTokens]"
        -> A JSTL tag was given an attribute that does not exist in the TLD.
           See section 10, item (k).

    A JSTL page shows an error message in red instead of data
        -> Read it - the Session 2 pages wrap every query in <c:catch> and
           print the actual message, because the PPT version hides the cause
           entirely.

    HTTP 404 on EVERY servlet URL, and nothing in the console
        -> The classes were compiled for a newer JDK than the one Tomcat runs.
           This is the silent failure described in STEP 2. Set the facet and
           the compiler level to 1.8, then Project > Clean.

    HTTP 404 on a servlet right after adding a new one
        -> Eclipse has not published it. Right-click project > Refresh, then
           right-click the server > Publish. Check that the .class file is
           actually present under the deployed
           .metadata\.plugins\org.eclipse.wst.server.core\tmp0\
           wtpwebapps\employee-form\WEB-INF\classes\

    HTTP 405 on /register or on an upload servlet
        -> Expected. They only answer POST. Use the form.

    HTTP 400 "This servlet only accepts multipart/form-data"
        -> The form is missing enctype="multipart/form-data". All three parts
           of a file upload are required: method="post",
           enctype="multipart/form-data", and <input type="file">.

    "The type javax.servlet.http.HttpServlet cannot be resolved"
        -> The servlet-api.jar classpath entry is broken. Repeat STEP 3.

    Port 9090 already in use
        -> Another Tomcat owns it. Do not start a second Tomcat by hand with
           catalina.bat while Eclipse is managing the server - they fight over
           the port. Stop the extra one, or change the port in the server's
           Ports tab.

    "Port 8105 already in use" when Tomcat will not start
        -> That is the SHUTDOWN port configured in conf/server.xml. A stray
           catalina.bat instance is holding it; stop it from Task Manager or
           with:  netstat -ano | findstr 8105   then kill that PID.

    Changes to a JSP do not show up
        -> Cached page. Ctrl+F5, then restart the server if needed.

    A change to a .java file does not show up
        -> Classes are published on build. Project > Build Project (or let
           automatic build run), then right-click the server > Publish.


--------------------------------------------------------------------------------
12. STOPPING THE SERVER
--------------------------------------------------------------------------------

    Servers view > right-click the Tomcat server > "Stop".

    Do not also leave a manually started catalina.bat running - it will hold
    ports the Eclipse server needs and cause "port already in use" errors.


================================================================================
 End of file
================================================================================
