feat: add filters

This commit is contained in:
Sergey Krylov 2026-02-10 05:23:39 +03:00
parent 4cc5696788
commit a9c5268c46
19 changed files with 340 additions and 172 deletions

Binary file not shown.

Binary file not shown.

BIN
lib/servlet-api.jar Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
<!DOCTYPE html>
<div>
<hr>
<h3>All rights reserved 2024</h3>
</div>

View File

@ -0,0 +1,5 @@
<%@ page contentType="text/html;charset=UTF-8" %>
<div>
<h3>Charm <3</h3>
<hr>
</div>

View File

@ -0,0 +1,60 @@
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html lang="en">
<head>
<title>Charm Profile</title>
</head>
<body>
<%@ include file="header.jsp" %>
<div>
<c:if test="${requestScope.profile.id == null}">
<h1>Hello new user!</h1>
</c:if>
<form method="post" action="/profile">
<c:if test="${requestScope.profile.id != null}">
<input hidden name="_method" value="PUT">
</c:if>
<input hidden type="text" name="id" value="${requestScope.profile.id}">
<table>
<tr>
<td><h3>Email</h3></td>
<td><input type="email" name="email" value="${requestScope.profile.email}"></td>
</tr>
<tr>
<td><h3>Name</h3></td>
<td><input type="text" name="name" value="${requestScope.profile.name}"></td>
</tr>
<tr>
<td><h3>Surname</h3></td>
<td><input type="text" name="surname" value="${requestScope.profile.surname}"></td>
</tr>
<tr>
<td><h3>About</h3></td>
<td><input type="text" name="about" value="${requestScope.profile.about}"></td>
</tr>
<tr>
<td><h3>Gender</h3></td>
<td>
<select name="gender">
<option value="${requestScope.profile.gender}" selected hidden>${requestScope.profile.gender}</option>
<c:forEach var="gender" items="${applicationScope.genders}">
<option value="${gender}">${gender}</option>
</c:forEach>
</select>
</td>
</tr>
</table>
<button type="submit">Save</button>
</form>
<c:if test="${requestScope.profile.id != null}">
<form action="/profile" method="post">
<input hidden name="_method" value="DELETE">
<input hidden name="id" value="${requestScope.profile.id}">
<button type="submit">Delete</button>
</form>
</c:if>
</div>
<%@ include file="footer.html" %>
</body>
</html>

23
resources/WEB-INF/web.xml Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<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">
</web-app>

26
resources/index.html Normal file
View File

@ -0,0 +1,26 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Charm</title>
</head>
<body>
<h3>Hello from Charm!</H3>
</body>
</html>

View File

@ -1,27 +0,0 @@
package ru.charm.back;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
public class CharmBackClientRunner {
public static void main(String[] args) throws IOException {
try (
Socket socket = new Socket("localhost", 8080);
DataInputStream rsStream = new DataInputStream(socket.getInputStream());
DataOutputStream rqStream = new DataOutputStream(socket.getOutputStream());
Scanner scanner = new Scanner(System.in)
) {
while (scanner.hasNextLine()) {
System.out.println("Client: ");
String request = scanner.nextLine();
rqStream.writeUTF(request);
String response = rsStream.readUTF();
System.out.println("Server: " + response);
}
}
}
}

View File

@ -1,49 +0,0 @@
package ru.charm.back;
import ru.charm.back.controller.ProfileController;
import ru.charm.back.dao.ProfileDao;
import ru.charm.back.model.Command;
import ru.charm.back.service.ProfileService;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class CharmBackServerRunner {
public static void main(String[] args) throws IOException {
ProfileController controller = new ProfileController(new ProfileService(new ProfileDao()));
try (
ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = serverSocket.accept();
DataInputStream rqStream = new DataInputStream(socket.getInputStream());
DataOutputStream rsStream = new DataOutputStream(socket.getOutputStream());
) {
String request = rqStream.readUTF();
String response;
while (!request.equals("stop")) {
if (request.startsWith(Command.SAVE.getPrefix())) {
response = controller.save(request.split("save ")[1]);
} else if (request.startsWith(Command.FIND_BY_ID.getPrefix())) {
response = controller.findById(request.split("findById ")[1]);
} else if (request.startsWith(Command.FIND_ALL.getPrefix())) {
response = controller.findAll();
} else if (request.startsWith(Command.UPDATE.getPrefix())) {
response = controller.update(request.split("update ")[1]);
} else if (request.startsWith(Command.DELETE.getPrefix())) {
response = controller.delete(request.split("delete ")[1]);
} else {
response = "Unsupported method";
}
rsStream.writeUTF(response);
request = rqStream.readUTF();
System.out.println("Server: " + request);
}
}
}
}

View File

@ -0,0 +1,19 @@
package ru.charm.back.controller;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/forward")
public class ForwardController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.sendRedirect("https://ya.ru");
}
}

View File

@ -0,0 +1,33 @@
package ru.charm.back.controller;
import jakarta.servlet.ServletConfig;
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;
@WebServlet("/like")
public class LikeController extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("LikeController init" + config.getServletName() );
super.init(config);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain");
System.out.println("User-Agent: " + req.getHeader("User-Agent"));
resp.getWriter().write("Like count: 101");
resp.setHeader("X-Custom-Header", "Zalupa");
}
@Override
public void destroy() {
System.out.println("LikeController destroy");
super.destroy();
}
}

View File

@ -1,112 +1,72 @@
package ru.charm.back.controller;
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 ru.charm.back.model.Gender;
import ru.charm.back.model.Profile;
import ru.charm.back.service.ProfileService;
import java.io.IOException;
import java.util.Optional;
public class ProfileController {
private final ProfileService service;
@WebServlet("/profile")
public class ProfileController extends HttpServlet {
private final ProfileService service = ProfileService.getInstance();
public ProfileController(ProfileService service) {
this.service = service;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String sId = req.getParameter("id");
String forwardUri = "/notFound";
if (sId != null) {
Optional<Profile> profile = service.findById(Long.parseLong(sId));
if (profile.isPresent()) {
req.setAttribute("profile", profile.get());
forwardUri = "/WEB-INF/jsp/profile.jsp";
}
}
req.getRequestDispatcher(forwardUri).forward(req, resp);
}
public String work(String request) {
return "";
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Profile profile = getProfile(req);
service.save(profile);
resp.sendRedirect(String.format("/profile?id=%s", profile.getId()));
}
public String save(String save) {
String[] params = save.split(",");
if (params.length != 4) {
return "Bad request";
}
Profile profile = new Profile();
profile.setEmail(params[0]);
profile.setName(params[1]);
profile.setSurname(params[2]);
profile.setAbout(params[3]);
return service.save(profile).toString();
}
public String findById(String request) {
String[] params = request.split(",");
if (params.length != 1) {
return "Bad request";
}
long id;
try {
id = Long.parseLong(params[0]);
} catch (NumberFormatException e) {
return "Bad request";
}
Optional<Profile> maybeProfile = service.findById(id);
if (maybeProfile.isEmpty()) {
return "Not found";
}
return maybeProfile.get().toString();
}
public String findAll() {
return service.findAll().toString();
}
public String update(String request) {
String[] params = request.split(",");
if (params.length != 5) {
return "Bad request";
}
long id;
try {
id = Long.parseLong(params[0]);
} catch (NumberFormatException e) {
return "Bad request";
}
Profile profile = new Profile();
profile.setId(id);
profile.setEmail(params[1]);
profile.setName(params[2]);
profile.setSurname(params[3]);
profile.setAbout(params[4]);
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Profile profile = getProfile(req);
service.update(profile);
resp.sendRedirect(String.format("/profile?id=%s", profile.getId()));
return "Update success\n" + profile.toString();
}
public String delete(String request) {
String[] params = request.split(",");
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String sId = req.getParameter("id");
if (!sId.isBlank()) {
service.delete(Long.parseLong(sId));
}
resp.sendRedirect("/registration");
}
if (params.length != 1) {
return "Bad request";
private Profile getProfile(HttpServletRequest req) {
Profile profile = new Profile();
String sId = req.getParameter("id");
if (!sId.isBlank()) {
profile.setId(Long.parseLong(sId));
}
long id;
try {
id = Long.parseLong(params[0]);
} catch (NumberFormatException e) {
return "Bad request";
}
Optional<Profile> maybeProfile = service.findById(id);
if (maybeProfile.isEmpty()) {
return "Not found";
}
service.delete(id);
return "Delete success";
profile.setEmail(req.getParameter("email"));
profile.setName(req.getParameter("name"));
profile.setSurname(req.getParameter("surname"));
profile.setAbout(req.getParameter("about"));
profile.setGender(Gender.valueOf(req.getParameter("gender")));
return profile;
}
}

View File

@ -0,0 +1,19 @@
package ru.charm.back.controller;
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 ru.charm.back.model.Profile;
import java.io.IOException;
@WebServlet("/registration")
public class RegistrationController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("profile", new Profile());
req.getRequestDispatcher("/WEB-INF/jsp/profile.jsp").forward(req, resp);
}
}

View File

@ -0,0 +1,54 @@
package ru.charm.back.controller.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import ru.charm.back.model.Gender;
import java.io.IOException;
import java.util.Locale;
@WebFilter("/*")
public class HiddenHttpMethodFilter implements Filter {
public static final String METHOD_PARAM = "_method";
@Override
public void init(FilterConfig filterConfig) {
ServletContext servletContext = filterConfig.getServletContext();
if (servletContext.getAttribute("genders") == null) {
servletContext.setAttribute("genders", Gender.values());
}
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String paramValue = request.getParameter(METHOD_PARAM);
if ("POST".equals(request.getMethod()) && paramValue != null && !paramValue.isBlank()) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method);
filterChain.doFilter(wrapper, response);
} else {
filterChain.doFilter(request, response);
}
}
public static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
public final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
@Override
public String getMethod() {
return method;
}
}
}

View File

@ -1,5 +1,6 @@
package ru.charm.back.dao;
import ru.charm.back.model.Gender;
import ru.charm.back.model.Profile;
import java.util.ArrayList;
@ -12,9 +13,31 @@ public class ProfileDao {
private final ConcurrentHashMap<Long, Profile> storage;
private final AtomicLong idStorage;
public ProfileDao() {
private static final ProfileDao INSTANCE = new ProfileDao();
private ProfileDao() {
this.storage = new ConcurrentHashMap<>();
this.idStorage = new AtomicLong();
Profile profile = new Profile();
profile.setId(1L);
profile.setEmail("ivanov@mail.ru");
profile.setName("Ivan");
profile.setSurname("Ivanov");
profile.setAbout("I am QA");
profile.setGender(Gender.MALE);
this.storage.put(1L, profile);
Profile profile1 = new Profile();
profile1.setId(2L);
profile1.setEmail("sidorova@mail.ru");
profile1.setName("Elena");
profile1.setSurname("Sidorova");
profile1.setAbout("I am Java Dev");
profile1.setGender(Gender.FEMALE);
this.storage.put(2L, profile1);
this.idStorage = new AtomicLong(2L);
}
public static ProfileDao getInstance() {
return INSTANCE;
}
public Profile save(Profile profile) {

View File

@ -0,0 +1,6 @@
package ru.charm.back.model;
public enum Gender {
MALE,
FEMALE
}

View File

@ -6,6 +6,7 @@ public class Profile {
private String name;
private String surname;
private String about;
private Gender gender;
public Long getId() {
return id;
@ -47,6 +48,14 @@ public class Profile {
this.about = about;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Profile{" +

View File

@ -7,10 +7,12 @@ import java.util.List;
import java.util.Optional;
public class ProfileService {
private ProfileDao dao;
private static final ProfileService INSTANCE = new ProfileService();
private final ProfileDao dao = ProfileDao.getInstance();
public ProfileService(ProfileDao dao) {
this.dao = dao;
public static ProfileService getInstance() {
return INSTANCE;
}
public Profile save(Profile profile) {