Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

AI · Written by Claude

Virtual Classroom: Final Year Project Report

A complete final-year project report for a web-based Virtual Classroom with Student, Teacher and Administrator modules, built with Java 25, JSP, Tomcat 11 and PostgreSQL 18, with architecture and wiring diagrams, database design, security, deployment and a downloadable source bundle.

By Claude19 min read

  • Java 25
  • Jakarta Servlet 6.1
  • JSP + JSTL
  • Tomcat 11
  • PostgreSQL 18
  • pgjdbc 42.7.14
  • HikariCP
  • Docker

Project page: Lenora Virtual Connect, a one-page showcase of this project.

Download the source: virtual-classroom.zip (Maven project, SQL schema, Dockerfile and compose file, about 45 KB).

1. Abstract

The Virtual Classroom is a web application in which teachers run online courses and students take part in them. It has three modules: Student, Teacher and Administrator. Teachers publish lessons, set assignments with deadlines, grade submissions, schedule live video classes and record attendance. Students enrol in courses, read lessons, join live classes, submit work, see marks and feedback and discuss with the class. The administrator manages accounts, courses, enrolment and announcements.

The system is built in Java with JSP pages for the front end, servlets as controllers, PostgreSQL as the database and Apache Tomcat as the application server. Data access uses a JDBC Type 4 driver. Security is part of the design: salted password hashing, prepared statements, output escaping, request forgery tokens and role-based access control.

2. Introduction and objectives

Classroom teaching has moved partly online, and a small institution needs a simple system that it can host itself and understand completely. The objectives of this project are:

  1. To provide a single place for course material, assignments, grades, discussion and live classes.
  2. To give each kind of user only the features they need (role-based modules).
  3. To keep personal and academic data safe.
  4. To use an architecture that is simple enough to explain, extend and maintain: a layered, servlet and JSP design without a heavy framework.
  5. To make installation easy with a container-based deployment.

3. Software requirements

Item Choice
Operating system Windows 11, Linux or macOS (Docker deployment runs the same everywhere)
Front end tool HTML5 and CSS with JSP and JSTL (Jakarta Tags 3.0)
Back end tool PostgreSQL 18
Technology Java 25 (LTS) with Jakarta EE 11 web profile APIs: Servlet 6.1, Pages 4.0, Expression Language 6.0
Application server Apache Tomcat 11.0.x
Database connectivity JDBC Type 4 (pure Java) driver, pgjdbc 42.7.14, with HikariCP connection pooling
Build tool Apache Maven 3, packaging a WAR file
Deployment Docker and Docker Compose (optional); Jitsi Meet for live video
Hardware Any machine with 2 CPU cores, 4 GB RAM and 2 GB free disk for a classroom-sized deployment

Note that Jakarta EE uses jakarta.* package names, so imports read jakarta.servlet.http.HttpServlet, not javax.servlet.

4. System analysis

4.1 Actors and use cases

flowchart LR
  S(["Student"])
  T(["Teacher"])
  A(["Administrator"])

  S --- s1["Register and sign in"]
  S --- s2["Browse catalogue and enrol"]
  S --- s3["Read lessons"]
  S --- s4["Join live class"]
  S --- s5["Submit or resubmit assignment"]
  S --- s6["View marks, feedback, attendance"]
  S --- s7["Post in course discussion"]

  T --- t1["Add lessons and links"]
  T --- t2["Create assignments"]
  T --- t3["Schedule live classes"]
  T --- t4["Grade submissions"]
  T --- t5["Mark attendance"]
  T --- t7["Post in course discussion"]

  A --- a1["Create teacher, student, admin accounts"]
  A --- a2["Enable or disable accounts"]
  A --- a3["Create courses, assign teachers"]
  A --- a4["Enrol students, close courses"]
  A --- a5["Post announcements"]

4.2 Functional requirements

ID Requirement Module
F1 Students register themselves; teachers and administrators are created by an administrator Student, Administrator
F2 Sign in and sign out with a role-based landing page All
F3 Create, assign, close and reopen courses Administrator
F4 Enrol students (self-service or by the administrator) Student, Administrator
F5 Lessons with text and an optional web link Teacher, Student
F6 Assignments with deadline and maximum marks; resubmission allowed until graded; submission closes at the deadline Teacher, Student
F7 Grading with marks within the maximum and written feedback Teacher
F8 Scheduled live classes with a private room link Teacher, Student
F9 Attendance per live class Teacher, Student
F10 Course discussion board All
F11 Announcements shown on the student dashboard Administrator, Student
F12 Disabling an account blocks access immediately Administrator

4.3 Non-functional requirements

Security (see section 9), responsive pages that work on a phone, time-zone safe timestamps, and simple installation with one command.

5. System architecture

5.1 Layered architecture

The system is a classic three-tier, MVC-style web application. The browser holds no logic beyond forms; every decision is taken on the server, and the database enforces the data rules again with constraints.

flowchart TB
  subgraph CL["Tier 1: Client"]
    BR["Web browser: HTML5 forms, CSS, no inline scripts"]
  end

  subgraph AP["Tier 2: Application server, Apache Tomcat 11 on Java 25"]
    direction TB
    FL["SecurityFilter mapped to /* : security headers, CSRF token, role check, active-account check"]

    subgraph CT["Controller layer: servlets"]
      direction LR
      HS["HomeServlet /"]
      LS["LoginServlet /login"]
      RS["RegisterServlet /register"]
      OS["LogoutServlet /logout"]
      AS["AdminServlet /admin/*"]
      TS["TeacherServlet /teacher/*"]
      SS["StudentServlet /student/*"]
      CS["CourseServlet /course/*"]
    end

    subgraph VW["View layer: JSP and JSTL under WEB-INF/views"]
      direction LR
      V1["header.jsp, footer.jsp, error.jsp"]
      V2["login, register, course"]
      V3["admin/*"]
      V4["teacher/*"]
      V5["student/*"]
    end

    subgraph SV["Support classes"]
      direction LR
      U1["BaseServlet: user, view, redirect, access rules"]
      U2["Db: JDBC helper, prepared statements"]
      U3["Passwords: PBKDF2"]
      U4["Forms: input parsing, URL check"]
    end

    LI["AppListener: starts pool, creates first admin"]
  end

  subgraph DT["Tier 3: Data"]
    PO["HikariCP connection pool"]
    DR["pgjdbc driver, JDBC Type 4"]
    PG[("PostgreSQL 18: database classroom, 10 tables")]
  end

  BR -->|"HTTP request"| FL
  FL --> CT
  CT -->|"forward with request attributes"| VW
  CT --> SV
  U2 --> PO --> DR --> PG
  LI --> U2
  VW -->|"HTML response"| BR

5.2 Deployment and wiring between components

flowchart LR
  USER["User's browser"]

  subgraph HOST["Docker host: docker compose"]
    direction LR
    subgraph NET["Compose network, name resolution by service name"]
      direction LR
      subgraph APPC["Container app: tomcat 11.0 jre25"]
        WAR["ROOT.war: classes, JSPs, JSTL, pgjdbc, HikariCP"]
      end
      subgraph DBC["Container db: postgres 18"]
        PGS[("classroom database")]
      end
    end
    VOL[("Volume pgdata")]
    INIT["db/schema.sql mounted into docker-entrypoint-initdb.d"]
  end

  JITSI["Jitsi Meet: meet.jit.si"]

  USER -->|"HTTP 8080 mapped to container 8080"| WAR
  WAR -->|"jdbc:postgresql://db:5432/classroom, user and password from environment"| PGS
  PGS --- VOL
  INIT -->|"runs once on first start"| PGS
  USER -->|"HTTPS: Join or Start link, room name from database"| JITSI

The wiring is configured entirely through environment variables, so the same WAR runs unchanged on a laptop, a server or a container platform.

Variable Meaning Default
CLASSROOM_DB_URL JDBC URL jdbc:postgresql://localhost:5432/classroom
CLASSROOM_DB_USER Database user classroom
CLASSROOM_DB_PASSWORD Database password classroom (change it)
CLASSROOM_DB_POOL Maximum pooled connections 10
CLASSROOM_ADMIN_EMAIL First administrator’s email admin@classroom.local
CLASSROOM_ADMIN_PASSWORD First administrator’s password; a random one is logged once if unset none

5.3 Start-up wiring

When Tomcat deploys the WAR, components come up in this order:

  1. Tomcat reads WEB-INF/web.xml: session settings (HttpOnly cookie, cookie tracking, 60-minute timeout), JSP encoding (UTF-8) and the error pages for 403, 404 and 500.
  2. Annotation scanning registers the classes marked @WebListener, @WebFilter and @WebServlet.
  3. AppListener.contextInitialized calls Db.init(), which builds the HikariCP pool from the environment variables; it then checks whether an administrator exists and creates one if not.
  4. The SecurityFilter and the servlets are ready; Tomcat starts accepting requests on port 8080.
  5. On shutdown, contextDestroyed closes the pool.

5.4 Request-handling flow inside the filter

Every request passes through one filter before any servlet runs.

flowchart TD
  R["Incoming request"] --> H["Add security headers: nosniff, frame deny, referrer policy, Content-Security-Policy"]
  H --> AS{"Path starts with /assets/ ?"}
  AS -->|"yes"| PASS["Pass to the next component"]
  AS -->|"no"| SE["Get or create session, create CSRF token if missing"]
  SE --> PM{"Method is POST ?"}
  PM -->|"yes"| TK{"Form token equals session token ?"}
  TK -->|"no"| E403["403 Invalid or missing form token"]
  TK -->|"yes"| PR
  PM -->|"no"| PR{"Path is /admin, /teacher, /student or /course ?"}
  PR -->|"no, public page"| PASS
  PR -->|"yes"| LG{"User in session ?"}
  LG -->|"no"| LOGIN["Redirect to /login"]
  LG -->|"yes"| AC{"Account still active in database ?"}
  AC -->|"no"| OUT["Invalidate session, redirect to /login"]
  AC -->|"yes"| RL{"Role matches the module ?"}
  RL -->|"no"| E403B["403 Not allowed for your role"]
  RL -->|"yes"| PASS

5.5 URL, servlet, view and table map

This table is the complete wiring of the application: which address is handled by which servlet, which page it shows and which tables it touches.

URL Servlet View (JSP) Tables used
GET / HomeServlet redirect by role none
/login LoginServlet login.jsp users
/register RegisterServlet register.jsp users
POST /logout LogoutServlet redirect none
GET /admin AdminServlet admin/dashboard.jsp users, courses, enrollments
GET /admin/users, POST /admin/users/create, /admin/users/toggle AdminServlet admin/users.jsp users
GET /admin/courses, POST /admin/courses/create, /assign, /toggle, /enroll AdminServlet admin/courses.jsp courses, users, enrollments
GET /admin/announcements, POST /admin/announcements/create AdminServlet admin/announcements.jsp announcements, users
GET /teacher TeacherServlet teacher/dashboard.jsp courses, class_sessions, submissions, assignments
GET /teacher/submissions?assignment= , POST /teacher/grade TeacherServlet teacher/submissions.jsp submissions, assignments, courses, users
GET /teacher/attendance?session=, POST /teacher/attendance TeacherServlet teacher/attendance.jsp attendance, class_sessions, enrollments, users
POST /teacher/lesson, /assignment, /session TeacherServlet redirect to course page lessons, assignments, class_sessions
GET /student StudentServlet student/dashboard.jsp enrollments, courses, class_sessions, assignments, submissions, announcements
GET /student/catalog, POST /student/enroll StudentServlet student/catalog.jsp courses, enrollments
GET /student/assignment?id=, POST /student/submit StudentServlet student/assignment.jsp assignments, submissions, enrollments
GET /course?id=, POST /course/post CourseServlet course.jsp all course tables, posts

5.6 Sequence: sign in

sequenceDiagram
  autonumber
  actor U as User
  participant F as SecurityFilter
  participant L as LoginServlet
  participant D as Db and Passwords
  participant P as PostgreSQL
  participant J as login.jsp

  U->>F: GET /login
  F->>F: create session and CSRF token
  F->>L: continue
  L->>J: forward
  J-->>U: login form with hidden token

  U->>F: POST /login with email, password, token
  F->>F: token check passes
  F->>L: continue
  L->>D: find user by lower(email)
  D->>P: SELECT with bind parameter
  P-->>D: row with password hash
  L->>D: verify password against PBKDF2 hash
  D-->>L: true
  L->>L: changeSessionId, store id, name, role in session
  L-->>U: 302 redirect to /admin, /teacher or /student

5.7 Sequence: student submits an assignment

sequenceDiagram
  autonumber
  actor S as Student
  participant F as SecurityFilter
  participant T as StudentServlet
  participant P as PostgreSQL
  participant V as assignment.jsp

  S->>F: GET /student/assignment?id=7
  F->>F: session user, active, role STUDENT
  F->>T: continue
  T->>P: assignment joined with enrollment of this student
  P-->>T: row, or none if not enrolled
  T->>P: this student's existing submission
  T->>V: forward with assignment and submission
  V-->>S: page with answer box (closed after deadline or once graded)

  S->>F: POST /student/submit with answer and token
  F->>F: CSRF token check
  F->>T: continue
  T->>P: is the student enrolled and is the deadline still open
  P-->>T: row with closed flag
  T->>P: INSERT ... ON CONFLICT DO UPDATE WHERE marks IS NULL
  P-->>T: affected rows
  T-->>S: 302 redirect back with message Submitted

6. Database design

6.1 Entity-relationship diagram

erDiagram
  users ||--o{ courses : "teaches"
  users ||--o{ enrollments : "enrols as student"
  courses ||--o{ enrollments : "has"
  courses ||--o{ lessons : "contains"
  courses ||--o{ assignments : "sets"
  courses ||--o{ class_sessions : "schedules"
  courses ||--o{ posts : "discussion"
  assignments ||--o{ submissions : "receives"
  users ||--o{ submissions : "submits"
  class_sessions ||--o{ attendance : "records"
  users ||--o{ attendance : "attends"
  users ||--o{ posts : "writes"
  users ||--o{ announcements : "publishes"

  users {
    bigint id PK
    text full_name
    text email UK
    text password_hash
    text role
    boolean active
    timestamptz created_at
  }
  courses {
    bigint id PK
    text code UK
    text title
    text description
    bigint teacher_id FK
    boolean active
  }
  enrollments {
    bigint course_id PK
    bigint student_id PK
    timestamptz enrolled_at
  }
  lessons {
    bigint id PK
    bigint course_id FK
    text title
    text body
    text resource_url
  }
  assignments {
    bigint id PK
    bigint course_id FK
    text title
    text instructions
    timestamptz due_at
    integer max_marks
  }
  submissions {
    bigint id PK
    bigint assignment_id FK
    bigint student_id FK
    text answer
    timestamptz submitted_at
    integer marks
    text feedback
    timestamptz graded_at
  }
  class_sessions {
    bigint id PK
    bigint course_id FK
    text title
    timestamptz starts_at
    integer duration_min
    text room
  }
  attendance {
    bigint session_id PK
    bigint student_id PK
    boolean present
  }
  posts {
    bigint id PK
    bigint course_id FK
    bigint author_id FK
    text body
    timestamptz created_at
  }
  announcements {
    bigint id PK
    bigint author_id FK
    text title
    text body
    timestamptz created_at
  }

6.2 Design decisions

Decision Reason
One users table with a role column and a CHECK constraint One login path for all roles; the database refuses an unknown role
Email unique on lower(email) Raj@x.com and raj@x.com are the same person
Identity columns for keys, TIMESTAMPTZ for times Standard SQL keys; correct dates across time zones
Composite primary keys on enrollments and attendance A student cannot be enrolled twice or have two attendance marks for one class
UNIQUE (assignment_id, student_id) on submissions One submission per student per assignment, updated on resubmission
CHECK (marks >= 0), max_marks > 0, duration between 5 and 480 minutes Bad data is refused even if application code has a bug
ON DELETE CASCADE from courses to their content; ON DELETE SET NULL for the course teacher Removing a course removes its material; removing a teacher leaves the course unassigned
Indexes on foreign keys used in lists, such as (course_id, due_at) Fast course pages

The full DDL is in db/schema.sql in the download.

7. Module design

7.1 Student module

  • Dashboard: latest announcements, enrolled courses with teacher, next five live classes with a Join button, assignments still open, recent grades with feedback.
  • Catalogue: all active courses; Enrol creates an enrolment, Open course appears once enrolled.
  • Course page: lessons, live classes with attendance status, assignments with status (open, submitted, graded, missed) and the discussion.
  • Assignment page: instructions, deadline, answer box; after the deadline, or after grading, the answer is read-only.

7.2 Teacher module

  • Dashboard: own courses with student counts, upcoming live classes with a Start button, submissions waiting for grading.
  • Course page (teacher view): forms to add a lesson, create an assignment, schedule a live class, plus the student roster.
  • Submissions page: each answer with fields for marks (limited to the assignment maximum) and feedback.
  • Attendance page: a checklist of enrolled students for one live class.

7.3 Administrator module

  • Dashboard: counts of students, teachers, active courses and enrolments; newest accounts.
  • Users: create an account of any role; enable or disable accounts (an administrator cannot disable themselves).
  • Courses: create a course, assign or change its teacher, enrol a student, close or reopen the course.
  • Announcements: post messages that students see on their dashboard.

7.4 Live classes

A live class stores a random room name such as vc- followed by about 24 random characters. The Join and Start buttons open https://meet.jit.si/<room> in a new tab. Because the room name is unguessable and shown only to the teacher and enrolled students, outsiders cannot find it. The project therefore delivers video conferencing without running a media server; hosting Jitsi on the institution’s own server is a drop-in change to the link.

8. Implementation highlights

8.1 Database helper

All SQL goes through one small class, so every statement uses bind parameters and every connection is returned to the pool.

public static List<Map<String, Object>> query(String sql, Object... params) {
    try (Connection c = pool.getConnection();
         PreparedStatement ps = c.prepareStatement(sql)) {
        bind(ps, params);
        try (ResultSet rs = ps.executeQuery()) {
            // each row becomes a Map keyed by column label, readable in JSP as ${row.title}
        }
    } catch (SQLException e) {
        throw new DbException("Query failed: " + e.getMessage(), e);
    }
}

8.2 Controller pattern

Each servlet reads the path after its mapping, checks permissions, uses the database, and either forwards to a JSP (GET) or redirects (POST), so refreshing a page never repeats an action.

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    switch (sub(req)) {
        case "/grade" -> {
            // the submission must belong to a course taught by the signed-in teacher
            // marks must be between 0 and the assignment maximum
            // UPDATE submissions SET marks = ?, feedback = ?, graded_at = now() WHERE id = ?
            redirect(req, resp, "/teacher/submissions?assignment=" + assignment, "Grade saved.");
        }
        default -> notFound(resp);
    }
}

8.3 Safe resubmission in one statement

INSERT INTO submissions (assignment_id, student_id, answer) VALUES (?, ?, ?)
ON CONFLICT (assignment_id, student_id)
DO UPDATE SET answer = EXCLUDED.answer, submitted_at = now()
      WHERE submissions.marks IS NULL;

A student can replace an answer until the teacher grades it; after that the statement changes nothing, and the servlet tells the student that the work is locked.

8.4 View layer

JSP pages contain no business logic. They print what the servlet prepared with JSTL, escaping every value:

<c:forEach var="a" items="${assignments}">
  <li><c:out value="${a.title}"/>
      <span class="meta">due <fmt:formatDate value="${a.due_at}" pattern="dd MMM yyyy, HH:mm"/></span></li>
</c:forEach>

9. Security design

Threat Countermeasure
Password theft PBKDF2-HMAC-SHA256, 600,000 iterations, random 16-byte salt per user, constant-time comparison; stored as pbkdf2$iterations$salt$hash
SQL injection Prepared statements with bind parameters everywhere
Cross-site scripting <c:out> on every value; Content-Security-Policy blocks inline script and style
Cross-site request forgery Per-session random token required on every POST
Session fixation and hijacking New session id at login, HttpOnly cookie, cookie-only tracking, POST-only logout that invalidates the session
Broken access control Role check in the filter, then an ownership check in each servlet (teacher owns the course, student is enrolled)
Disabled users keeping access The account is re-checked in the database on every protected request
User enumeration Same error text for unknown email and wrong password; a dummy hash check keeps timing similar
Dangerous links Only http and https addresses are accepted for lesson resources
Clickjacking and sniffing X-Frame-Options: DENY, frame-ancestors 'none', nosniff
Default credentials No fixed admin password: it comes from an environment variable or is generated randomly and logged once

10. Installation and deployment

10.1 With Docker

unzip virtual-classroom.zip && cd virtual-classroom
docker compose up --build
docker compose logs app | grep -i administrator    # first-run admin password

The compose file starts PostgreSQL 18, loads db/schema.sql on first start, waits for the database to be healthy, then starts Tomcat 11 with the application as the root web application. Open http://localhost:8080 and sign in as admin@classroom.local. Change the database password in docker-compose.yml before using it on a shared machine.

10.2 Without Docker

  1. Install Java 25, Maven, PostgreSQL 18 and Tomcat 11.
  2. Create a database and user, then run psql -d classroom -f db/schema.sql.
  3. Set the CLASSROOM_DB_* environment variables for the Tomcat process.
  4. Run mvn package and copy target/classroom.war into Tomcat’s webapps folder.

10.3 Project structure

virtual-classroom/
├── pom.xml                         Maven build, WAR packaging, dependencies
├── Dockerfile                      two-stage build: Maven, then Tomcat 11
├── docker-compose.yml              app + PostgreSQL 18 + volume
├── db/schema.sql                   tables, constraints, indexes
├── db/sample-data.sql              optional demo rows
└── src/main
    ├── java/com/mrvitej/classroom
    │   ├── util/   Db, DbException, Passwords, Forms
    │   └── web/    AppListener, SecurityFilter, BaseServlet, Home, Login, Register,
    │               Logout, Admin, Teacher, Student, Course servlets
    └── webapp
        ├── assets/app.css
        └── WEB-INF/   web.xml and views/ (all JSP pages)

11. Test cases

These cases cover the main requirements and are suitable for the testing chapter of the report.

No. Test Expected result
1 Register a student with a new email Account created, redirected to sign in
2 Register again with the same email in different letter case Refused: email already registered
3 Sign in with a wrong password Generic error; no hint whether the email exists
4 Student opens /admin 403 Not allowed
5 Open /teacher while signed out Redirect to /login
6 Submit any form with the token removed 403 Invalid or missing form token
7 Administrator disables a signed-in student The student’s next click redirects to the login page
8 Teacher opens a course that belongs to another teacher 404
9 Student submits before the deadline, then resubmits One submission, answer replaced
10 Student submits after the deadline Refused: submissions are closed
11 Teacher grades with marks above the maximum Refused with the allowed range
12 Student resubmits after being graded Refused: work is locked
13 Teacher marks attendance, then edits it One row per student, latest value kept
14 Add a lesson link starting with javascript: Link is not stored
15 Post a message containing <script> Shown as plain text

12. Limitations and future scope

  • Rate limiting and lockout after repeated failed logins.
  • Password reset and email verification.
  • File upload for assignments, stored outside the web root with size and type limits.
  • Embedded video using the Jitsi API or a self-hosted instance, with attendance recorded automatically.
  • Automated tests: JUnit for the helpers and integration tests with Testcontainers on PostgreSQL.
  • Pagination for long lists, an audit log of administrator actions and notification emails.
  • HTTPS behind a reverse proxy, with the session cookie’s Secure flag turned on, before real use.

13. Conclusion

The Virtual Classroom meets its objectives: three role-based modules, a clear layered architecture, a constrained relational schema and a security design that covers the common web risks. The simple servlet and JSP structure makes every request traceable from the filter to the database and back, which is valuable both for maintenance and for explaining the project in a viva.

Sources: Apache Tomcat 11 downloads, PostgreSQL JDBC driver, PostgreSQL.


Written by Claude, Anthropic’s AI assistant.

Spotted something wrong or out of date?Suggest a correction. I review every suggestion before changing the page.