A servlet is a Java class that answers HTTP requests inside a container such as Tomcat. This session covers the request and response objects, HTML forms, client information, session tracking and a first database-backed CRUD application.
Objectives
Do not copy. Read for understanding and the viva- Complete questions 1 to 5 of the manual: basics of servlet
- Prepare the deliverable before the lab and finish it during the session
- Be ready to explain every step in the viva
Questions Covered
Do not copy. Read for understanding and the viva| Question | Requirement | Status |
|---|---|---|
| Q1 | Write a Servlet Programme to print the current date and time along with the timestamp | Complete |
| Q2 | Create an HTML form with the input of student information using HTTP Protocol and… | Complete |
| Q3 | Write a servlet program to capture client IPs and display it | Complete |
| Q4 | Write a servlet program for session management using HTTP Session along with tracking… | Complete |
| Q5 | Write a CRUD (Create/Save, Read, Edit/Update, Delete) application using servlet… | Complete |
Preparation
Do not copy. Read for understanding and the viva- Create a Dynamic Web Project (Eclipse) or Java Web application (NetBeans) targeting Tomcat 10; note that Tomcat 10 uses the
jakarta.servletpackage, notjavax.servlet. - Design the IGNOU database and the Student table on paper first: enrolment number, name, date of birth, email, mobile, address, programme, courses. Question 5 and most later sessions reuse it.
- Add the MySQL Connector/J jar to the project’s library path.
- One Maven web project called
ServletLabholds all five answers. Every servlet is mapped with@WebServlet, soweb.xmlonly carries the welcome file and the session timeout. All Java files live insrc/main/java/ignou/, static pages insrc/main/webapp/.
Project Setup
Do not copy. Read for understanding and the vivaDo this once; every question below drops files into the same project.
- NetBeans: File, New Project, Java with Maven, Web Application, Next. Project Name
ServletLab, Next. Server: Apache Tomcat 10.1 (click Add if it is not listed and browse to the extracted Tomcat folder, as in manual figure 2.24), Java EE Version: Jakarta EE 10 Web, Finish. - Eclipse: File, New, Dynamic Web Project, name
ServletLab, Target runtime: Apache Tomcat v10.1, Dynamic web module version 6.0, Finish. Then right-click the project, Configure, Convert to Maven Project. - Replace the generated
pom.xmldependencies with the fragment below and save; the IDE downloads the jars. - Put
web.xmlinsrc/main/webapp/WEB-INF/. - Run: right-click the project, Run (NetBeans) or Run As, Run on Server (Eclipse). The base URL is
http://localhost:8080/ServletLab/.
Lab record: every tab is one file of the answer. Write all of them.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Dependency fragment for a Maven WAR project on Tomcat 10.1 / JDK 17.
NetBeans: File > New Project > Java with Maven > Web Application creates
the rest of this file; paste the <dependencies> block in.
Eclipse: right-click the Dynamic Web Project > Configure > Convert to
Maven Project, then paste the same block. -->
<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>ignou</groupId>
<artifactId>ServletLab</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<dependencies>
<!-- Servlet 6.0 API: Tomcat 10.1 already ships it, so scope is provided -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<!-- JSTL 3.0 (Session 2): API plus the Glassfish implementation, packed in the WAR -->
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jakarta.servlet.jsp.jstl</artifactId>
<version>3.0.1</version>
</dependency>
<!-- MySQL JDBC driver, packed in the WAR -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.4.0</version>
</dependency>
</dependencies>
<build>
<finalName>ServletLab</finalName>
</build>
</project><?xml version="1.0" encoding="UTF-8"?>
<!-- src/main/webapp/WEB-INF/web.xml
Servlet URLs are mapped with @WebServlet annotations in each class, so this
file only holds container-wide settings. -->
<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>ServletLab</display-name>
<welcome-file-list>
<welcome-file>student-form.html</welcome-file>
</welcome-file-list>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>Question 1
Problem Statement
Write in lab recordWrite a Servlet Programme to print the current date and time along with the timestamp.
Solution
Write in lab recordSteps
- Right-click
Source Packages, New, Servlet (NetBeans) or New, Servlet (Eclipse). Class nameDateTimeServlet, packageignou. Untick “Add information to deployment descriptor” so the annotation does the mapping. - Replace the generated body with the listing.
- Run the project and open
http://localhost:8080/ServletLab/datetime. - Press F5 a few times: time and timestamp change, the date does not.
Program
package ignou;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
/** Q1: current date, time and timestamp. URL: /datetime */
@WebServlet("/datetime")
public class DateTimeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
long millis = System.currentTimeMillis();
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateFmt = DateTimeFormatter.ofPattern("dd-MM-yyyy");
DateTimeFormatter timeFmt = DateTimeFormatter.ofPattern("HH:mm:ss");
try (PrintWriter out = response.getWriter()) {
out.println("<!DOCTYPE html><html><head><title>Date and Time</title></head><body>");
out.println("<h2>Current Date and Time</h2>");
out.println("<p>Date: " + now.format(dateFmt) + "</p>");
out.println("<p>Time: " + now.format(timeFmt) + "</p>");
out.println("<p>Full (java.util.Date): " + new Date(millis) + "</p>");
out.println("<p>Timestamp (milliseconds since 1 Jan 1970 UTC): " + millis + "</p>");
out.println("<p>SQL Timestamp: " + new Timestamp(millis) + "</p>");
out.println("</body></html>");
}
}
}Output
Expected browser page (values for the moment of the request; checked by reading the listing, not executed, since no Tomcat is available here):
Current Date and Time
Date: 26-09-2026
Time: 10:42:07
Full (java.util.Date): Sat Sep 26 10:42:07 IST 2026
Timestamp (milliseconds since 1 Jan 1970 UTC): 1790397127342
SQL Timestamp: 2026-09-26 10:42:07.342Explanation
@WebServlet("/datetime")registers the servlet with the container; noservletandservlet-mappingentries are needed inweb.xml. The manual’s examples useweb.xml; both work on Tomcat 10.doGetruns once per GET request.response.setContentTypemust come beforegetWriter()or the charset is ignored.System.currentTimeMillis()is the timestamp: milliseconds since the Unix epoch.LocalDateTimeandDateTimeFormatter(java.time) format the same instant as a readable date and time;java.sql.Timestampis the form a database column would store.- The
trywith resources closes thePrintWriter, which flushes the buffer to the client.
Question 2
Problem Statement
Write in lab recordCreate an HTML form with the input of student information using HTTP Protocol and method, then display the input information using Servlet.
Solution
Write in lab recordSteps
- Add
student-form.htmlundersrc/main/webapp/(NetBeans: right-click Web Pages, New, HTML File). - Add servlet
StudentInfoServletin packageignou. - Run and open
http://localhost:8080/ServletLab/student-form.html(it is also the welcome file, so the bare context URL works). - Fill the form and press Submit. Then change
method="post"tomethod="get"in the HTML, redeploy, submit again and compare the address bar.
Program
Lab record: every tab is one file of the answer. Write all of them.
<!DOCTYPE html>
<!-- src/main/webapp/student-form.html : Q2 input form.
method="post" sends the fields in the request body over HTTP;
change to method="get" once to see them appear in the URL instead. -->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Student Information Form</title>
</head>
<body>
<h2>Student Information</h2>
<form action="studentInfo" method="post">
<p><label>Enrolment No: <input type="text" name="enrolmentNo" required></label></p>
<p><label>Name: <input type="text" name="name" required></label></p>
<p><label>Date of Birth: <input type="date" name="dob"></label></p>
<p><label>Email: <input type="email" name="email"></label></p>
<p><label>Mobile: <input type="tel" name="mobile" pattern="[0-9]{10}"></label></p>
<p><label>Programme:
<select name="programme">
<option>MCA</option>
<option>BCA</option>
<option>MSc</option>
</select></label></p>
<p>Courses:
<label><input type="checkbox" name="courses" value="MCS-218"> MCS-218</label>
<label><input type="checkbox" name="courses" value="MCS-219"> MCS-219</label>
<label><input type="checkbox" name="courses" value="MCS-220"> MCS-220</label>
<label><input type="checkbox" name="courses" value="MCS-221"> MCS-221</label>
</p>
<p><button type="submit">Submit</button> <button type="reset">Reset</button></p>
</form>
</body>
</html>package ignou;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/** Q2: reads the fields posted by student-form.html and echoes them. URL: /studentInfo */
@WebServlet("/studentInfo")
public class StudentInfoServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
show(request, response);
}
/** Also answer GET so the form can be switched to method="get" for comparison. */
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
show(request, response);
}
private void show(HttpServletRequest request, HttpServletResponse response)
throws IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
String[] courses = request.getParameterValues("courses");
String courseList = courses == null ? "none" : String.join(", ", courses);
try (PrintWriter out = response.getWriter()) {
out.println("<!DOCTYPE html><html><head><title>Student Details</title></head><body>");
out.println("<h2>Submitted Student Information</h2>");
out.println("<p>HTTP method: " + request.getMethod()
+ ", protocol: " + request.getProtocol()
+ ", content type: " + request.getContentType() + "</p>");
out.println("<table border='1' cellpadding='4'>");
row(out, "Enrolment No", request.getParameter("enrolmentNo"));
row(out, "Name", request.getParameter("name"));
row(out, "Date of Birth", request.getParameter("dob"));
row(out, "Email", request.getParameter("email"));
row(out, "Mobile", request.getParameter("mobile"));
row(out, "Programme", request.getParameter("programme"));
row(out, "Courses", courseList);
out.println("</table>");
out.println("<p><a href='student-form.html'>Back to form</a></p>");
out.println("</body></html>");
}
}
private static void row(PrintWriter out, String label, String value) {
out.println("<tr><th align='left'>" + label + "</th><td>" + esc(value) + "</td></tr>");
}
/** Escape user input before writing it back into HTML (stops script injection). */
private static String esc(String s) {
if (s == null) return "";
return s.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """);
}
}Output
Checked by reading, not executed. With POST the address bar shows /ServletLab/studentInfo and the page reads:
Submitted Student Information
HTTP method: POST, protocol: HTTP/1.1, content type: application/x-www-form-urlencoded
Enrolment No 2451001234
Name Asha Verma
Date of Birth 2001-03-14
Email asha.verma@example.com
Mobile 9876543210
Programme MCA
Courses MCS-218, MCS-220
Back to formWith GET the first line becomes HTTP method: GET, protocol: HTTP/1.1, content type: null and the address bar carries every field: studentInfo?enrolmentNo=2451001234&name=Asha+Verma&....
Explanation
- The form
action="studentInfo"is relative, so the browser resolves it against/ServletLab/; the servlet is mapped to/studentInfo. request.getParameter(name)returns one value;getParameterValues("courses")returns all ticked checkboxes as an array, ornullwhen none is ticked.request.getMethod()andgetProtocol()show the HTTP method and version, which is what the question means by “HTTP Protocol and method”. GET puts the fields in the query string (visible, bookmarkable, length-limited); POST puts them in the body, so passwords and long text belong in POST.esc()HTML-escapes the values before they are written back. Without it a name like<script>would run in the browser.setCharacterEncoding("UTF-8")before the firstgetParametercall makes Hindi or other non-ASCII names decode correctly.
Question 3
Problem Statement
Write in lab recordWrite a servlet program to capture client IPs and display it.
Solution
Write in lab recordSteps
- Add servlet
ClientIpServletin packageignou. - Run and open
http://localhost:8080/ServletLab/clientIpfrom the same machine, thenhttp://127.0.0.1:8080/ServletLab/clientIp, then from a phone on the same Wi-Fi using the PC’s LAN address (find it withipconfigorip addr).
Program
package ignou;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/** Q3: shows the client's IP address and related request details. URL: /clientIp */
@WebServlet("/clientIp")
public class ClientIpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
// Behind a proxy or load balancer the real client IP arrives in this header;
// on a direct localhost connection it is null.
String forwarded = request.getHeader("X-Forwarded-For");
String clientIp = forwarded != null ? forwarded.split(",")[0].trim() : request.getRemoteAddr();
try (PrintWriter out = response.getWriter()) {
out.println("<!DOCTYPE html><html><head><title>Client IP</title></head><body>");
out.println("<h2>Client Information</h2>");
out.println("<p>Client IP address: <b>" + clientIp + "</b></p>");
out.println("<p>request.getRemoteAddr(): " + request.getRemoteAddr() + "</p>");
out.println("<p>request.getRemoteHost(): " + request.getRemoteHost() + "</p>");
out.println("<p>request.getRemotePort(): " + request.getRemotePort() + "</p>");
out.println("<p>X-Forwarded-For header: " + forwarded + "</p>");
out.println("<p>Server name and port: " + request.getServerName() + ":" + request.getServerPort() + "</p>");
out.println("<p>User-Agent: " + request.getHeader("User-Agent") + "</p>");
out.println("</body></html>");
}
}
}Output
Checked by reading. From the same PC using localhost:
Client Information
Client IP address: 0:0:0:0:0:0:0:1
request.getRemoteAddr(): 0:0:0:0:0:0:0:1
request.getRemoteHost(): 0:0:0:0:0:0:0:1
request.getRemotePort(): 53412
X-Forwarded-For header: null
Server name and port: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/129.0Using 127.0.0.1 in the URL the first two lines show 127.0.0.1; from a phone they show something like 192.168.1.7.
Explanation
getRemoteAddr()is the address of the TCP peer that connected to Tomcat. On localhost that is the IPv6 loopback0:0:0:0:0:0:0:1on most Windows builds, because the browser prefers IPv6 forlocalhost.- When a reverse proxy or load balancer sits in front of Tomcat, the peer is the proxy, and the original client is in the
X-Forwarded-Forheader (a comma list, first entry is the client). The servlet prefers that header when present. Trust it only when you control the proxy; anyone can send that header. getRemoteHost()returns a host name only if Tomcat’senableLookupsis on; by default it just repeats the IP.getRemotePort()is the client’s ephemeral port, different for every connection.
Question 4
Problem Statement
Write in lab recordWrite a servlet program for session management using HTTP Session along with tracking and also use a cookie for session tracking.
Solution
Write in lab recordSteps
- Add servlet
SessionTrackingServletin packageignou. - Run and open
http://localhost:8080/ServletLab/session. Reload three times and watch “Visits in this session” count up. - Type a name, click “Remember me”. The name appears in the heading and in the
visitorNamerow. - Open the browser dev tools, Application, Cookies: you see
JSESSIONID(session cookie, no expiry) andvisitorName(expires in 7 days). - Click Logout: the visit count restarts at 1, the name is gone, and the Session ID is new.
- Block cookies for
localhostin the browser and reload twice: “Session id came from cookie?” turns false, and the “Reload” link now contains;jsessionid=...becauseencodeURLswitched to URL rewriting.
Program
package ignou;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
/**
* Q4: session management with HttpSession plus a cookie that survives the session.
* URL: /session shows counters and session details
* URL: /session?action=logout invalidates the session and deletes the cookie
*/
@WebServlet("/session")
public class SessionTrackingServlet extends HttpServlet {
private static final String NAME_COOKIE = "visitorName";
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if ("logout".equals(request.getParameter("action"))) {
HttpSession old = request.getSession(false);
if (old != null) old.invalidate();
Cookie gone = new Cookie(NAME_COOKIE, "");
gone.setMaxAge(0); // max age 0 tells the browser to delete it
gone.setPath(request.getContextPath());
response.addCookie(gone);
response.sendRedirect(request.getContextPath() + "/session");
return;
}
HttpSession session = request.getSession(); // creates one on the first visit
Integer visits = (Integer) session.getAttribute("visits");
visits = visits == null ? 1 : visits + 1;
session.setAttribute("visits", visits);
String name = readCookie(request, NAME_COOKIE);
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
out.println("<!DOCTYPE html><html><head><title>Session Tracking</title></head><body>");
out.println("<h2>Welcome " + (name == null ? "guest" : esc(name)) + "</h2>");
out.println("<table border='1' cellpadding='4'>");
out.println("<tr><th align='left'>Session ID</th><td>" + session.getId() + "</td></tr>");
out.println("<tr><th align='left'>New session?</th><td>" + session.isNew() + "</td></tr>");
out.println("<tr><th align='left'>Created</th><td>" + new Date(session.getCreationTime()) + "</td></tr>");
out.println("<tr><th align='left'>Last accessed</th><td>" + new Date(session.getLastAccessedTime()) + "</td></tr>");
out.println("<tr><th align='left'>Timeout (s)</th><td>" + session.getMaxInactiveInterval() + "</td></tr>");
out.println("<tr><th align='left'>Visits in this session</th><td>" + visits + "</td></tr>");
out.println("<tr><th align='left'>Session id came from cookie?</th><td>"
+ request.isRequestedSessionIdFromCookie() + "</td></tr>");
out.println("<tr><th align='left'>Session id came from URL?</th><td>"
+ request.isRequestedSessionIdFromURL() + "</td></tr>");
out.println("<tr><th align='left'>visitorName cookie</th><td>" + esc(name) + "</td></tr>");
out.println("</table>");
out.println("<form method='post'><p>Your name: <input name='name' required> "
+ "<button type='submit'>Remember me (cookie, 7 days)</button></p></form>");
// encodeURL appends ;jsessionid=... only when the browser refused the session cookie
out.println("<p><a href='" + response.encodeURL("session") + "'>Reload (URL rewriting aware)</a> | "
+ "<a href='session?action=logout'>Logout</a></p>");
out.println("</body></html>");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String name = request.getParameter("name");
Cookie c = new Cookie(NAME_COOKIE, java.net.URLEncoder.encode(name, "UTF-8"));
c.setMaxAge(7 * 24 * 60 * 60);
c.setPath(request.getContextPath());
c.setHttpOnly(true);
response.addCookie(c);
request.getSession().setAttribute("name", name);
response.sendRedirect(request.getContextPath() + "/session");
}
private static String readCookie(HttpServletRequest request, String key) throws IOException {
Cookie[] cookies = request.getCookies();
if (cookies == null) return null;
for (Cookie c : cookies) {
if (key.equals(c.getName())) return java.net.URLDecoder.decode(c.getValue(), "UTF-8");
}
return null;
}
private static String esc(String s) {
if (s == null) return "";
return s.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """);
}
}Output
Checked by reading. After two reloads and “Remember me” with the name Asha:
Welcome Asha
Session ID 5F3A0C1E9B7D4E2A8C6F1B0D9E8A7C5B
New session? false
Created Sat Sep 26 10:50:02 IST 2026
Last accessed Sat Sep 26 10:50:41 IST 2026
Timeout (s) 1800
Visits in this session 3
Session id came from cookie? true
Session id came from URL? false
visitorName cookie Asha
Your name: [ ] [Remember me (cookie, 7 days)]
Reload (URL rewriting aware) | LogoutExplanation
- HTTP is stateless.
request.getSession()makes Tomcat create anHttpSessionobject on the server and send its id to the browser in theJSESSIONIDcookie; every later request carries the cookie, so Tomcat finds the same object. Thevisitsattribute lives in that object, not in the browser. - The session cookie has no expiry, so it dies when the browser closes; the session itself dies after 30 minutes of inactivity (
session-timeoutinweb.xml, shown as 1800 seconds). - The
visitorNamecookie is the second tracking mechanism: it is stored by the browser withsetMaxAge(7 days), so it survives browser restarts and evensession.invalidate(). Logout deletes it by sending the same cookie with max age 0. response.encodeURL()is the fallback when cookies are refused: it appends;jsessionid=to links so the id travels in the URL.isRequestedSessionIdFromCookie()andisRequestedSessionIdFromURL()show which route was used.- Cookie values may not contain spaces or semicolons, so the name is URL-encoded before storing and decoded when read.
setHttpOnly(true)keeps JavaScript from reading it.
Question 5
Problem Statement
Write in lab recordWrite a CRUD (Create/Save, Read, Edit/Update, Delete) application using servlet. Create a Database named IGNOU, create a table named Student which must capture the student information (basics, contact, enrollment details along with courses). Make necessary assumptions required.
Solution
Write in lab recordAssumptions
- Enrolment number is the primary key (IGNOU issues a unique 9 to 12 digit number).
- Courses are stored in one column as a comma-separated list of course codes. A student registers for a handful of courses, so a separate Course table and join table would add two more screens without teaching anything new for this session. Session 6 (Hibernate) is the place to normalise it.
- One application login
ignou/ignou123with only SELECT, INSERT, UPDATE and DELETE rights on the IGNOU database. - MySQL 8 runs on
localhost:3306.
Steps
- Start MySQL and run
schema.sqlin MySQL Workbench (File, Open SQL Script, then the lightning-bolt Execute button) or withmysql -u root -pthensource schema.sql. Check withSELECT * FROM Student;that three rows exist. - Add
Student.java,StudentDao.javaandStudentServlet.javato packageignou. Themysql-connector-jdependency is already inpom.xml. - Run and open
http://localhost:8080/ServletLab/students. - Read: the list shows the three seeded rows. Create: click “Add new student”, fill the form, Save. Update: click Edit on a row, change the mobile, Save. Delete: click Delete, confirm.
- Verify each step in Workbench with
SELECT enrolment_no, name, mobile FROM Student;.
Program
Lab record: every tab is one file of the answer. Write all of them.
-- Q5: IGNOU database and Student table.
-- Run in MySQL Workbench or: mysql -u root -p < schema.sql
-- One table holds basics, contact and enrolment details; the courses column is a
-- comma-separated list of course codes (assumption: a student registers for at most
-- a handful of courses, so a separate Course table is not needed for this lab).
CREATE DATABASE IF NOT EXISTS IGNOU CHARACTER SET utf8mb4;
USE IGNOU;
DROP TABLE IF EXISTS Student;
CREATE TABLE Student (
-- basics
enrolment_no VARCHAR(12) NOT NULL,
name VARCHAR(80) NOT NULL,
dob DATE NOT NULL,
gender ENUM('M', 'F', 'O') NOT NULL DEFAULT 'M',
-- contact
email VARCHAR(120) NOT NULL,
mobile CHAR(10) NOT NULL,
address VARCHAR(200),
city VARCHAR(60),
state VARCHAR(60),
pincode CHAR(6),
-- enrolment details
programme VARCHAR(10) NOT NULL, -- MCA, BCA, MSc ...
semester TINYINT NOT NULL DEFAULT 1,
admission_year YEAR NOT NULL,
study_centre VARCHAR(10), -- e.g. 0710
courses VARCHAR(200), -- 'MCS-218,MCS-219,MCS-220'
PRIMARY KEY (enrolment_no),
UNIQUE KEY uq_student_email (email),
CONSTRAINT chk_mobile CHECK (mobile REGEXP '^[0-9]{10}$'),
CONSTRAINT chk_semester CHECK (semester BETWEEN 1 AND 6)
);
-- Application login used by the servlet: create once, grant only what the lab needs.
CREATE USER IF NOT EXISTS 'ignou'@'localhost' IDENTIFIED BY 'ignou123';
GRANT SELECT, INSERT, UPDATE, DELETE ON IGNOU.* TO 'ignou'@'localhost';
INSERT INTO Student (enrolment_no, name, dob, gender, email, mobile, address, city, state, pincode,
programme, semester, admission_year, study_centre, courses) VALUES
('2451001234', 'Asha Verma', '2001-03-14', 'F', 'asha.verma@example.com', '9876543210', '12 MG Road', 'Jaipur', 'Rajasthan', '302001', 'MCA', 2, 2024, '0710', 'MCS-218,MCS-219,MCS-220,MCS-221'),
('2451001235', 'Rahul Singh', '2000-11-02', 'M', 'rahul.singh@example.com', '9123456780', '4 Park Street', 'Kolkata', 'West Bengal', '700016', 'MCA', 2, 2024, '2801', 'MCS-218,MCS-220,MCS-221'),
('2451001236', 'Meera Nair', '2002-07-25', 'F', 'meera.nair@example.com', '9988776655', '9 Beach Road', 'Kochi', 'Kerala', '682001', 'MCA', 1, 2025, '1401', 'MCS-211,MCS-212,MCS-213');
SELECT enrolment_no, name, programme, semester, courses FROM Student;package ignou;
/** One row of the Student table. Plain data holder used by the DAO, servlets and JSPs. */
public class Student {
private String enrolmentNo;
private String name;
private String dob; // yyyy-MM-dd, the format <input type="date"> and MySQL both use
private String gender;
private String email;
private String mobile;
private String address;
private String city;
private String state;
private String pincode;
private String programme;
private int semester;
private int admissionYear;
private String studyCentre;
private String courses; // comma-separated course codes
public String getEnrolmentNo() { return enrolmentNo; }
public void setEnrolmentNo(String enrolmentNo) { this.enrolmentNo = enrolmentNo; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDob() { return dob; }
public void setDob(String dob) { this.dob = dob; }
public String getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getMobile() { return mobile; }
public void setMobile(String mobile) { this.mobile = mobile; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public String getState() { return state; }
public void setState(String state) { this.state = state; }
public String getPincode() { return pincode; }
public void setPincode(String pincode) { this.pincode = pincode; }
public String getProgramme() { return programme; }
public void setProgramme(String programme) { this.programme = programme; }
public int getSemester() { return semester; }
public void setSemester(int semester) { this.semester = semester; }
public int getAdmissionYear() { return admissionYear; }
public void setAdmissionYear(int admissionYear) { this.admissionYear = admissionYear; }
public String getStudyCentre() { return studyCentre; }
public void setStudyCentre(String studyCentre) { this.studyCentre = studyCentre; }
public String getCourses() { return courses; }
public void setCourses(String courses) { this.courses = courses; }
}package ignou;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* Data access for the Student table (IGNOU database). Every query uses a
* PreparedStatement, so values are sent separately from the SQL text and a
* quote in a name cannot break or hijack the statement.
*/
public class StudentDao {
private static final String URL =
"jdbc:mysql://localhost:3306/IGNOU?useSSL=false&serverTimezone=Asia/Kolkata";
private static final String USER = "ignou";
private static final String PASSWORD = "ignou123";
private static final String COLUMNS =
"enrolment_no, name, dob, gender, email, mobile, address, city, state, pincode, "
+ "programme, semester, admission_year, study_centre, courses";
// Connector/J 8 registers itself through META-INF/services; no Class.forName needed.
private Connection connect() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
public List<Student> findAll() throws SQLException {
List<Student> list = new ArrayList<>();
String sql = "SELECT " + COLUMNS + " FROM Student ORDER BY enrolment_no";
try (Connection con = connect();
PreparedStatement ps = con.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) list.add(map(rs));
}
return list;
}
public Student findById(String enrolmentNo) throws SQLException {
String sql = "SELECT " + COLUMNS + " FROM Student WHERE enrolment_no = ?";
try (Connection con = connect();
PreparedStatement ps = con.prepareStatement(sql)) {
ps.setString(1, enrolmentNo);
try (ResultSet rs = ps.executeQuery()) {
return rs.next() ? map(rs) : null;
}
}
}
public int insert(Student s) throws SQLException {
String sql = "INSERT INTO Student (" + COLUMNS + ") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
try (Connection con = connect();
PreparedStatement ps = con.prepareStatement(sql)) {
ps.setString(1, s.getEnrolmentNo());
bind(ps, 2, s);
return ps.executeUpdate();
}
}
public int update(Student s) throws SQLException {
String sql = "UPDATE Student SET name=?, dob=?, gender=?, email=?, mobile=?, address=?, "
+ "city=?, state=?, pincode=?, programme=?, semester=?, admission_year=?, "
+ "study_centre=?, courses=? WHERE enrolment_no=?";
try (Connection con = connect();
PreparedStatement ps = con.prepareStatement(sql)) {
bind(ps, 1, s);
ps.setString(15, s.getEnrolmentNo());
return ps.executeUpdate();
}
}
public int delete(String enrolmentNo) throws SQLException {
try (Connection con = connect();
PreparedStatement ps = con.prepareStatement("DELETE FROM Student WHERE enrolment_no = ?")) {
ps.setString(1, enrolmentNo);
return ps.executeUpdate();
}
}
/** Binds the 14 non-key columns starting at parameter index {@code from}. */
private static void bind(PreparedStatement ps, int from, Student s) throws SQLException {
ps.setString(from, s.getName());
ps.setString(from + 1, s.getDob());
ps.setString(from + 2, s.getGender());
ps.setString(from + 3, s.getEmail());
ps.setString(from + 4, s.getMobile());
ps.setString(from + 5, s.getAddress());
ps.setString(from + 6, s.getCity());
ps.setString(from + 7, s.getState());
ps.setString(from + 8, s.getPincode());
ps.setString(from + 9, s.getProgramme());
ps.setInt(from + 10, s.getSemester());
ps.setInt(from + 11, s.getAdmissionYear());
ps.setString(from + 12, s.getStudyCentre());
ps.setString(from + 13, s.getCourses());
}
private static Student map(ResultSet rs) throws SQLException {
Student s = new Student();
s.setEnrolmentNo(rs.getString("enrolment_no"));
s.setName(rs.getString("name"));
s.setDob(rs.getString("dob"));
s.setGender(rs.getString("gender"));
s.setEmail(rs.getString("email"));
s.setMobile(rs.getString("mobile"));
s.setAddress(rs.getString("address"));
s.setCity(rs.getString("city"));
s.setState(rs.getString("state"));
s.setPincode(rs.getString("pincode"));
s.setProgramme(rs.getString("programme"));
s.setSemester(rs.getInt("semester"));
s.setAdmissionYear(rs.getInt("admission_year"));
s.setStudyCentre(rs.getString("study_centre"));
s.setCourses(rs.getString("courses"));
return s;
}
}package ignou;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.List;
/**
* Q5: CRUD front controller for the Student table.
* GET /students list
* GET /students?action=new empty form
* GET /students?action=edit&id=E form filled from the database
* POST /students action=insert | update | delete
*/
@WebServlet("/students")
public class StudentServlet extends HttpServlet {
private final StudentDao dao = new StudentDao();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String action = request.getParameter("action");
try {
if ("new".equals(action)) {
renderForm(response, new Student(), false);
} else if ("edit".equals(action)) {
Student s = dao.findById(request.getParameter("id"));
if (s == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such student");
return;
}
renderForm(response, s, true);
} else {
renderList(response, dao.findAll(), request.getParameter("msg"));
}
} catch (SQLException e) {
throw new ServletException("Database error: " + e.getMessage(), e);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String action = request.getParameter("action");
String msg;
try {
switch (action == null ? "" : action) {
case "insert" -> { dao.insert(fromRequest(request)); msg = "Student added"; }
case "update" -> { dao.update(fromRequest(request)); msg = "Student updated"; }
case "delete" -> { dao.delete(request.getParameter("id")); msg = "Student deleted"; }
default -> msg = "Unknown action";
}
} catch (SQLException e) {
msg = "Database error: " + e.getMessage(); // e.g. duplicate enrolment number
}
// Redirect after POST so a browser refresh does not repeat the insert.
response.sendRedirect("students?msg=" + java.net.URLEncoder.encode(msg, "UTF-8"));
}
private static Student fromRequest(HttpServletRequest r) {
Student s = new Student();
s.setEnrolmentNo(r.getParameter("enrolmentNo").trim());
s.setName(r.getParameter("name").trim());
s.setDob(r.getParameter("dob"));
s.setGender(r.getParameter("gender"));
s.setEmail(r.getParameter("email").trim());
s.setMobile(r.getParameter("mobile").trim());
s.setAddress(r.getParameter("address"));
s.setCity(r.getParameter("city"));
s.setState(r.getParameter("state"));
s.setPincode(r.getParameter("pincode"));
s.setProgramme(r.getParameter("programme"));
s.setSemester(Integer.parseInt(r.getParameter("semester")));
s.setAdmissionYear(Integer.parseInt(r.getParameter("admissionYear")));
s.setStudyCentre(r.getParameter("studyCentre"));
String[] courses = r.getParameterValues("courses");
s.setCourses(courses == null ? "" : String.join(",", courses));
return s;
}
private void renderList(HttpServletResponse response, List<Student> list, String msg) throws IOException {
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
out.println("<!DOCTYPE html><html><head><title>Students</title></head><body>");
out.println("<h2>IGNOU Students (" + list.size() + ")</h2>");
if (msg != null) out.println("<p style='color:green'>" + esc(msg) + "</p>");
out.println("<p><a href='students?action=new'>Add new student</a></p>");
out.println("<table border='1' cellpadding='4'><tr><th>Enrolment</th><th>Name</th><th>DOB</th>"
+ "<th>Email</th><th>Mobile</th><th>Programme</th><th>Sem</th><th>Courses</th><th>Actions</th></tr>");
for (Student s : list) {
out.println("<tr><td>" + esc(s.getEnrolmentNo()) + "</td><td>" + esc(s.getName()) + "</td><td>"
+ s.getDob() + "</td><td>" + esc(s.getEmail()) + "</td><td>" + esc(s.getMobile())
+ "</td><td>" + esc(s.getProgramme()) + "</td><td>" + s.getSemester() + "</td><td>"
+ esc(s.getCourses()) + "</td><td>"
+ "<a href='students?action=edit&id=" + esc(s.getEnrolmentNo()) + "'>Edit</a> "
+ "<form method='post' style='display:inline' onsubmit='return confirm(\"Delete?\")'>"
+ "<input type='hidden' name='action' value='delete'>"
+ "<input type='hidden' name='id' value='" + esc(s.getEnrolmentNo()) + "'>"
+ "<button type='submit'>Delete</button></form></td></tr>");
}
out.println("</table></body></html>");
}
}
private void renderForm(HttpServletResponse response, Student s, boolean editing) throws IOException {
response.setContentType("text/html;charset=UTF-8");
String[] allCourses = {"MCS-218", "MCS-219", "MCS-220", "MCS-221", "MCSL-222", "MCSL-223"};
String chosen = s.getCourses() == null ? "" : "," + s.getCourses() + ",";
try (PrintWriter out = response.getWriter()) {
out.println("<!DOCTYPE html><html><head><title>Student Form</title></head><body>");
out.println("<h2>" + (editing ? "Edit" : "Add") + " Student</h2>");
out.println("<form method='post' action='students'>");
out.println("<input type='hidden' name='action' value='" + (editing ? "update" : "insert") + "'>");
field(out, "Enrolment No", "enrolmentNo", s.getEnrolmentNo(), editing ? "readonly" : "required");
field(out, "Name", "name", s.getName(), "required");
out.println("<p><label>Date of Birth: <input type='date' name='dob' value='" + esc(s.getDob()) + "' required></label></p>");
out.println("<p>Gender: <label><input type='radio' name='gender' value='M' " + ("F".equals(s.getGender()) || "O".equals(s.getGender()) ? "" : "checked") + "> Male</label> "
+ "<label><input type='radio' name='gender' value='F' " + ("F".equals(s.getGender()) ? "checked" : "") + "> Female</label> "
+ "<label><input type='radio' name='gender' value='O' " + ("O".equals(s.getGender()) ? "checked" : "") + "> Other</label></p>");
field(out, "Email", "email", s.getEmail(), "type='email' required");
field(out, "Mobile", "mobile", s.getMobile(), "pattern='[0-9]{10}' required");
field(out, "Address", "address", s.getAddress(), "");
field(out, "City", "city", s.getCity(), "");
field(out, "State", "state", s.getState(), "");
field(out, "Pincode", "pincode", s.getPincode(), "pattern='[0-9]{6}'");
field(out, "Programme", "programme", s.getProgramme() == null ? "MCA" : s.getProgramme(), "required");
field(out, "Semester", "semester", s.getSemester() == 0 ? "1" : String.valueOf(s.getSemester()), "type='number' min='1' max='6'");
field(out, "Admission Year", "admissionYear", s.getAdmissionYear() == 0 ? "2024" : String.valueOf(s.getAdmissionYear()), "type='number' min='2000' max='2099'");
field(out, "Study Centre", "studyCentre", s.getStudyCentre(), "");
out.println("<p>Courses:");
for (String c : allCourses) {
out.println("<label><input type='checkbox' name='courses' value='" + c + "' "
+ (chosen.contains("," + c + ",") ? "checked" : "") + "> " + c + "</label>");
}
out.println("</p><p><button type='submit'>Save</button> <a href='students'>Cancel</a></p>");
out.println("</form></body></html>");
}
}
private static void field(PrintWriter out, String label, String name, String value, String extra) {
out.println("<p><label>" + label + ": <input name='" + name + "' value='" + esc(value) + "' " + extra + "></label></p>");
}
private static String esc(String s) {
if (s == null) return "";
return s.replace("&", "&").replace("<", "<").replace(">", ">")
.replace("\"", """).replace("'", "'");
}
}Output
Checked by reading; the SQL was checked against MySQL 8 syntax. After adding a fourth student the list page reads:
IGNOU Students (4)
Student added
Add new student
Enrolment Name DOB Email Mobile Programme Sem Courses Actions
2451001234 Asha Verma 2001-03-14 asha.verma@example.com 9876543210 MCA 2 MCS-218,MCS-219,MCS-220,MCS-221 Edit [Delete]
2451001235 Rahul Singh 2000-11-02 rahul.singh@example.com 9123456780 MCA 2 MCS-218,MCS-220,MCS-221 Edit [Delete]
2451001236 Meera Nair 2002-07-25 meera.nair@example.com 9988776655 MCA 1 MCS-211,MCS-212,MCS-213 Edit [Delete]
2451001237 Vikram Rao 2001-08-30 vikram.rao@example.com 9012345678 MCA 2 MCS-218,MCSL-222 Edit [Delete]Submitting the add form a second time with the same enrolment number shows, in green text at the top of the list, Database error: Duplicate entry '2451001237' for key 'student.PRIMARY'. After Delete the count drops to 3 and the message reads Student deleted.
Explanation
- Three layers:
Student(data),StudentDao(SQL),StudentServlet(HTTP and HTML). The servlet never builds SQL; the DAO never touches the request. That is the split every later session (Spring, Hibernate) formalises. PreparedStatementsends the SQL text with?placeholders first and the values separately, so a name containing a quote cannot change the statement (SQL injection).setString,setIntalso handle quoting and type conversion.- Every DAO method opens and closes its own connection through
trywith resources. That is fine for a lab; a real application uses a connection pool (Tomcat’s JNDIDataSource). - The servlet is a front controller: the
actionparameter selects list, new, edit, insert, update or delete. Reads are GET; writes are POST, and every POST ends insendRedirectback to the list (Post-Redirect-Get), so pressing F5 on the list page never re-inserts a row. - Delete is a tiny POST form rather than a link, because browsers and crawlers prefetch links; a GET that deletes data is a classic bug.
- The
CHECKconstraints inschema.sqlcatch bad mobile numbers and semesters even if someone bypasses the HTMLpatternattributes; the database is the last line of validation.
Viva Questions
Do not copy. Read for understanding and the viva- Q: What is the servlet life cycle? A: The container loads the class, calls
init()once, callsservice()(which dispatches todoGet,doPost) for every request on a pooled thread, and callsdestroy()once at undeploy. - Q: Why
jakarta.servletand notjavax.servlet? A: Tomcat 10 implements Jakarta EE 9+, where every package was renamed fromjavax.*tojakarta.*. Code withjavax.servletimports compiles but Tomcat 10 never calls it. - Q: Difference between
@WebServletandweb.xmlmapping? A: Same effect; the annotation keeps the mapping next to the code,web.xmllets you change it without recompiling and is needed for container-wide settings like session timeout and error pages. - Q: GET versus POST? A: GET carries parameters in the URL, is idempotent and cacheable; POST carries them in the body and is used for anything that changes state.
- Q: How does Tomcat know which session belongs to which browser? A: By the
JSESSIONIDcookie, or by;jsessionid=in the URL when cookies are off. - Q: Where is session data stored, browser or server? A: On the server, in the
HttpSessionobject; the browser only holds the id. - Q: Why
PreparedStatementinstead ofStatement? A: Parameters are bound, not concatenated, so user input cannot alter the SQL; the driver can also cache the compiled statement. - Q: What does Post-Redirect-Get solve? A: A browser refresh after a POST re-sends the form; redirecting to a GET page after the write prevents duplicate inserts.
Common Mistakes
Do not copy. Read for understanding and the viva- Importing
javax.servlet.*: the project compiles against an old jar but Tomcat 10 returns 404 for the servlet. - Calling
getWriter()beforesetContentType(): the charset header is ignored and non-ASCII text appears garbled. - Forgetting
mysql-connector-jinWEB-INF/lib(orpom.xml):No suitable driver found for jdbc:mysql://...at run time. - Leaving the MySQL server stopped or the
IGNOUschema uncreated:Communications link failureorUnknown database 'ignou'. - Deleting through a GET link, or forgetting to redirect after POST, so a refresh repeats the write.
- Writing user input straight into HTML without escaping, which lets a
<script>in the name field run in every viewer’s browser.
Session Summary
Write in lab record- Project
ServletLabon Tomcat 10.1 withpom.xml(jakarta.servlet-api, JSTL, mysql-connector-j) andweb.xml - Question 1:
DateTimeServletat/datetimeprinting date, time and millisecond timestamp - Question 2:
student-form.htmlposting toStudentInfoServletat/studentInfo, showing method and protocol - Question 3:
ClientIpServletat/clientIpwithgetRemoteAddrandX-Forwarded-For - Question 4:
SessionTrackingServletat/sessionwithHttpSessionvisit counter,visitorNamecookie, logout and URL rewriting - Question 5:
schema.sql(IGNOU database, Student table),Student,StudentDaowithPreparedStatement,StudentServletCRUD at/students